From 95566d04f81aaf0246d8d2f42e5c550a1c7f7904 Mon Sep 17 00:00:00 2001 From: Matthew Heon Date: Mon, 7 Dec 2020 11:50:47 -0500 Subject: [PATCH 01/24] Change name of imageVolumes in container config JSON Podman pre-1.8 also included a field with this name, which was a String. Podman 2.2.0 added a new field reusing the name but as a Struct. This completely broke JSON decode for pre-1.8 containers in Podman 2.2, resulting in completely broken behavior. Re-name the JSON field and add a note that the old name should not be re-used to prevent this problem from re-occurring. This will still result in containers from 2.2.0 being broken (specifically, containers with image volumes will have them disappear) but this is the lesser of two evils. Fixes #8613 Signed-off-by: Matthew Heon --- libpod/container_config.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/libpod/container_config.go b/libpod/container_config.go index cc3ad25ea9..c95be9b55d 100644 --- a/libpod/container_config.go +++ b/libpod/container_config.go @@ -135,7 +135,13 @@ type ContainerRootFSConfig struct { // OverlayVolumes lists the overlay volumes to mount into the container. OverlayVolumes []*ContainerOverlayVolume `json:"overlayVolumes,omitempty"` // ImageVolumes lists the image volumes to mount into the container. - ImageVolumes []*ContainerImageVolume `json:"imageVolumes,omitempty"` + // Please note that this is named ctrImageVolumes in JSON to + // distinguish between these and the old `imageVolumes` field in Podman + // pre-1.8, which was used in very old Podman versions to determine how + // image volumes were handled in Libpod (support for these eventually + // moved out of Libpod into pkg/specgen). + // Please DO NOT re-use the `imageVolumes` name in container JSON again. + ImageVolumes []*ContainerImageVolume `json:"ctrImageVolumes,omitempty"` // CreateWorkingDir indicates that Libpod should create the container's // working directory if it does not exist. Some OCI runtimes do this by // default, but others do not. From 4ceac05eacc37ee4cb0af03290c1c8a0f491e302 Mon Sep 17 00:00:00 2001 From: Valentin Rothberg Date: Tue, 17 Nov 2020 16:46:11 +0100 Subject: [PATCH 02/24] container cgroup path Before querying for a container's cgroup path, make sure that the container is synced. Also make sure to error out if the container isn't running. Signed-off-by: Valentin Rothberg --- libpod/container.go | 25 ++++++++++++++++++++++--- libpod/stats.go | 2 +- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/libpod/container.go b/libpod/container.go index e954d84ebd..c86625a4a9 100644 --- a/libpod/container.go +++ b/libpod/container.go @@ -916,13 +916,33 @@ func (c *Container) CgroupManager() string { return cgroupManager } -// CGroupPath returns a cgroups "path" for a given container. +// CGroupPath returns a cgroups "path" for the given container. +// Note that the container must be running. Otherwise, an error +// is returned. func (c *Container) CGroupPath() (string, error) { + if !c.batched { + c.lock.Lock() + defer c.lock.Unlock() + if err := c.syncContainer(); err != nil { + return "", errors.Wrapf(err, "error updating container %s state", c.ID()) + } + } + return c.cGroupPath() +} + +// cGroupPath returns a cgroups "path" for the given container. +// Note that the container must be running. Otherwise, an error +// is returned. +// NOTE: only call this when owning the container's lock. +func (c *Container) cGroupPath() (string, error) { if c.config.NoCgroups || c.config.CgroupsMode == "disabled" { return "", errors.Wrapf(define.ErrNoCgroups, "this container is not creating cgroups") } + if c.state.State != define.ContainerStateRunning && c.state.State != define.ContainerStatePaused { + return "", errors.Wrapf(define.ErrCtrStopped, "cannot get cgroup path unless container %s is running", c.ID()) + } - // Read /proc/[PID]/cgroup and find the *longest* cgroup entry. That's + // 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. @@ -947,7 +967,6 @@ func (c *Container) CGroupPath() (string, error) { if len(path) > len(cgroupPath) { cgroupPath = path } - } if len(cgroupPath) == 0 { diff --git a/libpod/stats.go b/libpod/stats.go index e34739626e..09d990017e 100644 --- a/libpod/stats.go +++ b/libpod/stats.go @@ -34,7 +34,7 @@ func (c *Container) GetContainerStats(previousStats *define.ContainerStats) (*de return stats, define.ErrCtrStateInvalid } - cgroupPath, err := c.CGroupPath() + cgroupPath, err := c.cGroupPath() if err != nil { return nil, err } From dca4444486a08b169862135ea5079856473f2c80 Mon Sep 17 00:00:00 2001 From: Matthew Heon Date: Mon, 7 Dec 2020 10:00:36 -0500 Subject: [PATCH 03/24] Do not error on installing duplicate shutdown handler Installing a duplicate shutdown handler fails, but if a handler with the same name is already present, we should be set to go. There's no reason to print a user-facing error about it. This comes up almost nowhere because Podman never makes more than one Libpod runtime, but there is one exception (`system reset`) and the error messages, while harmless, were making people very confused (we got several bug reports that `system reset` was nonfunctional). Signed-off-by: Matthew Heon --- libpod/runtime.go | 2 +- libpod/shutdown/handler.go | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/libpod/runtime.go b/libpod/runtime.go index df3dfae2bd..73d73fb5d5 100644 --- a/libpod/runtime.go +++ b/libpod/runtime.go @@ -190,7 +190,7 @@ func newRuntimeFromConfig(ctx context.Context, conf *config.Config, options ...R if err := shutdown.Register("libpod", func(sig os.Signal) error { os.Exit(1) return nil - }); err != nil { + }); err != nil && errors.Cause(err) != shutdown.ErrHandlerExists { logrus.Errorf("Error registering shutdown handler for libpod: %v", err) } diff --git a/libpod/shutdown/handler.go b/libpod/shutdown/handler.go index 87538dec91..f0f228b19e 100644 --- a/libpod/shutdown/handler.go +++ b/libpod/shutdown/handler.go @@ -10,6 +10,10 @@ import ( "github.com/sirupsen/logrus" ) +var ( + ErrHandlerExists error = errors.New("handler with given name already exists") +) + var ( stopped bool sigChan chan os.Signal @@ -98,7 +102,7 @@ func Register(name string, handler func(os.Signal) error) error { } if _, ok := handlers[name]; ok { - return errors.Errorf("handler with name %s already exists", name) + return ErrHandlerExists } handlers[name] = handler From 4fa94d7e14bcfb41b4a73093f0b08a51469b2c45 Mon Sep 17 00:00:00 2001 From: Matthew Heon Date: Wed, 2 Dec 2020 15:01:46 -0500 Subject: [PATCH 04/24] Do not mount sysfs as rootless in more cases We can't mount sysfs as rootless unless we manage the network namespace. Problem: slirp4netns is now creating and managing a network namespace separate from the OCI runtime, so we can't mount sysfs in many circumstances. The `crun` OCI runtime will automatically handle this by falling back to a bind mount, but `runc` will not, so we didn't notice until RHEL gating tests ran on the new branch. Signed-off-by: Matthew Heon --- pkg/specgen/generate/oci.go | 2 +- test/e2e/run_memory_test.go | 6 +++--- test/e2e/run_test.go | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pkg/specgen/generate/oci.go b/pkg/specgen/generate/oci.go index 8454458a8a..9649873fd1 100644 --- a/pkg/specgen/generate/oci.go +++ b/pkg/specgen/generate/oci.go @@ -165,7 +165,7 @@ func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runt inUserNS = true } } - if inUserNS && s.NetNS.IsHost() { + if inUserNS && s.NetNS.NSMode != specgen.NoNetwork { canMountSys = false } diff --git a/test/e2e/run_memory_test.go b/test/e2e/run_memory_test.go index b3913c1e62..ad3a2b54fd 100644 --- a/test/e2e/run_memory_test.go +++ b/test/e2e/run_memory_test.go @@ -38,7 +38,7 @@ var _ = Describe("Podman run memory", func() { var session *PodmanSessionIntegration if CGROUPSV2 { - session = podmanTest.Podman([]string{"run", "--memory=40m", ALPINE, "sh", "-c", "cat /sys/fs/cgroup/$(sed -e 's|0::||' < /proc/self/cgroup)/memory.max"}) + session = podmanTest.Podman([]string{"run", "--memory=40m", "--net=none", ALPINE, "sh", "-c", "cat /sys/fs/cgroup/$(sed -e 's|0::||' < /proc/self/cgroup)/memory.max"}) } else { session = podmanTest.Podman([]string{"run", "--memory=40m", ALPINE, "cat", "/sys/fs/cgroup/memory/memory.limit_in_bytes"}) } @@ -55,7 +55,7 @@ var _ = Describe("Podman run memory", func() { var session *PodmanSessionIntegration if CGROUPSV2 { - session = podmanTest.Podman([]string{"run", "--memory-reservation=40m", ALPINE, "sh", "-c", "cat /sys/fs/cgroup/$(sed -e 's|0::||' < /proc/self/cgroup)/memory.low"}) + session = podmanTest.Podman([]string{"run", "--memory-reservation=40m", "--net=none", ALPINE, "sh", "-c", "cat /sys/fs/cgroup/$(sed -e 's|0::||' < /proc/self/cgroup)/memory.low"}) } else { session = podmanTest.Podman([]string{"run", "--memory-reservation=40m", ALPINE, "cat", "/sys/fs/cgroup/memory/memory.soft_limit_in_bytes"}) } @@ -81,7 +81,7 @@ var _ = Describe("Podman run memory", func() { var session *PodmanSessionIntegration if CGROUPSV2 { - session = podmanTest.Podman([]string{"run", "--memory-reservation=40m", ALPINE, "sh", "-c", "cat /sys/fs/cgroup/$(sed -e 's|0::||' < /proc/self/cgroup)/memory.low"}) + session = podmanTest.Podman([]string{"run", "--net=none", "--memory-reservation=40m", ALPINE, "sh", "-c", "cat /sys/fs/cgroup/$(sed -e 's|0::||' < /proc/self/cgroup)/memory.low"}) } else { session = podmanTest.Podman([]string{"run", "--memory-reservation=40m", ALPINE, "cat", "/sys/fs/cgroup/memory/memory.soft_limit_in_bytes"}) } diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go index 0d65a3e596..5831bb2f9f 100644 --- a/test/e2e/run_test.go +++ b/test/e2e/run_test.go @@ -1267,7 +1267,7 @@ USER mail` It("podman run verify pids-limit", func() { SkipIfCgroupV1("pids-limit not supported on cgroup V1") limit := "4321" - session := podmanTest.Podman([]string{"run", "--pids-limit", limit, "--rm", ALPINE, "cat", "/sys/fs/cgroup/pids.max"}) + session := podmanTest.Podman([]string{"run", "--pids-limit", limit, "--net=none", "--rm", ALPINE, "cat", "/sys/fs/cgroup/pids.max"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) Expect(session.OutputToString()).To(ContainSubstring(limit)) From 9b4266eb942b10926ea69ba13d84d5e36a61bd3c Mon Sep 17 00:00:00 2001 From: Milivoje Legenovic Date: Mon, 7 Dec 2020 12:50:10 +0100 Subject: [PATCH 05/24] Docker compat API - containers create ignores the name /containers/create compat endpoint does not set the name correctly (#7857) Signed-off-by: Milivoje Legenovic --- pkg/api/handlers/compat/containers_create.go | 6 +++--- test/apiv2/rest_api/test_rest_v2_0_0.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/api/handlers/compat/containers_create.go b/pkg/api/handlers/compat/containers_create.go index 729639928e..409a74de22 100644 --- a/pkg/api/handlers/compat/containers_create.go +++ b/pkg/api/handlers/compat/containers_create.go @@ -37,6 +37,9 @@ func CreateContainer(w http.ResponseWriter, r *http.Request) { return } + // Override the container name in the body struct + body.Name = query.Name + if len(body.HostConfig.Links) > 0 { utils.Error(w, utils.ErrLinkNotSupport.Error(), http.StatusBadRequest, errors.Wrapf(utils.ErrLinkNotSupport, "bad parameter")) return @@ -69,9 +72,6 @@ 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/test/apiv2/rest_api/test_rest_v2_0_0.py b/test/apiv2/rest_api/test_rest_v2_0_0.py index 52348d4f46..abb8200532 100644 --- a/test/apiv2/rest_api/test_rest_v2_0_0.py +++ b/test/apiv2/rest_api/test_rest_v2_0_0.py @@ -172,7 +172,7 @@ def test_post_create_compat_connect(self): self.assertEqual(net_default.status_code, 201, net_default.text) create = requests.post( - PODMAN_URL + "/v1.40/containers/create?name=postCreate", + PODMAN_URL + "/v1.40/containers/create?name=postCreateConnect", json={ "Cmd": ["top"], "Image": "alpine:latest", From 2ddb16d4c8ace2da51f4abd94efd463c637e8808 Mon Sep 17 00:00:00 2001 From: Valentin Rothberg Date: Mon, 7 Dec 2020 11:58:10 +0100 Subject: [PATCH 06/24] container create: do not clear image name When creating a container, do not clear the input-image name before looking up image names. Also add a regression test. Fixes: #8558 Signed-off-by: Valentin Rothberg Signed-off-by: Matthew Heon --- pkg/specgen/generate/container_create.go | 1 - test/system/030-run.bats | 11 +++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/pkg/specgen/generate/container_create.go b/pkg/specgen/generate/container_create.go index 45a374216e..6a77d0807a 100644 --- a/pkg/specgen/generate/container_create.go +++ b/pkg/specgen/generate/container_create.go @@ -98,7 +98,6 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener // present. imgName := newImage.InputName if s.Image == newImage.InputName && strings.HasPrefix(newImage.ID(), s.Image) { - imgName = "" names := newImage.Names() if len(names) > 0 { imgName = names[0] diff --git a/test/system/030-run.bats b/test/system/030-run.bats index 71831da104..bf7f67f7f1 100644 --- a/test/system/030-run.bats +++ b/test/system/030-run.bats @@ -532,6 +532,17 @@ json-file | f run_podman untag $IMAGE $newtag $newtag2 } +# Regression test for issue #8558 +@test "podman run on untagged image: make sure that image metadata is set" { + run_podman inspect $IMAGE --format "{{.ID}}" + imageID="$output" + + run_podman untag $IMAGE + run_podman run --rm $imageID ls + + run_podman tag $imageID $IMAGE +} + @test "podman run with --net=host and --port prints warning" { run_podman run -d --rm -p 8080 --net=host $IMAGE ls > /dev/null is "$output" ".*Port mappings have been discarded as one of the Host, Container, Pod, and None network modes are in use" From 34eac83371210e8fd445244fcf91599580edf39e Mon Sep 17 00:00:00 2001 From: Qi Wang Date: Fri, 4 Dec 2020 11:00:02 -0500 Subject: [PATCH 07/24] Close image rawSource when each loop ends Previously close rawSouce in the middle makes future use of rawSource invalid. Move the rawSource.Close() to the end of each loop. Signed-off-by: Qi Wang --- pkg/domain/infra/abi/images.go | 147 +++++++++++++++++---------------- 1 file changed, 77 insertions(+), 70 deletions(-) diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go index ef0e152649..75ce518b6f 100644 --- a/pkg/domain/infra/abi/images.go +++ b/pkg/domain/infra/abi/images.go @@ -714,83 +714,90 @@ func (ir *ImageEngine) Sign(ctx context.Context, names []string, options entitie } for _, signimage := range names { - srcRef, err := alltransports.ParseImageName(signimage) - if err != nil { - return nil, errors.Wrapf(err, "error parsing image name") - } - rawSource, err := srcRef.NewImageSource(ctx, sc) - if err != nil { - return nil, errors.Wrapf(err, "error getting image source") - } - err = rawSource.Close() - if err != nil { - logrus.Errorf("unable to close new image source %q", err) - } - getManifest, _, err := rawSource.GetManifest(ctx, nil) - if err != nil { - return nil, errors.Wrapf(err, "error getting getManifest") - } - dockerReference := rawSource.Reference().DockerReference() - if dockerReference == nil { - return nil, errors.Errorf("cannot determine canonical Docker reference for destination %s", transports.ImageName(rawSource.Reference())) - } - var sigStoreDir string - if options.Directory != "" { - sigStoreDir = options.Directory - } - if sigStoreDir == "" { - if rootless.IsRootless() { - sigStoreDir = filepath.Join(filepath.Dir(ir.Libpod.StorageConfig().GraphRoot), "sigstore") - } else { - var sigStoreURI string - registryInfo := trust.HaveMatchRegistry(rawSource.Reference().DockerReference().String(), registryConfigs) - if registryInfo != nil { - if sigStoreURI = registryInfo.SigStoreStaging; sigStoreURI == "" { - sigStoreURI = registryInfo.SigStore - } + err = func() error { + srcRef, err := alltransports.ParseImageName(signimage) + if err != nil { + return errors.Wrapf(err, "error parsing image name") + } + rawSource, err := srcRef.NewImageSource(ctx, sc) + if err != nil { + return errors.Wrapf(err, "error getting image source") + } + defer func() { + if err = rawSource.Close(); err != nil { + logrus.Errorf("unable to close %s image source %q", srcRef.DockerReference().Name(), err) } - if sigStoreURI == "" { - return nil, errors.Errorf("no signature storage configuration found for %s", rawSource.Reference().DockerReference().String()) + }() + getManifest, _, err := rawSource.GetManifest(ctx, nil) + if err != nil { + return errors.Wrapf(err, "error getting getManifest") + } + dockerReference := rawSource.Reference().DockerReference() + if dockerReference == nil { + return errors.Errorf("cannot determine canonical Docker reference for destination %s", transports.ImageName(rawSource.Reference())) + } + var sigStoreDir string + if options.Directory != "" { + sigStoreDir = options.Directory + } + if sigStoreDir == "" { + if rootless.IsRootless() { + sigStoreDir = filepath.Join(filepath.Dir(ir.Libpod.StorageConfig().GraphRoot), "sigstore") + } else { + var sigStoreURI string + registryInfo := trust.HaveMatchRegistry(rawSource.Reference().DockerReference().String(), registryConfigs) + if registryInfo != nil { + if sigStoreURI = registryInfo.SigStoreStaging; sigStoreURI == "" { + sigStoreURI = registryInfo.SigStore + } + } + if sigStoreURI == "" { + return errors.Errorf("no signature storage configuration found for %s", rawSource.Reference().DockerReference().String()) - } - sigStoreDir, err = localPathFromURI(sigStoreURI) - if err != nil { - return nil, errors.Wrapf(err, "invalid signature storage %s", sigStoreURI) + } + sigStoreDir, err = localPathFromURI(sigStoreURI) + if err != nil { + return errors.Wrapf(err, "invalid signature storage %s", sigStoreURI) + } } } - } - manifestDigest, err := manifest.Digest(getManifest) - if err != nil { - return nil, err - } - repo := reference.Path(dockerReference) - if path.Clean(repo) != repo { // Coverage: This should not be reachable because /./ and /../ components are not valid in docker references - return nil, errors.Errorf("Unexpected path elements in Docker reference %s for signature storage", dockerReference.String()) - } + manifestDigest, err := manifest.Digest(getManifest) + if err != nil { + return err + } + repo := reference.Path(dockerReference) + if path.Clean(repo) != repo { // Coverage: This should not be reachable because /./ and /../ components are not valid in docker references + return errors.Errorf("Unexpected path elements in Docker reference %s for signature storage", dockerReference.String()) + } - // create signature - newSig, err := signature.SignDockerManifest(getManifest, dockerReference.String(), mech, options.SignBy) - if err != nil { - return nil, errors.Wrapf(err, "error creating new signature") - } - // create the signstore file - signatureDir := fmt.Sprintf("%s@%s=%s", filepath.Join(sigStoreDir, repo), manifestDigest.Algorithm(), manifestDigest.Hex()) - if err := os.MkdirAll(signatureDir, 0751); err != nil { - // The directory is allowed to exist - if !os.IsExist(err) { - logrus.Error(err) - continue + // create signature + newSig, err := signature.SignDockerManifest(getManifest, dockerReference.String(), mech, options.SignBy) + if err != nil { + return errors.Wrapf(err, "error creating new signature") } - } - sigFilename, err := getSigFilename(signatureDir) - if err != nil { - logrus.Errorf("error creating sigstore file: %v", err) - continue - } - err = ioutil.WriteFile(filepath.Join(signatureDir, sigFilename), newSig, 0644) + // create the signstore file + signatureDir := fmt.Sprintf("%s@%s=%s", filepath.Join(sigStoreDir, repo), manifestDigest.Algorithm(), manifestDigest.Hex()) + if err := os.MkdirAll(signatureDir, 0751); err != nil { + // The directory is allowed to exist + if !os.IsExist(err) { + logrus.Error(err) + return nil + } + } + sigFilename, err := getSigFilename(signatureDir) + if err != nil { + logrus.Errorf("error creating sigstore file: %v", err) + return nil + } + err = ioutil.WriteFile(filepath.Join(signatureDir, sigFilename), newSig, 0644) + if err != nil { + logrus.Errorf("error storing signature for %s", rawSource.Reference().DockerReference().String()) + return nil + } + return nil + }() if err != nil { - logrus.Errorf("error storing signature for %s", rawSource.Reference().DockerReference().String()) - continue + return nil, err } } return nil, nil From a680958de0b56ce2d681a8b1cc14692f578c74a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20F=20Bj=C3=B6rklund?= Date: Fri, 4 Dec 2020 17:08:39 +0100 Subject: [PATCH 08/24] Use PasswordCallback instead of Password for ssh MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently asking for login password, even if not supported by the ssh server. So wait with prompt until actually requested. Signed-off-by: Anders F Björklund --- pkg/bindings/connection.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/bindings/connection.go b/pkg/bindings/connection.go index 31435ae910..b0e3ceb9b0 100644 --- a/pkg/bindings/connection.go +++ b/pkg/bindings/connection.go @@ -207,11 +207,11 @@ func sshClient(_url *url.URL, secure bool, passPhrase string, identity string) ( authMethods = append(authMethods, ssh.Password(pw)) } if len(authMethods) == 0 { - pass, err := terminal.ReadPassword("Login password:") - if err != nil { - return Connection{}, err + callback := func() (string, error) { + pass, err := terminal.ReadPassword("Login password:") + return string(pass), err } - authMethods = append(authMethods, ssh.Password(string(pass))) + authMethods = append(authMethods, ssh.PasswordCallback(callback)) } port := _url.Port() From e168608460f203e5a3b9298cc1bc6824f5eb78ab Mon Sep 17 00:00:00 2001 From: Paul Holzinger Date: Thu, 3 Dec 2020 14:43:37 +0100 Subject: [PATCH 09/24] Correct which network commands can be run as rootless Signed-off-by: Paul Holzinger --- docs/source/markdown/podman-network-connect.1.md | 2 ++ docs/source/markdown/podman-network-disconnect.1.md | 2 ++ docs/source/markdown/podman-network-inspect.1.md | 2 +- docs/source/markdown/podman-network-ls.1.md | 2 +- docs/source/markdown/podman-network.1.md | 2 +- 5 files changed, 7 insertions(+), 3 deletions(-) diff --git a/docs/source/markdown/podman-network-connect.1.md b/docs/source/markdown/podman-network-connect.1.md index 58b6e5c44f..a31a415dc0 100644 --- a/docs/source/markdown/podman-network-connect.1.md +++ b/docs/source/markdown/podman-network-connect.1.md @@ -10,6 +10,8 @@ podman\-network\-connect - Connect a container to a network Connects a container to a network. A container can be connected to a network by name or by ID. Once connected, the container can communicate with other containers in the same network. +This command is not available for rootless users. + ## OPTIONS #### **--alias** Add network-scoped alias for the container. If the network is using the `dnsname` CNI plugin, these aliases diff --git a/docs/source/markdown/podman-network-disconnect.1.md b/docs/source/markdown/podman-network-disconnect.1.md index 95c7018a8b..8b7125282f 100644 --- a/docs/source/markdown/podman-network-disconnect.1.md +++ b/docs/source/markdown/podman-network-disconnect.1.md @@ -9,6 +9,8 @@ podman\-network\-disconnect - Disconnect a container from a network ## DESCRIPTION Disconnects a container from a network. +This command is not available for rootless users. + ## OPTIONS #### **--force**, **-f** diff --git a/docs/source/markdown/podman-network-inspect.1.md b/docs/source/markdown/podman-network-inspect.1.md index 47d647b3f6..56515d0c10 100644 --- a/docs/source/markdown/podman-network-inspect.1.md +++ b/docs/source/markdown/podman-network-inspect.1.md @@ -7,7 +7,7 @@ podman\-network\-inspect - Displays the raw CNI network configuration for one or **podman network inspect** [*options*] [*network* ...] ## DESCRIPTION -Display the raw (JSON format) network configuration. This command is not available for rootless users. +Display the raw (JSON format) network configuration. ## OPTIONS #### **--format**, **-f** diff --git a/docs/source/markdown/podman-network-ls.1.md b/docs/source/markdown/podman-network-ls.1.md index 34b40b3ae4..63dcace232 100644 --- a/docs/source/markdown/podman-network-ls.1.md +++ b/docs/source/markdown/podman-network-ls.1.md @@ -7,7 +7,7 @@ podman\-network\-ls - Display a summary of CNI networks **podman network ls** [*options*] ## DESCRIPTION -Displays a list of existing podman networks. This command is not available for rootless users. +Displays a list of existing podman networks. ## OPTIONS #### **--quiet**, **-q** diff --git a/docs/source/markdown/podman-network.1.md b/docs/source/markdown/podman-network.1.md index d21b200d9b..bc161659af 100644 --- a/docs/source/markdown/podman-network.1.md +++ b/docs/source/markdown/podman-network.1.md @@ -7,7 +7,7 @@ podman\-network - Manage Podman CNI networks **podman network** *subcommand* ## DESCRIPTION -The network command manages CNI networks for Podman. It is not supported for rootless users. +The network command manages CNI networks for Podman. ## COMMANDS From 0bbb997838c4828b63c7f45003866fff1fe517e7 Mon Sep 17 00:00:00 2001 From: Daniel J Walsh Date: Wed, 2 Dec 2020 08:28:30 -0500 Subject: [PATCH 10/24] Support --network=default as if it was private Docker defines an option of "default" which means to use the default network. We should support this with the same code path as --network="". This is important for compatibility with the Docker API. Fixes: https://github.com/containers/podman/issues/8544 Signed-off-by: Daniel J Walsh --- pkg/specgen/generate/namespaces.go | 2 ++ pkg/specgen/namespaces.go | 16 +++++++--------- test/e2e/run_networking_test.go | 23 +++++++++++++++++++++-- 3 files changed, 30 insertions(+), 11 deletions(-) diff --git a/pkg/specgen/generate/namespaces.go b/pkg/specgen/generate/namespaces.go index ddc73ca610..036c7b7a16 100644 --- a/pkg/specgen/generate/namespaces.go +++ b/pkg/specgen/generate/namespaces.go @@ -233,6 +233,8 @@ func namespaceOptions(ctx context.Context, s *specgen.SpecGenerator, rt *libpod. val = fmt.Sprintf("slirp4netns:%s", s.NetNS.Value) } toReturn = append(toReturn, libpod.WithNetNS(portMappings, postConfigureNetNS, val, nil)) + case specgen.Private: + fallthrough case specgen.Bridge: portMappings, err := createPortMappings(ctx, s, img) if err != nil { diff --git a/pkg/specgen/namespaces.go b/pkg/specgen/namespaces.go index d15745fa07..9d78a02108 100644 --- a/pkg/specgen/namespaces.go +++ b/pkg/specgen/namespaces.go @@ -258,24 +258,22 @@ func ParseNetworkNamespace(ns string) (Namespace, []string, error) { var cniNetworks []string // Net defaults to Slirp on rootless switch { - case ns == "slirp4netns", strings.HasPrefix(ns, "slirp4netns:"): + case ns == string(Slirp), strings.HasPrefix(ns, string(Slirp)+":"): toReturn.NSMode = Slirp - case ns == "pod": + case ns == string(FromPod): toReturn.NSMode = FromPod - case ns == "": + case ns == "" || ns == string(Default) || ns == string(Private): if rootless.IsRootless() { toReturn.NSMode = Slirp } else { toReturn.NSMode = Bridge } - case ns == "bridge": + case ns == string(Bridge): toReturn.NSMode = Bridge - case ns == "none": + case ns == string(NoNetwork): toReturn.NSMode = NoNetwork - case ns == "host": + case ns == string(Host): toReturn.NSMode = Host - case ns == "private": - toReturn.NSMode = Private case strings.HasPrefix(ns, "ns:"): split := strings.SplitN(ns, ":", 2) if len(split) != 2 { @@ -283,7 +281,7 @@ func ParseNetworkNamespace(ns string) (Namespace, []string, error) { } toReturn.NSMode = Path toReturn.Value = split[1] - case strings.HasPrefix(ns, "container:"): + case strings.HasPrefix(ns, string(FromContainer)+":"): 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:") diff --git a/test/e2e/run_networking_test.go b/test/e2e/run_networking_test.go index 3e80e953ee..3fb00a28be 100644 --- a/test/e2e/run_networking_test.go +++ b/test/e2e/run_networking_test.go @@ -49,9 +49,28 @@ var _ = Describe("Podman run networking", func() { Expect(session.ExitCode()).To(Equal(0)) }) + It("podman run network connection with default", func() { + session := podmanTest.Podman([]string{"run", "--network", "default", ALPINE, "wget", "www.podman.io"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + }) + + It("podman run network connection with none", func() { + session := podmanTest.Podman([]string{"run", "--network", "none", ALPINE, "wget", "www.podman.io"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(1)) + Expect(session.ErrorToString()).To(ContainSubstring("wget: bad address 'www.podman.io'")) + }) + + It("podman run network connection with private", func() { + session := podmanTest.Podman([]string{"run", "--network", "private", ALPINE, "wget", "www.podman.io"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + }) + It("podman run network connection with loopback", func() { - session := podmanTest.Podman([]string{"run", "-dt", "--network", "host", ALPINE, "wget", "www.podman.io"}) - session.Wait(90) + session := podmanTest.Podman([]string{"run", "--network", "host", ALPINE, "wget", "www.podman.io"}) + session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) }) From 4052afa5dffa2eddfc522a85bdeaab670b1231ac Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Wed, 2 Dec 2020 22:07:55 +0100 Subject: [PATCH 11/24] podman, exec: move conmon to the correct cgroup move the conmon process to the conmon cgroup also on exec. The previous implementation would fail to move the conmon process as the systemd unit already exists so its creation would fail. When the unit cannot be created, attempt to directly join the cgroup instead. Signed-off-by: Giuseppe Scrivano --- utils/utils_supported.go | 45 ++++++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/utils/utils_supported.go b/utils/utils_supported.go index bcaa2c61ac..e6978ca6f2 100644 --- a/utils/utils_supported.go +++ b/utils/utils_supported.go @@ -43,6 +43,15 @@ func RunUnderSystemdScope(pid int, slice string, unitName string) error { ch := make(chan string) _, err = conn.StartTransientUnit(unitName, "replace", properties, ch) if err != nil { + // On errors check if the cgroup already exists, if it does move the process there + if props, err := conn.GetUnitTypeProperties(unitName, "Scope"); err == nil { + if cgroup, ok := props["ControlGroup"].(string); ok && cgroup != "" { + if err := moveUnderCgroup(cgroup, "", []uint32{uint32(pid)}); err != nil { + return err + } + return nil + } + } return err } defer conn.Close() @@ -101,6 +110,13 @@ func GetCgroupProcess(pid int) (string, error) { // MoveUnderCgroupSubtree moves the PID under a cgroup subtree. func MoveUnderCgroupSubtree(subtree string) error { + return moveUnderCgroup("", subtree, nil) +} + +// moveUnderCgroup moves a group of processes to a new cgroup. +// If cgroup is the empty string, then the current calling process cgroup is used. +// If processes is empty, then the processes from the current cgroup are moved. +func moveUnderCgroup(cgroup, subtree string, processes []uint32) error { procFile := "/proc/self/cgroup" f, err := os.Open(procFile) if err != nil { @@ -140,13 +156,12 @@ func MoveUnderCgroupSubtree(subtree string) error { cgroupRoot = filepath.Join(cgroupRoot, controller) } - processes, err := ioutil.ReadFile(filepath.Join(cgroupRoot, parts[2], "cgroup.procs")) - if err != nil { - return err + parentCgroup := cgroup + if parentCgroup == "" { + parentCgroup = parts[2] } - - newCgroup := filepath.Join(cgroupRoot, parts[2], subtree) - if err := os.Mkdir(newCgroup, 0755); err != nil { + newCgroup := filepath.Join(cgroupRoot, parentCgroup, subtree) + if err := os.Mkdir(newCgroup, 0755); err != nil && !os.IsExist(err) { return err } @@ -156,9 +171,21 @@ func MoveUnderCgroupSubtree(subtree string) error { } defer f.Close() - for _, pid := range bytes.Split(processes, []byte("\n")) { - if _, err := f.Write(pid); err != nil { - logrus.Warnf("Cannot move process %s to cgroup %q", pid, newCgroup) + if len(processes) > 0 { + for _, pid := range processes { + if _, err := f.Write([]byte(fmt.Sprintf("%d\n", pid))); err != nil { + logrus.Warnf("Cannot move process %d to cgroup %q", pid, newCgroup) + } + } + } else { + processesData, err := ioutil.ReadFile(filepath.Join(cgroupRoot, parts[2], "cgroup.procs")) + if err != nil { + return err + } + for _, pid := range bytes.Split(processesData, []byte("\n")) { + if _, err := f.Write(pid); err != nil { + logrus.Warnf("Cannot move process %d to cgroup %q", pid, newCgroup) + } } } } From 704dff44c60b4bcb1327c7ef99289cda27baaae0 Mon Sep 17 00:00:00 2001 From: Jhon Honce Date: Wed, 2 Dec 2020 16:10:18 -0700 Subject: [PATCH 12/24] Change bindings to stop two API calls for ping * existing code caused an unnecessary 301 redirect Signed-off-by: Jhon Honce --- pkg/bindings/connection.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/bindings/connection.go b/pkg/bindings/connection.go index b0e3ceb9b0..f2cb3147cc 100644 --- a/pkg/bindings/connection.go +++ b/pkg/bindings/connection.go @@ -152,7 +152,7 @@ func pingNewConnection(ctx context.Context) error { return err } // the ping endpoint sits at / in this case - response, err := client.DoRequest(nil, http.MethodGet, "../../../_ping", nil, nil) + response, err := client.DoRequest(nil, http.MethodGet, "/_ping", nil, nil) if err != nil { return err } From fa9ad1671e9b3433c6b5fb0c393cebca70a0811b Mon Sep 17 00:00:00 2001 From: Matthew Heon Date: Wed, 2 Dec 2020 10:05:34 -0500 Subject: [PATCH 13/24] Use Libpod tmpdir for pause path Previously, we always computed pause path from the Rootless runtime directory. Problem: this does not match the behavior of Libpod when the directory changes. Libpod will continue to use the previous directory, cached in the database; Pause pidfiles will swap to the new path. This is problematic when the directory needs to exist to write the pidfile, and Libpod is what creates the directory. There are two potential solutions - allow the pause pidfile to move and just make the directory when we want to write it, or use the cached Libpod paths for a guaranteed location. This patch does the second, because it seems safer - we will never miss a previously-existing pidfile because the location is now consistent. Fixes #8539 Signed-off-by: Matthew Heon --- libpod/reset.go | 2 +- libpod/runtime.go | 11 ++++++++++- libpod/runtime_migrate.go | 6 +++--- libpod/runtime_migrate_unsupported.go | 2 +- pkg/domain/infra/abi/system.go | 17 +++++++++++++---- pkg/util/utils_supported.go | 13 ++++++++++++- pkg/util/utils_windows.go | 6 ++++++ 7 files changed, 46 insertions(+), 11 deletions(-) diff --git a/libpod/reset.go b/libpod/reset.go index f8828fed4a..6d28427232 100644 --- a/libpod/reset.go +++ b/libpod/reset.go @@ -46,7 +46,7 @@ func (r *Runtime) Reset(ctx context.Context) error { } } - if err := stopPauseProcess(); err != nil { + if err := r.stopPauseProcess(); err != nil { logrus.Errorf("Error stopping pause process: %v", err) } diff --git a/libpod/runtime.go b/libpod/runtime.go index 73d73fb5d5..1f50f34075 100644 --- a/libpod/runtime.go +++ b/libpod/runtime.go @@ -472,7 +472,7 @@ func makeRuntime(ctx context.Context, runtime *Runtime) (retErr error) { // we will need to access the storage. if os.Geteuid() != 0 { aliveLock.Unlock() // Unlock to avoid deadlock as BecomeRootInUserNS will reexec. - pausePid, err := util.GetRootlessPauseProcessPidPath() + pausePid, err := util.GetRootlessPauseProcessPidPathGivenDir(runtime.config.Engine.TmpDir) if err != nil { return errors.Wrapf(err, "could not get pause process pid file path") } @@ -538,6 +538,15 @@ func makeRuntime(ctx context.Context, runtime *Runtime) (retErr error) { return nil } +// TmpDir gets the current Libpod temporary files directory. +func (r *Runtime) TmpDir() (string, error) { + if !r.valid { + return "", define.ErrRuntimeStopped + } + + return r.config.Engine.TmpDir, nil +} + // GetConfig returns a copy of the configuration used by the runtime func (r *Runtime) GetConfig() (*config.Config, error) { r.lock.RLock() diff --git a/libpod/runtime_migrate.go b/libpod/runtime_migrate.go index 1ad32fe9cf..f0f800ef0c 100644 --- a/libpod/runtime_migrate.go +++ b/libpod/runtime_migrate.go @@ -18,9 +18,9 @@ import ( "github.com/sirupsen/logrus" ) -func stopPauseProcess() error { +func (r *Runtime) stopPauseProcess() error { if rootless.IsRootless() { - pausePidPath, err := util.GetRootlessPauseProcessPidPath() + pausePidPath, err := util.GetRootlessPauseProcessPidPathGivenDir(r.config.Engine.TmpDir) if err != nil { return errors.Wrapf(err, "could not get pause process pid file path") } @@ -98,5 +98,5 @@ func (r *Runtime) migrate(ctx context.Context) error { } } - return stopPauseProcess() + return r.stopPauseProcess() } diff --git a/libpod/runtime_migrate_unsupported.go b/libpod/runtime_migrate_unsupported.go index e362cca637..a9d3513186 100644 --- a/libpod/runtime_migrate_unsupported.go +++ b/libpod/runtime_migrate_unsupported.go @@ -10,6 +10,6 @@ func (r *Runtime) migrate(ctx context.Context) error { return nil } -func stopPauseProcess() error { +func (r *Runtime) stopPauseProcess() error { return nil } diff --git a/pkg/domain/infra/abi/system.go b/pkg/domain/infra/abi/system.go index 72fd98ac10..ec2532bea9 100644 --- a/pkg/domain/infra/abi/system.go +++ b/pkg/domain/infra/abi/system.go @@ -11,6 +11,7 @@ import ( "strings" "github.com/containers/common/pkg/config" + "github.com/containers/podman/v2/libpod" "github.com/containers/podman/v2/libpod/define" "github.com/containers/podman/v2/pkg/cgroups" "github.com/containers/podman/v2/pkg/domain/entities" @@ -86,7 +87,11 @@ func (ic *ContainerEngine) SetupRootless(_ context.Context, cmd *cobra.Command) return nil } - pausePidPath, err := util.GetRootlessPauseProcessPidPath() + tmpDir, err := ic.Libpod.TmpDir() + if err != nil { + return err + } + pausePidPath, err := util.GetRootlessPauseProcessPidPathGivenDir(tmpDir) if err != nil { return errors.Wrapf(err, "could not get pause process pid file path") } @@ -112,7 +117,7 @@ func (ic *ContainerEngine) SetupRootless(_ context.Context, cmd *cobra.Command) } became, ret, err = rootless.TryJoinFromFilePaths(pausePidPath, true, paths) - if err := movePauseProcessToScope(); err != nil { + if err := movePauseProcessToScope(ic.Libpod); err != nil { conf, err := ic.Config(context.Background()) if err != nil { return err @@ -133,8 +138,12 @@ func (ic *ContainerEngine) SetupRootless(_ context.Context, cmd *cobra.Command) return nil } -func movePauseProcessToScope() error { - pausePidPath, err := util.GetRootlessPauseProcessPidPath() +func movePauseProcessToScope(r *libpod.Runtime) error { + tmpDir, err := r.TmpDir() + if err != nil { + return err + } + pausePidPath, err := util.GetRootlessPauseProcessPidPathGivenDir(tmpDir) if err != nil { return errors.Wrapf(err, "could not get pause process pid file path") } diff --git a/pkg/util/utils_supported.go b/pkg/util/utils_supported.go index 2d636a7cb0..a63c764152 100644 --- a/pkg/util/utils_supported.go +++ b/pkg/util/utils_supported.go @@ -99,7 +99,8 @@ func GetRootlessConfigHomeDir() (string, error) { } // GetRootlessPauseProcessPidPath returns the path to the file that holds the pid for -// the pause process +// the pause process. +// DEPRECATED - switch to GetRootlessPauseProcessPidPathGivenDir func GetRootlessPauseProcessPidPath() (string, error) { runtimeDir, err := GetRuntimeDir() if err != nil { @@ -107,3 +108,13 @@ func GetRootlessPauseProcessPidPath() (string, error) { } return filepath.Join(runtimeDir, "libpod", "pause.pid"), nil } + +// GetRootlessPauseProcessPidPathGivenDir returns the path to the file that +// holds the PID of the pause process, given the location of Libpod's temporary +// files. +func GetRootlessPauseProcessPidPathGivenDir(libpodTmpDir string) (string, error) { + if libpodTmpDir == "" { + return "", errors.Errorf("must provide non-empty tmporary directory") + } + return filepath.Join(libpodTmpDir, "pause.pid"), nil +} diff --git a/pkg/util/utils_windows.go b/pkg/util/utils_windows.go index 9bba2d1ee7..46ca5e7f18 100644 --- a/pkg/util/utils_windows.go +++ b/pkg/util/utils_windows.go @@ -25,6 +25,12 @@ func GetRootlessPauseProcessPidPath() (string, error) { return "", errors.Wrap(errNotImplemented, "GetRootlessPauseProcessPidPath") } +// GetRootlessPauseProcessPidPath returns the path to the file that holds the pid for +// the pause process +func GetRootlessPauseProcessPidPathGivenDir(unused string) (string, error) { + return "", errors.Wrap(errNotImplemented, "GetRootlessPauseProcessPidPath") +} + // GetRuntimeDir returns the runtime directory func GetRuntimeDir() (string, error) { return "", errors.New("this function is not implemented for windows") From 3f6197e3d2a55ab843dee7be922706a079d875a4 Mon Sep 17 00:00:00 2001 From: Jhon Honce Date: Wed, 18 Nov 2020 09:09:53 -0700 Subject: [PATCH 14/24] Fix `podman images...` missing headers in table templates Signed-off-by: Jhon Honce --- cmd/podman/images/list.go | 4 ++-- test/e2e/images_test.go | 2 +- test/system/010-images.bats | 13 ++++++++++--- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/cmd/podman/images/list.go b/cmd/podman/images/list.go index bcb31e6ee1..8a79519236 100644 --- a/cmd/podman/images/list.go +++ b/cmd/podman/images/list.go @@ -126,8 +126,8 @@ func images(cmd *cobra.Command, args []string) error { case listFlag.quiet: return writeID(imgs) default: - if cmd.Flag("format").Changed { - listFlag.noHeading = true // V1 compatibility + if cmd.Flags().Changed("format") && !parse.HasTable(listFlag.format) { + listFlag.noHeading = true } return writeTemplate(imgs) } diff --git a/test/e2e/images_test.go b/test/e2e/images_test.go index 4c65a85d59..e045718ac1 100644 --- a/test/e2e/images_test.go +++ b/test/e2e/images_test.go @@ -278,7 +278,7 @@ WORKDIR /test It("podman images sort by values", func() { sortValueTest := func(value string, result int, format string) []string { f := fmt.Sprintf("{{.%s}}", format) - session := podmanTest.Podman([]string{"images", "--sort", value, "--format", f}) + session := podmanTest.Podman([]string{"images", "--noheading", "--sort", value, "--format", f}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(result)) diff --git a/test/system/010-images.bats b/test/system/010-images.bats index 98bb0cc576..7379e5571e 100644 --- a/test/system/010-images.bats +++ b/test/system/010-images.bats @@ -197,9 +197,16 @@ Labels.created_at | 20[0-9-]\\\+T[0-9:]\\\+Z local format=$2 run_podman images --sort repository --format "$format" - _check_line 0 ${aaa_name} ${aaa_tag} - _check_line 1 "${PODMAN_TEST_IMAGE_REGISTRY}/${PODMAN_TEST_IMAGE_USER}/${PODMAN_TEST_IMAGE_NAME}" "${PODMAN_TEST_IMAGE_TAG}" - _check_line 2 ${zzz_name} ${zzz_tag} + + line_no=0 + if [[ $format == table* ]]; then + # skip headers from table command + line_no=1 + fi + + _check_line $line_no ${aaa_name} ${aaa_tag} + _check_line $((line_no+1)) "${PODMAN_TEST_IMAGE_REGISTRY}/${PODMAN_TEST_IMAGE_USER}/${PODMAN_TEST_IMAGE_NAME}" "${PODMAN_TEST_IMAGE_TAG}" + _check_line $((line_no+2)) ${zzz_name} ${zzz_tag} } # Begin the test: tag $IMAGE with both the given names From 1c16f3a5e234e3b050eb31353c5fb10e71c02c61 Mon Sep 17 00:00:00 2001 From: baude Date: Wed, 2 Dec 2020 09:56:43 -0600 Subject: [PATCH 15/24] add commas between mount options when formatting mount options into a string for the compat container create, the options need to be comma delimited. Signed-off-by: baude --- cmd/podman/common/create_opts.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/podman/common/create_opts.go b/cmd/podman/common/create_opts.go index 6dc43dbc63..23de21676d 100644 --- a/cmd/podman/common/create_opts.go +++ b/cmd/podman/common/create_opts.go @@ -203,10 +203,10 @@ func ContainerCreateToContainerCLIOpts(cc handlers.CreateContainerConfig, cgroup for _, m := range cc.HostConfig.Mounts { mount := fmt.Sprintf("type=%s", m.Type) if len(m.Source) > 0 { - mount += fmt.Sprintf("source=%s", m.Source) + mount += fmt.Sprintf(",source=%s", m.Source) } if len(m.Target) > 0 { - mount += fmt.Sprintf("dest=%s", m.Target) + mount += fmt.Sprintf(",dst=%s", m.Target) } mounts = append(mounts, mount) } From bc602e49a2dd6141f1a3ea2a906df5386f33ac2d Mon Sep 17 00:00:00 2001 From: baude Date: Wed, 2 Dec 2020 08:19:48 -0600 Subject: [PATCH 16/24] target is not tag remove mistaken use of target being used for tag Signed-off-by: baude --- pkg/api/handlers/compat/images_build.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pkg/api/handlers/compat/images_build.go b/pkg/api/handlers/compat/images_build.go index a4bb72140a..149050209a 100644 --- a/pkg/api/handlers/compat/images_build.go +++ b/pkg/api/handlers/compat/images_build.go @@ -104,9 +104,6 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { if len(query.Tag) > 0 { output = query.Tag[0] } - if _, found := r.URL.Query()["target"]; found { - output = query.Target - } var additionalNames []string if len(query.Tag) > 1 { @@ -162,7 +159,6 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { reporter := channel.NewWriter(make(chan []byte, 1)) defer reporter.Close() - buildOptions := imagebuildah.BuildOptions{ ContextDirectory: contextDirectory, PullPolicy: pullPolicy, From e5511addcb4565333fd23433aac521b6af9d4e9a Mon Sep 17 00:00:00 2001 From: Paul Holzinger Date: Wed, 2 Dec 2020 10:44:17 +0100 Subject: [PATCH 17/24] Fix shell completion for ps --filter ancestor The `ancestor` option was missing an equal sign. Therefore the completion did not work as expected. Signed-off-by: Paul Holzinger --- cmd/podman/common/completion.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cmd/podman/common/completion.go b/cmd/podman/common/completion.go index 9856e46efa..7fed15e5e3 100644 --- a/cmd/podman/common/completion.go +++ b/cmd/podman/common/completion.go @@ -861,10 +861,10 @@ func AutocompletePsFilters(cmd *cobra.Command, args []string, toComplete string) "status=": func(_ string) ([]string, cobra.ShellCompDirective) { return containerStatuses, cobra.ShellCompDirectiveNoFileComp }, - "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) }, + "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 From 688a6f1dd57e847c4d4ad8dc900b7e0adfa70c04 Mon Sep 17 00:00:00 2001 From: Matthew Heon Date: Tue, 1 Dec 2020 14:51:11 -0500 Subject: [PATCH 18/24] Do not use "true" after "syslog" in exit commands Instead of being interpreted as an argument to the boolean flag, the 'true' is being intepreted as the Podman command to be run - so we're trying to run `podman true`, which does not exist. This causes the cleanup command to fail when `--log-level=debug` is set, so containers are not cleaned up or removed. This problem is easily reproduced with any command combining the `--rm`, `-d`, and `--log-level=debug` flags - the command will execute and exit, but the container will not be removed. Separate, but worth looking into later: the errors we get on trying `podman true` with any flags are terrible - if you just type `podman true` you get a quite sane "Unrecognized command" error, but if you try `podman true --rm` you get an "unknown flag --rm" error - which makes very little sense given the command itself doesn't exist. Signed-off-by: Matthew Heon --- pkg/specgen/generate/container_create.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/specgen/generate/container_create.go b/pkg/specgen/generate/container_create.go index 6a77d0807a..4f36744cab 100644 --- a/pkg/specgen/generate/container_create.go +++ b/pkg/specgen/generate/container_create.go @@ -387,7 +387,7 @@ func CreateExitCommandArgs(storageConfig storage.StoreOptions, config *config.Co } if syslog { - command = append(command, "--syslog", "true") + command = append(command, "--syslog") } command = append(command, []string{"container", "cleanup"}...) From 1ad0554d99c45a7322160968fd4c8fa143fa7377 Mon Sep 17 00:00:00 2001 From: Daniel J Walsh Date: Tue, 1 Dec 2020 14:20:15 -0500 Subject: [PATCH 19/24] Fix typo in tests Signed-off-by: Daniel J Walsh --- test/e2e/containers_conf_test.go | 12 ++++++------ test/e2e/images_test.go | 12 ++++++------ test/e2e/import_test.go | 2 +- test/e2e/port_test.go | 12 ++++++------ test/e2e/pull_test.go | 2 +- test/e2e/run_dns_test.go | 12 ++++++------ test/e2e/run_entrypoint_test.go | 6 +++--- test/utils/podmansession_test.go | 4 ++-- test/utils/utils.go | 2 +- 9 files changed, 32 insertions(+), 32 deletions(-) diff --git a/test/e2e/containers_conf_test.go b/test/e2e/containers_conf_test.go index 906153c0fa..866162f7f3 100644 --- a/test/e2e/containers_conf_test.go +++ b/test/e2e/containers_conf_test.go @@ -203,35 +203,35 @@ var _ = Describe("Podman run", func() { session := podmanTest.Podman([]string{"run", ALPINE, "cat", "/etc/resolv.conf"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - session.LineInOuputStartsWith("search foobar.com") + session.LineInOutputStartsWith("search foobar.com") }) It("podman run add dns server", func() { session := podmanTest.Podman([]string{"run", ALPINE, "cat", "/etc/resolv.conf"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - session.LineInOuputStartsWith("server 1.2.3.4") + session.LineInOutputStartsWith("server 1.2.3.4") }) It("podman run add dns option", func() { session := podmanTest.Podman([]string{"run", ALPINE, "cat", "/etc/resolv.conf"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - session.LineInOuputStartsWith("options debug") + session.LineInOutputStartsWith("options debug") }) It("podman run containers.conf remove all search domain", func() { session := podmanTest.Podman([]string{"run", "--dns-search=.", ALPINE, "cat", "/etc/resolv.conf"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - Expect(session.LineInOuputStartsWith("search")).To(BeFalse()) + Expect(session.LineInOutputStartsWith("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.LineInOutputStartsWith("search")).To(BeTrue()) Expect(session.OutputToString()).To(ContainSubstring("foobar.com")) Expect(session.OutputToString()).To(ContainSubstring("1.2.3.4")) @@ -275,7 +275,7 @@ var _ = Describe("Podman run", 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.LineInOutputStartsWith("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")) diff --git a/test/e2e/images_test.go b/test/e2e/images_test.go index e045718ac1..281b2c313c 100644 --- a/test/e2e/images_test.go +++ b/test/e2e/images_test.go @@ -41,8 +41,8 @@ var _ = Describe("Podman images", func() { session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(BeNumerically(">", 2)) - Expect(session.LineInOuputStartsWith("quay.io/libpod/alpine")).To(BeTrue()) - Expect(session.LineInOuputStartsWith("quay.io/libpod/busybox")).To(BeTrue()) + Expect(session.LineInOutputStartsWith("quay.io/libpod/alpine")).To(BeTrue()) + Expect(session.LineInOutputStartsWith("quay.io/libpod/busybox")).To(BeTrue()) }) It("podman image List", func() { @@ -50,8 +50,8 @@ var _ = Describe("Podman images", func() { session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(BeNumerically(">", 2)) - Expect(session.LineInOuputStartsWith("quay.io/libpod/alpine")).To(BeTrue()) - Expect(session.LineInOuputStartsWith("quay.io/libpod/busybox")).To(BeTrue()) + Expect(session.LineInOutputStartsWith("quay.io/libpod/alpine")).To(BeTrue()) + Expect(session.LineInOutputStartsWith("quay.io/libpod/busybox")).To(BeTrue()) }) It("podman images with multiple tags", func() { @@ -86,8 +86,8 @@ var _ = Describe("Podman images", func() { session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(BeNumerically(">", 2)) - Expect(session.LineInOuputStartsWith("quay.io/libpod/alpine")).To(BeTrue()) - Expect(session.LineInOuputStartsWith("quay.io/libpod/busybox")).To(BeTrue()) + Expect(session.LineInOutputStartsWith("quay.io/libpod/alpine")).To(BeTrue()) + Expect(session.LineInOutputStartsWith("quay.io/libpod/busybox")).To(BeTrue()) }) It("podman empty images list in JSON format", func() { diff --git a/test/e2e/import_test.go b/test/e2e/import_test.go index 1be4ef9208..bd465bf388 100644 --- a/test/e2e/import_test.go +++ b/test/e2e/import_test.go @@ -87,7 +87,7 @@ var _ = Describe("Podman import", func() { results := podmanTest.Podman([]string{"history", "imported-image", "--format", "{{.Comment}}"}) results.WaitWithDefaultTimeout() Expect(results.ExitCode()).To(Equal(0)) - Expect(results.LineInOuputStartsWith("importing container test message")).To(BeTrue()) + Expect(results.LineInOutputStartsWith("importing container test message")).To(BeTrue()) }) It("podman import with change flag CMD=", func() { diff --git a/test/e2e/port_test.go b/test/e2e/port_test.go index a3ce8bd694..4aaf2cbc17 100644 --- a/test/e2e/port_test.go +++ b/test/e2e/port_test.go @@ -61,7 +61,7 @@ var _ = Describe("Podman port", func() { result.WaitWithDefaultTimeout() Expect(result.ExitCode()).To(Equal(0)) port := strings.Split(result.OutputToStringArray()[0], ":")[1] - Expect(result.LineInOuputStartsWith(fmt.Sprintf("80/tcp -> 0.0.0.0:%s", port))).To(BeTrue()) + Expect(result.LineInOutputStartsWith(fmt.Sprintf("80/tcp -> 0.0.0.0:%s", port))).To(BeTrue()) }) It("podman container port -l nginx", func() { @@ -79,7 +79,7 @@ var _ = Describe("Podman port", func() { result.WaitWithDefaultTimeout() Expect(result.ExitCode()).To(Equal(0)) port := strings.Split(result.OutputToStringArray()[0], ":")[1] - Expect(result.LineInOuputStartsWith(fmt.Sprintf("80/tcp -> 0.0.0.0:%s", port))).To(BeTrue()) + Expect(result.LineInOutputStartsWith(fmt.Sprintf("80/tcp -> 0.0.0.0:%s", port))).To(BeTrue()) }) It("podman port -l port nginx", func() { @@ -97,7 +97,7 @@ var _ = Describe("Podman port", func() { result.WaitWithDefaultTimeout() Expect(result.ExitCode()).To(Equal(0)) port := strings.Split(result.OutputToStringArray()[0], ":")[1] - Expect(result.LineInOuputStartsWith(fmt.Sprintf("0.0.0.0:%s", port))).To(BeTrue()) + Expect(result.LineInOutputStartsWith(fmt.Sprintf("0.0.0.0:%s", port))).To(BeTrue()) }) It("podman port -a nginx", func() { @@ -124,7 +124,7 @@ var _ = Describe("Podman port", func() { result := podmanTest.Podman([]string{"port", "portcheck"}) result.WaitWithDefaultTimeout() Expect(result.ExitCode()).To(Equal(0)) - result.LineInOuputStartsWith("80/tcp -> 0.0.0.0:") + result.LineInOutputStartsWith("80/tcp -> 0.0.0.0:") }) It("podman port multiple ports", func() { @@ -142,12 +142,12 @@ var _ = Describe("Podman port", func() { result1 := podmanTest.Podman([]string{"port", "test", "5000"}) result1.WaitWithDefaultTimeout() Expect(result1.ExitCode()).To(BeZero()) - Expect(result1.LineInOuputStartsWith("0.0.0.0:5000")).To(BeTrue()) + Expect(result1.LineInOutputStartsWith("0.0.0.0:5000")).To(BeTrue()) // Check that the second port was honored result2 := podmanTest.Podman([]string{"port", "test", "5001"}) result2.WaitWithDefaultTimeout() Expect(result2.ExitCode()).To(BeZero()) - Expect(result2.LineInOuputStartsWith("0.0.0.0:5001")).To(BeTrue()) + Expect(result2.LineInOutputStartsWith("0.0.0.0:5001")).To(BeTrue()) }) }) diff --git a/test/e2e/pull_test.go b/test/e2e/pull_test.go index f1b055d6d1..1f13190ba1 100644 --- a/test/e2e/pull_test.go +++ b/test/e2e/pull_test.go @@ -384,7 +384,7 @@ var _ = Describe("Podman pull", func() { session := podmanTest.Podman([]string{"pull", "--all-tags", "k8s.gcr.io/pause"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - Expect(session.LineInOuputStartsWith("Pulled Images:")).To(BeTrue()) + Expect(session.LineInOutputStartsWith("Pulled Images:")).To(BeTrue()) session = podmanTest.Podman([]string{"images"}) session.WaitWithDefaultTimeout() diff --git a/test/e2e/run_dns_test.go b/test/e2e/run_dns_test.go index ff018f5d8f..c8996c5e83 100644 --- a/test/e2e/run_dns_test.go +++ b/test/e2e/run_dns_test.go @@ -36,14 +36,14 @@ var _ = Describe("Podman run dns", func() { session := podmanTest.Podman([]string{"run", "--dns-search=foobar.com", ALPINE, "cat", "/etc/resolv.conf"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - session.LineInOuputStartsWith("search foobar.com") + session.LineInOutputStartsWith("search foobar.com") }) It("podman run remove all search domain", func() { session := podmanTest.Podman([]string{"run", "--dns-search=.", ALPINE, "cat", "/etc/resolv.conf"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - Expect(session.LineInOuputStartsWith("search")).To(BeFalse()) + Expect(session.LineInOutputStartsWith("search")).To(BeFalse()) }) It("podman run add bad dns server", func() { @@ -56,14 +56,14 @@ var _ = Describe("Podman run dns", func() { session := podmanTest.Podman([]string{"run", "--dns=1.2.3.4", ALPINE, "cat", "/etc/resolv.conf"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - session.LineInOuputStartsWith("server 1.2.3.4") + session.LineInOutputStartsWith("server 1.2.3.4") }) It("podman run add dns option", func() { session := podmanTest.Podman([]string{"run", "--dns-opt=debug", ALPINE, "cat", "/etc/resolv.conf"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - session.LineInOuputStartsWith("options debug") + session.LineInOutputStartsWith("options debug") }) It("podman run add bad host", func() { @@ -76,8 +76,8 @@ var _ = Describe("Podman run dns", func() { session := podmanTest.Podman([]string{"run", "--add-host=foobar:1.1.1.1", "--add-host=foobaz:2001:db8::68", ALPINE, "cat", "/etc/hosts"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - session.LineInOuputStartsWith("1.1.1.1 foobar") - session.LineInOuputStartsWith("2001:db8::68 foobaz") + session.LineInOutputStartsWith("1.1.1.1 foobar") + session.LineInOutputStartsWith("2001:db8::68 foobaz") }) It("podman run add hostname", func() { diff --git a/test/e2e/run_entrypoint_test.go b/test/e2e/run_entrypoint_test.go index 2185d6b13d..cac3d759d0 100644 --- a/test/e2e/run_entrypoint_test.go +++ b/test/e2e/run_entrypoint_test.go @@ -99,12 +99,12 @@ ENTRYPOINT ["grep", "Alpine", "/etc/os-release"] session := podmanTest.Podman([]string{"run", "--entrypoint=uname", "foobar.com/entrypoint:latest"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - Expect(session.LineInOuputStartsWith("Linux")).To(BeTrue()) + Expect(session.LineInOutputStartsWith("Linux")).To(BeTrue()) session = podmanTest.Podman([]string{"run", "--entrypoint", "", "foobar.com/entrypoint:latest", "uname"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - Expect(session.LineInOuputStartsWith("Linux")).To(BeTrue()) + Expect(session.LineInOutputStartsWith("Linux")).To(BeTrue()) }) It("podman run user entrypoint with command overrides image entrypoint and image cmd", func() { @@ -116,6 +116,6 @@ ENTRYPOINT ["grep", "Alpine", "/etc/os-release"] session := podmanTest.Podman([]string{"run", "--entrypoint=uname", "foobar.com/entrypoint:latest", "-r"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - Expect(session.LineInOuputStartsWith("Linux")).To(BeFalse()) + Expect(session.LineInOutputStartsWith("Linux")).To(BeFalse()) }) }) diff --git a/test/utils/podmansession_test.go b/test/utils/podmansession_test.go index d0e2f3d06f..763cb4f26a 100644 --- a/test/utils/podmansession_test.go +++ b/test/utils/podmansession_test.go @@ -53,8 +53,8 @@ var _ = Describe("PodmanSession test", func() { }) It("Test LineInOutputStartsWith", func() { - Expect(session.LineInOuputStartsWith("Podman")).To(BeTrue()) - Expect(session.LineInOuputStartsWith("Session")).To(Not(BeTrue())) + Expect(session.LineInOutputStartsWith("Podman")).To(BeTrue()) + Expect(session.LineInOutputStartsWith("Session")).To(Not(BeTrue())) }) It("Test LineInOutputContains", func() { diff --git a/test/utils/utils.go b/test/utils/utils.go index dd836f2587..d089396787 100644 --- a/test/utils/utils.go +++ b/test/utils/utils.go @@ -277,7 +277,7 @@ func (s *PodmanSession) ErrorGrepString(term string) (bool, []string) { // LineInOutputStartsWith returns true if a line in a // session output starts with the supplied string -func (s *PodmanSession) LineInOuputStartsWith(term string) bool { +func (s *PodmanSession) LineInOutputStartsWith(term string) bool { for _, i := range s.OutputToStringArray() { if strings.HasPrefix(i, term) { return true From 0a4e44fc2bd9471b6cf50a2c6c1e5d11598b9d81 Mon Sep 17 00:00:00 2001 From: Paul Holzinger Date: Tue, 1 Dec 2020 12:32:27 +0100 Subject: [PATCH 20/24] Revert the custom cobra vendor Vendor in the latest cobra release v1.1.1 This will hurt the completion experience but is required for proper packaging, see: #8528. The best solution is to keep the current scripts since they work fine with cobra v1.1.1. Signed-off-by: Paul Holzinger --- Makefile | 17 - cmd/podman/completion/completion.go | 6 +- go.mod | 2 - go.sum | 5 +- .../spf13/cobra/bash_completions.go | 752 +++++++++++++----- .../spf13/cobra/custom_completions.go | 101 +-- .../spf13/cobra/fish_completions.go | 115 ++- .../github.com/spf13/cobra/zsh_completions.go | 55 +- vendor/modules.txt | 2 +- 9 files changed, 623 insertions(+), 432 deletions(-) diff --git a/Makefile b/Makefile index 8785afc208..0316ea2845 100644 --- a/Makefile +++ b/Makefile @@ -475,15 +475,6 @@ changelog: ## Generate changelog $(shell cat $(TMPFILE) >> changelog.txt) $(shell rm $(TMPFILE)) -completions: binaries - install ${SELINUXOPT} -d -m 755 completions/{bash,zsh,fish} - ./bin/podman completion bash --no-desc -f completions/bash/podman - ./bin/podman-remote completion bash --no-desc -f completions/bash/podman-remote - ./bin/podman completion zsh -f completions/zsh/_podman - ./bin/podman-remote completion zsh -f completions/zsh/_podman-remote - ./bin/podman completion fish -f completions/fish/podman.fish - ./bin/podman-remote completion fish -f completions/fish/podman-remote.fish - .PHONY: install install: .gopathok install.bin install.remote install.man install.cni install.systemd ## Install binaries to system locations @@ -673,14 +664,6 @@ API.md: pkg/varlink/io.podman.varlink .PHONY: validate.completions validate.completions: SHELL:=/usr/bin/env bash # Set shell to bash for this target validate.completions: - # Check that nobody has manually edited the completion scripts - # If this check fails run make completions to restore the correct scripts - diff completions/bash/podman <(./bin/podman completion --no-desc bash) - diff completions/zsh/_podman <(./bin/podman completion zsh) - diff completions/fish/podman.fish <(./bin/podman completion fish) - diff completions/bash/podman-remote <(./bin/podman-remote completion --no-desc bash) - diff completions/zsh/_podman-remote <(./bin/podman-remote completion zsh) - diff completions/fish/podman-remote.fish <(./bin/podman-remote completion fish) # Check if the files can be loaded by the shell . completions/bash/podman if [ -x /bin/zsh ]; then /bin/zsh completions/zsh/_podman; fi diff --git a/cmd/podman/completion/completion.go b/cmd/podman/completion/completion.go index ead8d1f05b..84942a5086 100644 --- a/cmd/podman/completion/completion.go +++ b/cmd/podman/completion/completion.go @@ -67,11 +67,7 @@ func completion(cmd *cobra.Command, args []string) error { var err error switch args[0] { case "bash": - if noDesc { - err = cmd.Root().GenBashCompletion(w) - } else { - err = cmd.Root().GenBashCompletionWithDesc(w) - } + err = cmd.Root().GenBashCompletion(w) case "zsh": if noDesc { err = cmd.Root().GenZshCompletionNoDesc(w) diff --git a/go.mod b/go.mod index 2ed1c56d1d..8ca63323e1 100644 --- a/go.mod +++ b/go.mod @@ -75,5 +75,3 @@ require ( ) replace github.com/cri-o/ocicni => github.com/cri-o/ocicni v0.2.1-0.20201109200316-afdc16ba66df - -replace github.com/spf13/cobra => github.com/Luap99/cobra v1.0.1-0.20201110155035-83a59186c706 diff --git a/go.sum b/go.sum index 761ba04de3..453c1499fe 100644 --- a/go.sum +++ b/go.sum @@ -20,8 +20,6 @@ github.com/Azure/go-autorest v11.1.2+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSW github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/Luap99/cobra v1.0.1-0.20201110155035-83a59186c706 h1:KcMtguD/NlxB4c08lzc91o5by51Sf+Ec5+1Yv9Wqvbk= -github.com/Luap99/cobra v1.0.1-0.20201110155035-83a59186c706/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= github.com/Microsoft/go-winio v0.4.15-0.20200113171025-3fe6c5262873 h1:93nQ7k53GjoMQ07HVP8g6Zj1fQZDDj7Xy2VkNNtvX8o= @@ -510,6 +508,9 @@ github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4k github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v1.1.1 h1:KfztREH0tPxJJ+geloSLaAkaPkr4ki2Er5quFV1TDo4= +github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= diff --git a/vendor/github.com/spf13/cobra/bash_completions.go b/vendor/github.com/spf13/cobra/bash_completions.go index 2ceebc5522..846636d75b 100644 --- a/vendor/github.com/spf13/cobra/bash_completions.go +++ b/vendor/github.com/spf13/cobra/bash_completions.go @@ -5,95 +5,70 @@ import ( "fmt" "io" "os" + "sort" + "strings" + + "github.com/spf13/pflag" ) // Annotations for Bash completion. const ( - BashCompFilenameExt = "cobra_annotation_bash_completion_filename_extensions" - // BashCompCustom should be avoided as it only works for bash. - // Function RegisterFlagCompletionFunc() should be used instead. + BashCompFilenameExt = "cobra_annotation_bash_completion_filename_extensions" BashCompCustom = "cobra_annotation_bash_completion_custom" BashCompOneRequiredFlag = "cobra_annotation_bash_completion_one_required_flag" BashCompSubdirsInDir = "cobra_annotation_bash_completion_subdirs_in_dir" ) -// GenBashCompletion generates bash completion file and writes to the passed writer. -func (c *Command) GenBashCompletion(w io.Writer) error { - return c.genBashCompletion(w, false) -} - -// GenBashCompletionWithDesc generates bash completion file with descriptions and writes to the passed writer. -func (c *Command) GenBashCompletionWithDesc(w io.Writer) error { - return c.genBashCompletion(w, true) -} - -// GenBashCompletionFile generates bash completion file. -func (c *Command) GenBashCompletionFile(filename string) error { - return c.genBashCompletionFile(filename, false) -} - -// GenBashCompletionFileWithDesc generates bash completion file with descriptions. -func (c *Command) GenBashCompletionFileWithDesc(filename string) error { - return c.genBashCompletionFile(filename, true) +func writePreamble(buf *bytes.Buffer, name string) { + buf.WriteString(fmt.Sprintf("# bash completion for %-36s -*- shell-script -*-\n", name)) + buf.WriteString(fmt.Sprintf(` +__%[1]s_debug() +{ + if [[ -n ${BASH_COMP_DEBUG_FILE} ]]; then + echo "$*" >> "${BASH_COMP_DEBUG_FILE}" + fi } -func (c *Command) genBashCompletionFile(filename string, includeDesc bool) error { - outFile, err := os.Create(filename) - if err != nil { - return err - } - defer outFile.Close() - - return c.genBashCompletion(outFile, includeDesc) +# Homebrew on Macs have version 1.3 of bash-completion which doesn't include +# _init_completion. This is a very minimal version of that function. +__%[1]s_init_completion() +{ + COMPREPLY=() + _get_comp_words_by_ref "$@" cur prev words cword } -func (c *Command) genBashCompletion(w io.Writer, includeDesc bool) error { - buf := new(bytes.Buffer) - if len(c.BashCompletionFunction) > 0 { - buf.WriteString(c.BashCompletionFunction + "\n") - } - genBashComp(buf, c.Name(), includeDesc) - - _, err := buf.WriteTo(w) - return err +__%[1]s_index_of_word() +{ + local w word=$1 + shift + index=0 + for w in "$@"; do + [[ $w = "$word" ]] && return + index=$((index+1)) + done + index=-1 } -func genBashComp(buf *bytes.Buffer, name string, includeDesc bool) { - compCmd := ShellCompRequestCmd - if !includeDesc { - compCmd = ShellCompNoDescRequestCmd - } - - buf.WriteString(fmt.Sprintf(`# bash completion for %-36[1]s -*- shell-script -*- - -__%[1]s_debug() +__%[1]s_contains_word() { - if [[ -n ${BASH_COMP_DEBUG_FILE} ]]; then - echo "$*" >> "${BASH_COMP_DEBUG_FILE}" - fi + local w word=$1; shift + for w in "$@"; do + [[ $w = "$word" ]] && return + done + return 1 } -__%[1]s_perform_completion() +__%[1]s_handle_go_custom_completion() { - __%[1]s_debug - __%[1]s_debug "========= starting completion logic ==========" - __%[1]s_debug "cur is ${cur}, words[*] is ${words[*]}, #words[@] is ${#words[@]}, cword is $cword" - - # The user could have moved the cursor backwards on the command-line. - # We need to trigger completion from the $cword location, so we need - # to truncate the command-line ($words) up to the $cword location. - words=("${words[@]:0:$cword+1}") - __%[1]s_debug "Truncated words[*]: ${words[*]}," + __%[1]s_debug "${FUNCNAME[0]}: cur is ${cur}, words[*] is ${words[*]}, #words[@] is ${#words[@]}" local shellCompDirectiveError=%[3]d local shellCompDirectiveNoSpace=%[4]d local shellCompDirectiveNoFileComp=%[5]d local shellCompDirectiveFilterFileExt=%[6]d local shellCompDirectiveFilterDirs=%[7]d - local shellCompDirectiveLegacyCustomComp=%[8]d - local shellCompDirectiveLegacyCustomArgsComp=%[9]d - local out requestComp lastParam lastChar comp directive args flagPrefix + local out requestComp lastParam lastChar comp directive args # Prepare the command to request completions for the program. # Calling ${words[0]} instead of directly %[1]s allows to handle aliases @@ -102,24 +77,16 @@ __%[1]s_perform_completion() lastParam=${words[$((${#words[@]}-1))]} lastChar=${lastParam:$((${#lastParam}-1)):1} - __%[1]s_debug "lastParam ${lastParam}, lastChar ${lastChar}" + __%[1]s_debug "${FUNCNAME[0]}: lastParam ${lastParam}, lastChar ${lastChar}" if [ -z "${cur}" ] && [ "${lastChar}" != "=" ]; then # If the last parameter is complete (there is a space following it) # We add an extra empty parameter so we can indicate this to the go method. - __%[1]s_debug "Adding extra empty parameter" + __%[1]s_debug "${FUNCNAME[0]}: Adding extra empty parameter" requestComp="${requestComp} \"\"" fi - # When completing a flag with an = (e.g., %[1]s -n=) - # bash focuses on the part after the =, so we need to remove - # the flag part from $cur - if [[ "${cur}" == -*=* ]]; then - flagPrefix="${cur%%%%=*}=" - cur="${cur#*=}" - fi - - __%[1]s_debug "Calling ${requestComp}" + __%[1]s_debug "${FUNCNAME[0]}: calling ${requestComp}" # Use eval to handle any environment variables and such out=$(eval "${requestComp}" 2>/dev/null) @@ -131,23 +98,23 @@ __%[1]s_perform_completion() # There is not directive specified directive=0 fi - __%[1]s_debug "The completion directive is: ${directive}" - __%[1]s_debug "The completions are: ${out[*]}" + __%[1]s_debug "${FUNCNAME[0]}: the completion directive is: ${directive}" + __%[1]s_debug "${FUNCNAME[0]}: the completions are: ${out[*]}" if [ $((directive & shellCompDirectiveError)) -ne 0 ]; then # Error code. No completion. - __%[1]s_debug "Received error from custom completion go code" + __%[1]s_debug "${FUNCNAME[0]}: received error from custom completion go code" return else if [ $((directive & shellCompDirectiveNoSpace)) -ne 0 ]; then if [[ $(type -t compopt) = "builtin" ]]; then - __%[1]s_debug "Activating no space" + __%[1]s_debug "${FUNCNAME[0]}: activating no space" compopt -o nospace fi fi if [ $((directive & shellCompDirectiveNoFileComp)) -ne 0 ]; then if [[ $(type -t compopt) = "builtin" ]]; then - __%[1]s_debug "Activating no file completion" + __%[1]s_debug "${FUNCNAME[0]}: activating no file completion" compopt +o default fi fi @@ -156,7 +123,6 @@ __%[1]s_perform_completion() if [ $((directive & shellCompDirectiveFilterFileExt)) -ne 0 ]; then # File extension filtering local fullFilter filter filteringCmd - # Do not use quotes around the $out variable or else newline # characters will be kept. for filter in ${out[*]}; do @@ -168,173 +134,545 @@ __%[1]s_perform_completion() $filteringCmd elif [ $((directive & shellCompDirectiveFilterDirs)) -ne 0 ]; then # File completion for directories only - + local subDir # Use printf to strip any trailing newline - local subdir subdir=$(printf "%%s" "${out[0]}") if [ -n "$subdir" ]; then __%[1]s_debug "Listing directories in $subdir" - pushd "$subdir" >/dev/null 2>&1 && _filedir -d && popd >/dev/null 2>&1 || return + __%[1]s_handle_subdirs_in_dir_flag "$subdir" else __%[1]s_debug "Listing directories in ." _filedir -d fi - elif [ $((directive & shellCompDirectiveLegacyCustomComp)) -ne 0 ]; then - local cmd - __%[1]s_debug "Legacy custom completion. Directive: $directive, cmds: ${out[*]}" - - # The following variables should get their value through the commands - # we have received as completions and are parsing below. - local last_command - local nouns - - # Execute every command received - while IFS='' read -r cmd; do - __%[1]s_debug "About to execute: $cmd" - eval "$cmd" - done < <(printf "%%s\n" "${out[@]}") - - __%[1]s_debug "last_command: $last_command" - __%[1]s_debug "nouns[0]: ${nouns[0]}, nouns[1]: ${nouns[1]}" - - if [ $((directive & shellCompDirectiveLegacyCustomArgsComp)) -ne 0 ]; then - # We should call the global legacy custom completion function, if it is defined - if declare -F __%[1]s_custom_func >/dev/null; then - # Use command name qualified legacy custom func - __%[1]s_debug "About to call: __%[1]s_custom_func" - __%[1]s_custom_func - elif declare -F __custom_func >/dev/null; then - # Otherwise fall back to unqualified legacy custom func for compatibility - __%[1]s_debug "About to call: __custom_func" - __custom_func - fi - fi else - local tab - tab=$(printf '\t') - local longest=0 - # Look for the longest completion so that we can format things nicely while IFS='' read -r comp; do - comp=${comp%%%%$tab*} - if ((${#comp}>longest)); then - longest=${#comp} + COMPREPLY+=("$comp") + done < <(compgen -W "${out[*]}" -- "$cur") + fi +} + +__%[1]s_handle_reply() +{ + __%[1]s_debug "${FUNCNAME[0]}" + local comp + case $cur in + -*) + if [[ $(type -t compopt) = "builtin" ]]; then + compopt -o nospace + fi + local allflags + if [ ${#must_have_one_flag[@]} -ne 0 ]; then + allflags=("${must_have_one_flag[@]}") + else + allflags=("${flags[*]} ${two_word_flags[*]}") + fi + while IFS='' read -r comp; do + COMPREPLY+=("$comp") + done < <(compgen -W "${allflags[*]}" -- "$cur") + if [[ $(type -t compopt) = "builtin" ]]; then + [[ "${COMPREPLY[0]}" == *= ]] || compopt +o nospace fi - done < <(printf "%%s\n" "${out[@]}") - local completions=() - while IFS='' read -r comp; do - if [ -z "$comp" ]; then - continue + # complete after --flag=abc + if [[ $cur == *=* ]]; then + if [[ $(type -t compopt) = "builtin" ]]; then + compopt +o nospace + fi + + local index flag + flag="${cur%%=*}" + __%[1]s_index_of_word "${flag}" "${flags_with_completion[@]}" + COMPREPLY=() + if [[ ${index} -ge 0 ]]; then + PREFIX="" + cur="${cur#*=}" + ${flags_completion[${index}]} + if [ -n "${ZSH_VERSION}" ]; then + # zsh completion needs --flag= prefix + eval "COMPREPLY=( \"\${COMPREPLY[@]/#/${flag}=}\" )" + fi + fi fi + return 0; + ;; + esac + + # check if we are handling a flag with special work handling + local index + __%[1]s_index_of_word "${prev}" "${flags_with_completion[@]}" + if [[ ${index} -ge 0 ]]; then + ${flags_completion[${index}]} + return + fi - __%[1]s_debug "Original comp: $comp" - comp="$(__%[1]s_format_comp_descriptions "$comp" "$longest")" - __%[1]s_debug "Final comp: $comp" - completions+=("$comp") - done < <(printf "%%s\n" "${out[@]}") + # we are parsing a flag and don't have a special handler, no completion + if [[ ${cur} != "${words[cword]}" ]]; then + return + fi + local completions + completions=("${commands[@]}") + if [[ ${#must_have_one_noun[@]} -ne 0 ]]; then + completions+=("${must_have_one_noun[@]}") + elif [[ -n "${has_completion_function}" ]]; then + # if a go completion function is provided, defer to that function + __%[1]s_handle_go_custom_completion + fi + if [[ ${#must_have_one_flag[@]} -ne 0 ]]; then + completions+=("${must_have_one_flag[@]}") + fi + while IFS='' read -r comp; do + COMPREPLY+=("$comp") + done < <(compgen -W "${completions[*]}" -- "$cur") + + if [[ ${#COMPREPLY[@]} -eq 0 && ${#noun_aliases[@]} -gt 0 && ${#must_have_one_noun[@]} -ne 0 ]]; then while IFS='' read -r comp; do - # Although this script should only be used for bash - # there may be programs that still convert the bash - # script into a zsh one. To continue supporting those - # programs, we do this single adaptation for zsh - if [ -n "${ZSH_VERSION}" ]; then - # zsh completion needs --flag= prefix - COMPREPLY+=("$flagPrefix$comp") - else - COMPREPLY+=("$comp") - fi - done < <(compgen -W "${completions[*]}" -- "$cur") - - # If there is a single completion left, remove the description text - if [ ${#COMPREPLY[*]} -eq 1 ]; then - __%[1]s_debug "COMPREPLY[0]: ${COMPREPLY[0]}" - comp="${COMPREPLY[0]%%%% *}" - __%[1]s_debug "Removed description from single completion, which is now: ${comp}" - COMPREPLY=() COMPREPLY+=("$comp") + done < <(compgen -W "${noun_aliases[*]}" -- "$cur") + fi + + if [[ ${#COMPREPLY[@]} -eq 0 ]]; then + if declare -F __%[1]s_custom_func >/dev/null; then + # try command name qualified custom func + __%[1]s_custom_func + else + # otherwise fall back to unqualified for compatibility + declare -F __custom_func >/dev/null && __custom_func + fi + fi + + # available in bash-completion >= 2, not always present on macOS + if declare -F __ltrim_colon_completions >/dev/null; then + __ltrim_colon_completions "$cur" + fi + + # If there is only 1 completion and it is a flag with an = it will be completed + # but we don't want a space after the = + if [[ "${#COMPREPLY[@]}" -eq "1" ]] && [[ $(type -t compopt) = "builtin" ]] && [[ "${COMPREPLY[0]}" == --*= ]]; then + compopt -o nospace + fi +} + +# The arguments should be in the form "ext1|ext2|extn" +__%[1]s_handle_filename_extension_flag() +{ + local ext="$1" + _filedir "@(${ext})" +} + +__%[1]s_handle_subdirs_in_dir_flag() +{ + local dir="$1" + pushd "${dir}" >/dev/null 2>&1 && _filedir -d && popd >/dev/null 2>&1 || return +} + +__%[1]s_handle_flag() +{ + __%[1]s_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" + + # if a command required a flag, and we found it, unset must_have_one_flag() + local flagname=${words[c]} + local flagvalue + # if the word contained an = + if [[ ${words[c]} == *"="* ]]; then + flagvalue=${flagname#*=} # take in as flagvalue after the = + flagname=${flagname%%=*} # strip everything after the = + flagname="${flagname}=" # but put the = back + fi + __%[1]s_debug "${FUNCNAME[0]}: looking for ${flagname}" + if __%[1]s_contains_word "${flagname}" "${must_have_one_flag[@]}"; then + must_have_one_flag=() + fi + + # if you set a flag which only applies to this command, don't show subcommands + if __%[1]s_contains_word "${flagname}" "${local_nonpersistent_flags[@]}"; then + commands=() + fi + + # keep flag value with flagname as flaghash + # flaghash variable is an associative array which is only supported in bash > 3. + if [[ -z "${BASH_VERSION}" || "${BASH_VERSINFO[0]}" -gt 3 ]]; then + if [ -n "${flagvalue}" ] ; then + flaghash[${flagname}]=${flagvalue} + elif [ -n "${words[ $((c+1)) ]}" ] ; then + flaghash[${flagname}]=${words[ $((c+1)) ]} + else + flaghash[${flagname}]="true" # pad "true" for bool flag + fi + fi + + # skip the argument to a two word flag + if [[ ${words[c]} != *"="* ]] && __%[1]s_contains_word "${words[c]}" "${two_word_flags[@]}"; then + __%[1]s_debug "${FUNCNAME[0]}: found a flag ${words[c]}, skip the next argument" + c=$((c+1)) + # if we are looking for a flags value, don't show commands + if [[ $c -eq $cword ]]; then + commands=() fi fi - __%[1]s_handle_special_char "$cur" : - __%[1]s_handle_special_char "$cur" = + c=$((c+1)) + } -__%[1]s_handle_special_char() +__%[1]s_handle_noun() { - local comp="$1" - local char=$2 - if [[ "$comp" == *${char}* && "$COMP_WORDBREAKS" == *${char}* ]]; then - local word=${comp%%"${comp##*${char}}"} - local idx=${#COMPREPLY[*]} - while [[ $((--idx)) -ge 0 ]]; do - COMPREPLY[$idx]=${COMPREPLY[$idx]#"$word"} - done + __%[1]s_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" + + if __%[1]s_contains_word "${words[c]}" "${must_have_one_noun[@]}"; then + must_have_one_noun=() + elif __%[1]s_contains_word "${words[c]}" "${noun_aliases[@]}"; then + must_have_one_noun=() fi + + nouns+=("${words[c]}") + c=$((c+1)) } -__%[1]s_format_comp_descriptions() +__%[1]s_handle_command() { - local tab - tab=$(printf '\t') - local comp="$1" - local longest=$2 - - # Properly format the description string which follows a tab character if there is one - if [[ "$comp" == *$tab* ]]; then - desc=${comp#*$tab} - comp=${comp%%%%$tab*} - - # $COLUMNS stores the current shell width. - # Remove an extra 4 because we add 2 spaces and 2 parentheses. - maxdesclength=$(( COLUMNS - longest - 4 )) - - # Make sure we can fit a description of at least 8 characters - # if we are to align the descriptions. - if [[ $maxdesclength -gt 8 ]]; then - # Add the proper number of spaces to align the descriptions - for ((i = ${#comp} ; i < longest ; i++)); do - comp+=" " - done + __%[1]s_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" + + local next_command + if [[ -n ${last_command} ]]; then + next_command="_${last_command}_${words[c]//:/__}" + else + if [[ $c -eq 0 ]]; then + next_command="_%[1]s_root_command" else - # Don't pad the descriptions so we can fit more text after the completion - maxdesclength=$(( COLUMNS - ${#comp} - 4 )) + next_command="_${words[c]//:/__}" fi + fi + c=$((c+1)) + __%[1]s_debug "${FUNCNAME[0]}: looking for ${next_command}" + declare -F "$next_command" >/dev/null && $next_command +} - # If there is enough space for any description text, - # truncate the descriptions that are too long for the shell width - if [ $maxdesclength -gt 0 ]; then - if [ ${#desc} -gt $maxdesclength ]; then - desc=${desc:0:$(( maxdesclength - 1 ))} - desc+="…" - fi - comp+=" ($desc)" +__%[1]s_handle_word() +{ + if [[ $c -ge $cword ]]; then + __%[1]s_handle_reply + return + fi + __%[1]s_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" + if [[ "${words[c]}" == -* ]]; then + __%[1]s_handle_flag + elif __%[1]s_contains_word "${words[c]}" "${commands[@]}"; then + __%[1]s_handle_command + elif [[ $c -eq 0 ]]; then + __%[1]s_handle_command + elif __%[1]s_contains_word "${words[c]}" "${command_aliases[@]}"; then + # aliashash variable is an associative array which is only supported in bash > 3. + if [[ -z "${BASH_VERSION}" || "${BASH_VERSINFO[0]}" -gt 3 ]]; then + words[c]=${aliashash[${words[c]}]} + __%[1]s_handle_command + else + __%[1]s_handle_noun fi + else + __%[1]s_handle_noun fi + __%[1]s_handle_word +} - # Must use printf to escape all special characters - printf "%%q" "${comp}" +`, name, ShellCompNoDescRequestCmd, + ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp, + ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs)) } -__start_%[1]s() -{ +func writePostscript(buf *bytes.Buffer, name string) { + name = strings.Replace(name, ":", "__", -1) + buf.WriteString(fmt.Sprintf("__start_%s()\n", name)) + buf.WriteString(fmt.Sprintf(`{ local cur prev words cword + declare -A flaghash 2>/dev/null || : + declare -A aliashash 2>/dev/null || : + if declare -F _init_completion >/dev/null 2>&1; then + _init_completion -s || return + else + __%[1]s_init_completion -n "=" || return + fi - COMPREPLY=() - _get_comp_words_by_ref -n "=:" cur prev words cword - - __%[1]s_perform_completion + local c=0 + local flags=() + local two_word_flags=() + local local_nonpersistent_flags=() + local flags_with_completion=() + local flags_completion=() + local commands=("%[1]s") + local must_have_one_flag=() + local must_have_one_noun=() + local has_completion_function + local last_command + local nouns=() + + __%[1]s_handle_word } -if [[ $(type -t compopt) = "builtin" ]]; then - complete -o default -F __start_%[1]s %[1]s +`, name)) + buf.WriteString(fmt.Sprintf(`if [[ $(type -t compopt) = "builtin" ]]; then + complete -o default -F __start_%s %s else - complete -o default -o nospace -F __start_%[1]s %[1]s + complete -o default -o nospace -F __start_%s %s fi -# ex: ts=4 sw=4 et filetype=sh -`, name, compCmd, - ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp, - ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs, - shellCompDirectiveLegacyCustomComp, shellCompDirectiveLegacyCustomArgsComp)) +`, name, name, name, name)) + buf.WriteString("# ex: ts=4 sw=4 et filetype=sh\n") +} + +func writeCommands(buf *bytes.Buffer, cmd *Command) { + buf.WriteString(" commands=()\n") + for _, c := range cmd.Commands() { + if !c.IsAvailableCommand() && c != cmd.helpCommand { + continue + } + buf.WriteString(fmt.Sprintf(" commands+=(%q)\n", c.Name())) + writeCmdAliases(buf, c) + } + buf.WriteString("\n") +} + +func writeFlagHandler(buf *bytes.Buffer, name string, annotations map[string][]string, cmd *Command) { + for key, value := range annotations { + switch key { + case BashCompFilenameExt: + buf.WriteString(fmt.Sprintf(" flags_with_completion+=(%q)\n", name)) + + var ext string + if len(value) > 0 { + ext = fmt.Sprintf("__%s_handle_filename_extension_flag ", cmd.Root().Name()) + strings.Join(value, "|") + } else { + ext = "_filedir" + } + buf.WriteString(fmt.Sprintf(" flags_completion+=(%q)\n", ext)) + case BashCompCustom: + buf.WriteString(fmt.Sprintf(" flags_with_completion+=(%q)\n", name)) + if len(value) > 0 { + handlers := strings.Join(value, "; ") + buf.WriteString(fmt.Sprintf(" flags_completion+=(%q)\n", handlers)) + } else { + buf.WriteString(" flags_completion+=(:)\n") + } + case BashCompSubdirsInDir: + buf.WriteString(fmt.Sprintf(" flags_with_completion+=(%q)\n", name)) + + var ext string + if len(value) == 1 { + ext = fmt.Sprintf("__%s_handle_subdirs_in_dir_flag ", cmd.Root().Name()) + value[0] + } else { + ext = "_filedir -d" + } + buf.WriteString(fmt.Sprintf(" flags_completion+=(%q)\n", ext)) + } + } +} + +func writeShortFlag(buf *bytes.Buffer, flag *pflag.Flag, cmd *Command) { + name := flag.Shorthand + format := " " + if len(flag.NoOptDefVal) == 0 { + format += "two_word_" + } + format += "flags+=(\"-%s\")\n" + buf.WriteString(fmt.Sprintf(format, name)) + writeFlagHandler(buf, "-"+name, flag.Annotations, cmd) +} + +func writeFlag(buf *bytes.Buffer, flag *pflag.Flag, cmd *Command) { + name := flag.Name + format := " flags+=(\"--%s" + if len(flag.NoOptDefVal) == 0 { + format += "=" + } + format += "\")\n" + buf.WriteString(fmt.Sprintf(format, name)) + if len(flag.NoOptDefVal) == 0 { + format = " two_word_flags+=(\"--%s\")\n" + buf.WriteString(fmt.Sprintf(format, name)) + } + writeFlagHandler(buf, "--"+name, flag.Annotations, cmd) +} + +func writeLocalNonPersistentFlag(buf *bytes.Buffer, flag *pflag.Flag) { + name := flag.Name + format := " local_nonpersistent_flags+=(\"--%[1]s\")\n" + if len(flag.NoOptDefVal) == 0 { + format += " local_nonpersistent_flags+=(\"--%[1]s=\")\n" + } + buf.WriteString(fmt.Sprintf(format, name)) + if len(flag.Shorthand) > 0 { + buf.WriteString(fmt.Sprintf(" local_nonpersistent_flags+=(\"-%s\")\n", flag.Shorthand)) + } +} + +// Setup annotations for go completions for registered flags +func prepareCustomAnnotationsForFlags(cmd *Command) { + for flag := range flagCompletionFunctions { + // Make sure the completion script calls the __*_go_custom_completion function for + // every registered flag. We need to do this here (and not when the flag was registered + // for completion) so that we can know the root command name for the prefix + // of ___go_custom_completion + if flag.Annotations == nil { + flag.Annotations = map[string][]string{} + } + flag.Annotations[BashCompCustom] = []string{fmt.Sprintf("__%[1]s_handle_go_custom_completion", cmd.Root().Name())} + } +} + +func writeFlags(buf *bytes.Buffer, cmd *Command) { + prepareCustomAnnotationsForFlags(cmd) + buf.WriteString(` flags=() + two_word_flags=() + local_nonpersistent_flags=() + flags_with_completion=() + flags_completion=() + +`) + localNonPersistentFlags := cmd.LocalNonPersistentFlags() + cmd.NonInheritedFlags().VisitAll(func(flag *pflag.Flag) { + if nonCompletableFlag(flag) { + return + } + writeFlag(buf, flag, cmd) + if len(flag.Shorthand) > 0 { + writeShortFlag(buf, flag, cmd) + } + // localNonPersistentFlags are used to stop the completion of subcommands when one is set + // if TraverseChildren is true we should allow to complete subcommands + if localNonPersistentFlags.Lookup(flag.Name) != nil && !cmd.Root().TraverseChildren { + writeLocalNonPersistentFlag(buf, flag) + } + }) + cmd.InheritedFlags().VisitAll(func(flag *pflag.Flag) { + if nonCompletableFlag(flag) { + return + } + writeFlag(buf, flag, cmd) + if len(flag.Shorthand) > 0 { + writeShortFlag(buf, flag, cmd) + } + }) + + buf.WriteString("\n") +} + +func writeRequiredFlag(buf *bytes.Buffer, cmd *Command) { + buf.WriteString(" must_have_one_flag=()\n") + flags := cmd.NonInheritedFlags() + flags.VisitAll(func(flag *pflag.Flag) { + if nonCompletableFlag(flag) { + return + } + for key := range flag.Annotations { + switch key { + case BashCompOneRequiredFlag: + format := " must_have_one_flag+=(\"--%s" + if flag.Value.Type() != "bool" { + format += "=" + } + format += "\")\n" + buf.WriteString(fmt.Sprintf(format, flag.Name)) + + if len(flag.Shorthand) > 0 { + buf.WriteString(fmt.Sprintf(" must_have_one_flag+=(\"-%s\")\n", flag.Shorthand)) + } + } + } + }) +} + +func writeRequiredNouns(buf *bytes.Buffer, cmd *Command) { + buf.WriteString(" must_have_one_noun=()\n") + sort.Sort(sort.StringSlice(cmd.ValidArgs)) + for _, value := range cmd.ValidArgs { + // Remove any description that may be included following a tab character. + // Descriptions are not supported by bash completion. + value = strings.Split(value, "\t")[0] + buf.WriteString(fmt.Sprintf(" must_have_one_noun+=(%q)\n", value)) + } + if cmd.ValidArgsFunction != nil { + buf.WriteString(" has_completion_function=1\n") + } +} + +func writeCmdAliases(buf *bytes.Buffer, cmd *Command) { + if len(cmd.Aliases) == 0 { + return + } + + sort.Sort(sort.StringSlice(cmd.Aliases)) + + buf.WriteString(fmt.Sprint(` if [[ -z "${BASH_VERSION}" || "${BASH_VERSINFO[0]}" -gt 3 ]]; then`, "\n")) + for _, value := range cmd.Aliases { + buf.WriteString(fmt.Sprintf(" command_aliases+=(%q)\n", value)) + buf.WriteString(fmt.Sprintf(" aliashash[%q]=%q\n", value, cmd.Name())) + } + buf.WriteString(` fi`) + buf.WriteString("\n") +} +func writeArgAliases(buf *bytes.Buffer, cmd *Command) { + buf.WriteString(" noun_aliases=()\n") + sort.Sort(sort.StringSlice(cmd.ArgAliases)) + for _, value := range cmd.ArgAliases { + buf.WriteString(fmt.Sprintf(" noun_aliases+=(%q)\n", value)) + } +} + +func gen(buf *bytes.Buffer, cmd *Command) { + for _, c := range cmd.Commands() { + if !c.IsAvailableCommand() && c != cmd.helpCommand { + continue + } + gen(buf, c) + } + commandName := cmd.CommandPath() + commandName = strings.Replace(commandName, " ", "_", -1) + commandName = strings.Replace(commandName, ":", "__", -1) + + if cmd.Root() == cmd { + buf.WriteString(fmt.Sprintf("_%s_root_command()\n{\n", commandName)) + } else { + buf.WriteString(fmt.Sprintf("_%s()\n{\n", commandName)) + } + + buf.WriteString(fmt.Sprintf(" last_command=%q\n", commandName)) + buf.WriteString("\n") + buf.WriteString(" command_aliases=()\n") + buf.WriteString("\n") + + writeCommands(buf, cmd) + writeFlags(buf, cmd) + writeRequiredFlag(buf, cmd) + writeRequiredNouns(buf, cmd) + writeArgAliases(buf, cmd) + buf.WriteString("}\n\n") +} + +// GenBashCompletion generates bash completion file and writes to the passed writer. +func (c *Command) GenBashCompletion(w io.Writer) error { + buf := new(bytes.Buffer) + writePreamble(buf, c.Name()) + if len(c.BashCompletionFunction) > 0 { + buf.WriteString(c.BashCompletionFunction + "\n") + } + gen(buf, c) + writePostscript(buf, c.Name()) + + _, err := buf.WriteTo(w) + return err +} + +func nonCompletableFlag(flag *pflag.Flag) bool { + return flag.Hidden || len(flag.Deprecated) > 0 +} + +// GenBashCompletionFile generates bash completion file. +func (c *Command) GenBashCompletionFile(filename string) error { + outFile, err := os.Create(filename) + if err != nil { + return err + } + defer outFile.Close() + + return c.GenBashCompletion(outFile) } diff --git a/vendor/github.com/spf13/cobra/custom_completions.go b/vendor/github.com/spf13/cobra/custom_completions.go index e2c68ae1e7..f9e88e081f 100644 --- a/vendor/github.com/spf13/cobra/custom_completions.go +++ b/vendor/github.com/spf13/cobra/custom_completions.go @@ -51,11 +51,6 @@ const ( // obtain the same behavior but only for flags. ShellCompDirectiveFilterDirs - // For internal use only. - // Used to maintain backwards-compatibility with the legacy bash custom completions. - shellCompDirectiveLegacyCustomComp - shellCompDirectiveLegacyCustomArgsComp - // =========================================================================== // All directives using iota should be above this one. @@ -99,12 +94,6 @@ func (d ShellCompDirective) string() string { if d&ShellCompDirectiveFilterDirs != 0 { directives = append(directives, "ShellCompDirectiveFilterDirs") } - if d&shellCompDirectiveLegacyCustomComp != 0 { - directives = append(directives, "shellCompDirectiveLegacyCustomComp") - } - if d&shellCompDirectiveLegacyCustomArgsComp != 0 { - directives = append(directives, "shellCompDirectiveLegacyCustomArgsComp") - } if len(directives) == 0 { directives = append(directives, "ShellCompDirectiveDefault") } @@ -160,6 +149,10 @@ func (c *Command) initCompleteCmd(args []string) { fmt.Fprintln(finalCmd.OutOrStdout(), comp) } + if directive >= shellCompDirectiveMaxValue { + directive = ShellCompDirectiveDefault + } + // As the last printout, print the completion directive for the completion script to parse. // The directive integer must be that last character following a single colon (:). // The completion script expects : @@ -369,10 +362,6 @@ func (c *Command) getCompletions(args []string) (*Command, []string, ShellCompDi var comps []string comps, directive = completionFn(finalCmd, finalArgs, toComplete) completions = append(completions, comps...) - } else { - // If there is no Go custom completion defined, check for legacy bash - // custom completion to preserve backwards-compatibility - completions, directive = checkLegacyCustomCompletion(finalCmd, finalArgs, flag, completions, directive) } return finalCmd, completions, directive, nil @@ -453,16 +442,7 @@ func checkIfFlagCompletion(finalCmd *Command, args []string, lastArg string) (*p if len(lastArg) > 0 && lastArg[0] == '-' { if index := strings.Index(lastArg, "="); index >= 0 { // Flag with an = - if strings.HasPrefix(lastArg[:index], "--") { - // Flag has full name - flagName = lastArg[2:index] - } else { - // Flag is shorthand - // We have to get the last shorthand flag name - // e.g. `-asd` => d to provide the correct completion - // https://github.com/spf13/cobra/issues/1257 - flagName = lastArg[index-1 : index] - } + flagName = strings.TrimLeft(lastArg[:index], "-") lastArg = lastArg[index+1:] flagWithEqual = true } else { @@ -479,16 +459,8 @@ func checkIfFlagCompletion(finalCmd *Command, args []string, lastArg string) (*p // If the flag contains an = it means it has already been fully processed, // so we don't need to deal with it here. if index := strings.Index(prevArg, "="); index < 0 { - if strings.HasPrefix(prevArg, "--") { - // Flag has full name - flagName = prevArg[2:] - } else { - // Flag is shorthand - // We have to get the last shorthand flag name - // e.g. `-asd` => d to provide the correct completion - // https://github.com/spf13/cobra/issues/1257 - flagName = prevArg[len(prevArg)-1:] - } + flagName = strings.TrimLeft(prevArg, "-") + // Remove the uncompleted flag or else there could be an error created // for an invalid value for that flag trimmedArgs = args[:len(args)-1] @@ -541,65 +513,6 @@ func findFlag(cmd *Command, name string) *pflag.Flag { return cmd.Flag(name) } -func nonCompletableFlag(flag *pflag.Flag) bool { - return flag.Hidden || len(flag.Deprecated) > 0 -} - -// This function checks if legacy bash custom completion should be performed and if so, -// it provides the shell script with the necessary information. -func checkLegacyCustomCompletion(cmd *Command, args []string, flag *pflag.Flag, completions []string, directive ShellCompDirective) ([]string, ShellCompDirective) { - // Check if any legacy custom completion is defined for the program - if len(cmd.Root().BashCompletionFunction) > 0 { - // Legacy custom completion is only triggered if no other completions were found. - if len(completions) == 0 { - if flag != nil { - // For legacy custom flag completion, we must let the script know the bash - // functions it should call based on the content of the annotation BashCompCustom. - if values, present := flag.Annotations[BashCompCustom]; present { - if len(values) > 0 { - handlers := strings.Join(values, "; ") - // We send the commands to set the shell variables that are needed - // for legacy custom completions followed by the functions to call - // to perform the actual flag completion - completions = append(prepareLegacyCustomCompletionVars(cmd, args), handlers) - directive = directive | shellCompDirectiveLegacyCustomComp - } - } - } else { - // Check if the legacy custom_func is defined. - // This check will work for both "__custom_func" and "___custom_func". - // This could happen if the program defined some functions for legacy flag completion - // but not the legacy custom_func. - if strings.Contains(cmd.Root().BashCompletionFunction, "_custom_func") { - // For legacy args completion, the script already knows what to call - // so we only need to tell it the commands to set the shell variables needed - completions = prepareLegacyCustomCompletionVars(cmd, args) - directive = directive | shellCompDirectiveLegacyCustomComp | shellCompDirectiveLegacyCustomArgsComp - } - } - } - } - return completions, directive -} - -// The original bash completion script had some shell variables that are used by legacy bash -// custom completions. Let's set those variables to allow those legacy custom completions -// to continue working. -func prepareLegacyCustomCompletionVars(cmd *Command, args []string) []string { - var compVarCmds []string - - // "last_command" variable - commandName := cmd.CommandPath() - commandName = strings.Replace(commandName, " ", "_", -1) - commandName = strings.Replace(commandName, ":", "__", -1) - compVarCmds = append(compVarCmds, fmt.Sprintf("last_command=%s", commandName)) - - // "nouns" array variable - compVarCmds = append(compVarCmds, fmt.Sprintf("nouns=(%s)", strings.Join(args, " "))) - - return compVarCmds -} - // CompDebug prints the specified string to the same file as where the // completion script prints its logs. // Note that completion printouts should never be on stdout as they would diff --git a/vendor/github.com/spf13/cobra/fish_completions.go b/vendor/github.com/spf13/cobra/fish_completions.go index aeaef0087d..eaae9bca86 100644 --- a/vendor/github.com/spf13/cobra/fish_completions.go +++ b/vendor/github.com/spf13/cobra/fish_completions.go @@ -28,9 +28,9 @@ function __%[1]s_debug end function __%[1]s_perform_completion - __%[1]s_debug "Starting __%[1]s_perform_completion" + __%[1]s_debug "Starting __%[1]s_perform_completion with: $argv" - set args (string split -- " " (commandline -c)) + set args (string split -- " " "$argv") set lastArg "$args[-1]" __%[1]s_debug "args: $args" @@ -71,22 +71,31 @@ function __%[1]s_perform_completion printf "%%s\n" "$directiveLine" end -# This function does two things: -# - Obtain the completions and store them in the global __%[1]s_comp_results -# - Return false if file completion should be performed +# This function does three things: +# 1- Obtain the completions and store them in the global __%[1]s_comp_results +# 2- Set the __%[1]s_comp_do_file_comp flag if file completion should be performed +# and unset it otherwise +# 3- Return true if the completion results are not empty function __%[1]s_prepare_completions - __%[1]s_debug "" - __%[1]s_debug "========= starting completion logic ==========" - # Start fresh + set --erase __%[1]s_comp_do_file_comp set --erase __%[1]s_comp_results - set results (__%[1]s_perform_completion) + # Check if the command-line is already provided. This is useful for testing. + if not set --query __%[1]s_comp_commandLine + # Use the -c flag to allow for completion in the middle of the line + set __%[1]s_comp_commandLine (commandline -c) + end + __%[1]s_debug "commandLine is: $__%[1]s_comp_commandLine" + + set results (__%[1]s_perform_completion "$__%[1]s_comp_commandLine") + set --erase __%[1]s_comp_commandLine __%[1]s_debug "Completion results: $results" if test -z "$results" __%[1]s_debug "No completion, probably due to a failure" # Might as well do file completion, in case it helps + set --global __%[1]s_comp_do_file_comp 1 return 1 end @@ -101,8 +110,6 @@ function __%[1]s_prepare_completions set shellCompDirectiveNoFileComp %[6]d set shellCompDirectiveFilterFileExt %[7]d set shellCompDirectiveFilterDirs %[8]d - set shellCompDirectiveLegacyCustomComp %[9]d - set shellCompDirectiveLegacyCustomArgsComp %[10]d if test -z "$directive" set directive 0 @@ -112,14 +119,6 @@ function __%[1]s_prepare_completions if test $compErr -eq 1 __%[1]s_debug "Received error directive: aborting." # Might as well do file completion, in case it helps - return 1 - end - - set legacyCustom (math (math --scale 0 $directive / $shellCompDirectiveLegacyCustomComp) %% 2) - set legacyCustomArgs (math (math --scale 0 $directive / $shellCompDirectiveLegacyCustomArgsComp) %% 2) - if test $legacyCustom -eq 1; or test $legacyCustomArgs -eq 1 - __%[1]s_debug "Legacy bash custom completion not applicable to fish" - # Do full file completion instead set --global __%[1]s_comp_do_file_comp 1 return 1 end @@ -129,6 +128,7 @@ function __%[1]s_prepare_completions if test $filefilter -eq 1; or test $dirfilter -eq 1 __%[1]s_debug "File extension filtering or directory filtering not supported" # Do full file completion instead + set --global __%[1]s_comp_do_file_comp 1 return 1 end @@ -137,51 +137,27 @@ function __%[1]s_prepare_completions __%[1]s_debug "nospace: $nospace, nofiles: $nofiles" - # If we want to prevent a space, or if file completion is NOT disabled, - # we need to count the number of valid completions. - # To do so, we will filter on prefix as the completions we have received - # may not already be filtered so as to allow fish to match on different - # criteria than prefix. - if test $nospace -ne 0; or test $nofiles -eq 0 - set prefix (commandline -t) - __%[1]s_debug "prefix: $prefix" - - set completions - for comp in $__%[1]s_comp_results - if test (string match -e -r "^$prefix" "$comp") - set -a completions $comp - end - end - set --global __%[1]s_comp_results $completions - __%[1]s_debug "Filtered completions are: $__%[1]s_comp_results" - - # Important not to quote the variable for count to work - set numComps (count $__%[1]s_comp_results) - __%[1]s_debug "numComps: $numComps" - - if test $numComps -eq 1; and test $nospace -ne 0 - # To support the "nospace" directive we trick the shell - # by outputting an extra, longer completion. - # We must first split on \t to get rid of the descriptions because - # the extra character we add to the fake second completion must be - # before the description. We don't need descriptions anyway since - # there is only a single real completion which the shell will expand - # immediately. - __%[1]s_debug "Adding second completion to perform nospace directive" - set split (string split --max 1 \t $__%[1]s_comp_results[1]) - set --global __%[1]s_comp_results $split[1] $split[1]. - __%[1]s_debug "Completions are now: $__%[1]s_comp_results" - end - - if test $numComps -eq 0; and test $nofiles -eq 0 - # To be consistent with bash and zsh, we only trigger file - # completion when there are no other completions - __%[1]s_debug "Requesting file completion" - return 1 - end + # Important not to quote the variable for count to work + set numComps (count $__%[1]s_comp_results) + __%[1]s_debug "numComps: $numComps" + + if test $numComps -eq 1; and test $nospace -ne 0 + # To support the "nospace" directive we trick the shell + # by outputting an extra, longer completion. + __%[1]s_debug "Adding second completion to perform nospace directive" + set --append __%[1]s_comp_results $__%[1]s_comp_results[1]. end - return 0 + if test $numComps -eq 0; and test $nofiles -eq 0 + __%[1]s_debug "Requesting file completion" + set --global __%[1]s_comp_do_file_comp 1 + end + + # If we don't want file completion, we must return true even if there + # are no completions found. This is because fish will perform the last + # completion command, even if its condition is false, if no other + # completion command was triggered + return (not set --query __%[1]s_comp_do_file_comp) end # Since Fish completions are only loaded once the user triggers them, we trigger them ourselves @@ -194,14 +170,21 @@ complete --do-complete "%[2]s " > /dev/null 2>&1 # Remove any pre-existing completions for the program since we will be handling all of them. complete -c %[2]s -e -# The call to __%[1]s_prepare_completions will setup __%[1]s_comp_results -# which provides the program's completion choices. +# The order in which the below two lines are defined is very important so that __%[1]s_prepare_completions +# is called first. It is __%[1]s_prepare_completions that sets up the __%[1]s_comp_do_file_comp variable. +# +# This completion will be run second as complete commands are added FILO. +# It triggers file completion choices when __%[1]s_comp_do_file_comp is set. +complete -c %[2]s -n 'set --query __%[1]s_comp_do_file_comp' + +# This completion will be run first as complete commands are added FILO. +# The call to __%[1]s_prepare_completions will setup both __%[1]s_comp_results and __%[1]s_comp_do_file_comp. +# It provides the program's completion choices. complete -c %[2]s -n '__%[1]s_prepare_completions' -f -a '$__%[1]s_comp_results' `, nameForVar, name, compCmd, ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp, - ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs, - shellCompDirectiveLegacyCustomComp, shellCompDirectiveLegacyCustomArgsComp)) + ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs)) } // GenFishCompletion generates fish completion file and writes to the passed writer. diff --git a/vendor/github.com/spf13/cobra/zsh_completions.go b/vendor/github.com/spf13/cobra/zsh_completions.go index ef290e2eb1..92a70394a9 100644 --- a/vendor/github.com/spf13/cobra/zsh_completions.go +++ b/vendor/github.com/spf13/cobra/zsh_completions.go @@ -94,10 +94,8 @@ _%[1]s() local shellCompDirectiveNoFileComp=%[5]d local shellCompDirectiveFilterFileExt=%[6]d local shellCompDirectiveFilterDirs=%[7]d - local shellCompDirectiveLegacyCustomComp=%[8]d - local shellCompDirectiveLegacyCustomArgsComp=%[9]d - local lastParam lastChar flagPrefix requestComp out directive comp lastComp noSpace + local lastParam lastChar flagPrefix requestComp out directive compCount comp lastComp local -a completions __%[1]s_debug "\n========= starting completion logic ==========" @@ -165,6 +163,7 @@ _%[1]s() return fi + compCount=0 while IFS='\n' read -r comp; do if [ -n "$comp" ]; then # If requested, completions are returned with a description. @@ -176,17 +175,13 @@ _%[1]s() local tab=$(printf '\t') comp=${comp//$tab/:} + ((compCount++)) __%[1]s_debug "Adding completion: ${comp}" completions+=${comp} lastComp=$comp fi done < <(printf "%%s\n" "${out[@]}") - if [ $((directive & shellCompDirectiveNoSpace)) -ne 0 ]; then - __%[1]s_debug "Activating nospace." - noSpace="-S ''" - fi - if [ $((directive & shellCompDirectiveFilterFileExt)) -ne 0 ]; then # File extension filtering local filteringCmd @@ -213,40 +208,25 @@ _%[1]s() __%[1]s_debug "Listing directories in ." fi - local result _arguments '*:dirname:_files -/'" ${flagPrefix}" - result=$? if [ -n "$subdir" ]; then popd >/dev/null 2>&1 fi - return $result - else - __%[1]s_debug "Calling _describe" - if eval _describe "completions" completions $flagPrefix $noSpace; then - __%[1]s_debug "_describe found some completions" - - # Return the success of having called _describe - return 0 + elif [ $((directive & shellCompDirectiveNoSpace)) -ne 0 ] && [ ${compCount} -eq 1 ]; then + __%[1]s_debug "Activating nospace." + # We can use compadd here as there is no description when + # there is only one completion. + compadd -S '' "${lastComp}" + elif [ ${compCount} -eq 0 ]; then + if [ $((directive & shellCompDirectiveNoFileComp)) -ne 0 ]; then + __%[1]s_debug "deactivating file completion" else - __%[1]s_debug "_describe did not find completions." - __%[1]s_debug "Checking if we should do file completion." - if [ $((directive & shellCompDirectiveNoFileComp)) -ne 0 ]; then - __%[1]s_debug "deactivating file completion" - - # We must return an error code here to let zsh know that there were no - # completions found by _describe; this is what will trigger other - # matching algorithms to attempt to find completions. - # For example zsh can match letters in the middle of words. - return 1 - else - # Perform file completion - __%[1]s_debug "Activating file completion" - - # We must return the result of this command, so it must be the - # last command, or else we must store its result to return it. - _arguments '*:filename:_files'" ${flagPrefix}" - fi + # Perform file completion + __%[1]s_debug "activating file completion" + _arguments '*:filename:_files'" ${flagPrefix}" fi + else + _describe "completions" completions $(echo $flagPrefix) fi } @@ -256,6 +236,5 @@ if [ "$funcstack[1]" = "_%[1]s" ]; then fi `, name, compCmd, ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp, - ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs, - shellCompDirectiveLegacyCustomComp, shellCompDirectiveLegacyCustomArgsComp)) + ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs)) } diff --git a/vendor/modules.txt b/vendor/modules.txt index 674b7a4e4a..557fe263f5 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -512,7 +512,7 @@ github.com/seccomp/libseccomp-golang # github.com/sirupsen/logrus v1.7.0 github.com/sirupsen/logrus github.com/sirupsen/logrus/hooks/syslog -# github.com/spf13/cobra v1.1.1 => github.com/Luap99/cobra v1.0.1-0.20201110155035-83a59186c706 +# github.com/spf13/cobra v1.1.1 github.com/spf13/cobra # github.com/spf13/pflag v1.0.5 github.com/spf13/pflag From 3288edcc328118ab8adc9b4232148e4063d00928 Mon Sep 17 00:00:00 2001 From: baude Date: Tue, 1 Dec 2020 12:28:52 -0600 Subject: [PATCH 21/24] compat create should use bindings the volumes provided is seemingly useless representing what volumes should be added to a container. instead, the host config bindings should be used as they acurately describe the src/dest and options for bindings. Signed-off-by: baude --- cmd/podman/common/create_opts.go | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/cmd/podman/common/create_opts.go b/cmd/podman/common/create_opts.go index 23de21676d..a20ac9d786 100644 --- a/cmd/podman/common/create_opts.go +++ b/cmd/podman/common/create_opts.go @@ -211,12 +211,6 @@ func ContainerCreateToContainerCLIOpts(cc handlers.CreateContainerConfig, cgroup mounts = append(mounts, mount) } - // volumes - volumes := make([]string, 0, len(cc.Config.Volumes)) - for v := range cc.Config.Volumes { - volumes = append(volumes, v) - } - // dns dns := make([]net.IP, 0, len(cc.HostConfig.DNS)) for _, d := range cc.HostConfig.DNS { @@ -373,7 +367,6 @@ func ContainerCreateToContainerCLIOpts(cc handlers.CreateContainerConfig, cgroup UserNS: string(cc.HostConfig.UsernsMode), UTS: string(cc.HostConfig.UTSMode), Mount: mounts, - Volume: volumes, VolumesFrom: cc.HostConfig.VolumesFrom, Workdir: cc.Config.WorkingDir, Net: &netInfo, @@ -388,6 +381,10 @@ func ContainerCreateToContainerCLIOpts(cc handlers.CreateContainerConfig, cgroup } } + // volumes + if volumes := cc.HostConfig.Binds; len(volumes) > 0 { + cliOpts.Volume = volumes + } if len(cc.HostConfig.BlkioWeightDevice) > 0 { devices := make([]string, 0, len(cc.HostConfig.BlkioWeightDevice)) for _, d := range cc.HostConfig.BlkioWeightDevice { From 18d75e5ecc36fbb3bcc1f2daf37948ba4ce541d6 Mon Sep 17 00:00:00 2001 From: zhangguanzhang Date: Thu, 3 Dec 2020 19:42:30 +0800 Subject: [PATCH 22/24] Fix some nit Signed-off-by: zhangguanzhang --- test/apiv2/20-containers.at | 2 +- utils/utils_supported.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/apiv2/20-containers.at b/test/apiv2/20-containers.at index b35c272159..5c35edf2bb 100644 --- a/test/apiv2/20-containers.at +++ b/test/apiv2/20-containers.at @@ -169,7 +169,7 @@ t GET containers/$cid/json 200 \ .Args[1]="param2" t DELETE containers/$cid 204 -# test only set the entrpoint, Cmd should be [] +# test only set the entrypoint, Cmd should be [] t POST containers/create '"Image":"'$IMAGE'","Entrypoint":["echo","param1"]' 201 \ .Id~[0-9a-f]\\{64\\} cid=$(jq -r '.Id' <<<"$output") diff --git a/utils/utils_supported.go b/utils/utils_supported.go index e6978ca6f2..6f517dc72e 100644 --- a/utils/utils_supported.go +++ b/utils/utils_supported.go @@ -184,7 +184,7 @@ func moveUnderCgroup(cgroup, subtree string, processes []uint32) error { } for _, pid := range bytes.Split(processesData, []byte("\n")) { if _, err := f.Write(pid); err != nil { - logrus.Warnf("Cannot move process %d to cgroup %q", pid, newCgroup) + logrus.Warnf("Cannot move process %s to cgroup %q", string(pid), newCgroup) } } } From 5980a66cc01071de2e72a38c482d6d2b24978536 Mon Sep 17 00:00:00 2001 From: Matthew Heon Date: Mon, 7 Dec 2020 15:24:59 -0500 Subject: [PATCH 23/24] Update release notes for v2.2.1 Signed-off-by: Matthew Heon --- RELEASE_NOTES.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index a1027b4657..c1c593e580 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,5 +1,30 @@ # Release Notes +## 2.2.1 +### Changes +- Due to a conflict with a previously-removed field, we were forced to modify the way image volumes (mounting images into containers using `--mount type=image`) were handled in the database. As a result, containers created in Podman 2.2.0 with image volumes will not have them in v2.2.1, and will need to be re-created. + +### Bugfixes +- Fixed a bug where rootless Podman would, on systems without the `XDG_RUNTIME_DIR` environment variable defined, use an incorrect path for the PID file of the Podman pause process, causing Podman to fail to start ([#8539](https://github.com/containers/podman/issues/8539)). +- Fixed a bug where containers created using Podman v1.7 and earlier were unusable in Podman due to JSON decode errors ([#8613](https://github.com/containers/podman/issues/8613)). +- Fixed a bug where Podman could retrieve invalid cgroup paths, instead of erroring, for containers that were not running. +- Fixed a bug where the `podman system reset` command would print a warning about a duplicate shutdown handler being registered. +- Fixed a bug where rootless Podman would attempt to mount `sysfs` in circumstances where it was not allowed; some OCI runtimes (notably `crun`) would fall back to alternatives and not fail, but others (notably `runc`) would fail to run containers. +- Fixed a bug where the `podman run` and `podman create` commands would fail to create containers from untagged images ([#8558](https://github.com/containers/podman/issues/8558)). +- Fixed a bug where remote Podman would prompt for a password even when the server did not support password authentication ([#8498](https://github.com/containers/podman/issues/8498)). +- Fixed a bug where the `podman exec` command did not move the Conmon process for the exec session into the correct cgroup. +- Fixed a bug where shell completion for the `ancestor` option to `podman ps --filter` did not work correctly. +- Fixed a bug where detached containers would not properly clean themselves up (or remove themselves if `--rm` was set) if the Podman command that created them was invoked with `--log-level=debug`. + +### API +- Fixed a bug where the Compat Create endpoint for Containers did not properly handle the `Binds` and `Mounts` parameters in `HostConfig`. +- Fixed a bug where the Compat Create endpoint for Containers ignored the `Name` query parameter. +- Fixed a bug where the Compat Create endpoint for Containers did not properly handle the "default" value for `NetworkMode` (this value is used extensively by `docker-compose`) ([#8544](https://github.com/containers/podman/issues/8544)). +- Fixed a bug where the Compat Build endpoint for Images would sometimes use the `target` query parameter as the image's tag incorrectly. + +### Misc +- Podman v2.2.0 vendored a non-released, custom version of the `github.com/spf13/cobra` package; this has been reverted to the latest upstream release to aid in packaging. + ## 2.2.0 ### Features - Experimental support for shortname aliasing has been added. This is not enabled by default, but can be turned on by setting the environment variable `CONTAINERS_SHORT_NAME_ALIASING` to `on`. Documentation is [available here](https://github.com/containers/image/blob/master/docs/containers-registries.conf.5.md#short-name-aliasing). From 33bde4569ccb65b447150894c56f9a351a09a471 Mon Sep 17 00:00:00 2001 From: Matthew Heon Date: Mon, 7 Dec 2020 15:27:24 -0500 Subject: [PATCH 24/24] Updat containers/image to v5.9.0 Signed-off-by: Matthew Heon --- RELEASE_NOTES.md | 5 +- go.mod | 2 +- go.sum | 2 + .../containers/image/v5/copy/copy.go | 12 +++ .../image/v5/pkg/compression/compression.go | 3 +- .../image/v5/pkg/shortnames/shortnames.go | 15 ++-- .../image/v5/signature/policy_config.go | 76 +++++++++++++++++++ .../v5/signature/policy_reference_match.go | 65 ++++++++++++++-- .../image/v5/signature/policy_types.go | 11 +++ .../containers/image/v5/version/version.go | 4 +- vendor/modules.txt | 2 +- 11 files changed, 177 insertions(+), 20 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index c1c593e580..a819fcac2b 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -2,7 +2,7 @@ ## 2.2.1 ### Changes -- Due to a conflict with a previously-removed field, we were forced to modify the way image volumes (mounting images into containers using `--mount type=image`) were handled in the database. As a result, containers created in Podman 2.2.0 with image volumes will not have them in v2.2.1, and will need to be re-created. +- Due to a conflict with a previously-removed field, we were forced to modify the way image volumes (mounting images into containers using `--mount type=image`) were handled in the database. As a result, containers created in Podman 2.2.0 with image volumes will not have them in v2.2.1, and these containers will need to be re-created. ### Bugfixes - Fixed a bug where rootless Podman would, on systems without the `XDG_RUNTIME_DIR` environment variable defined, use an incorrect path for the PID file of the Podman pause process, causing Podman to fail to start ([#8539](https://github.com/containers/podman/issues/8539)). @@ -20,10 +20,11 @@ - Fixed a bug where the Compat Create endpoint for Containers did not properly handle the `Binds` and `Mounts` parameters in `HostConfig`. - Fixed a bug where the Compat Create endpoint for Containers ignored the `Name` query parameter. - Fixed a bug where the Compat Create endpoint for Containers did not properly handle the "default" value for `NetworkMode` (this value is used extensively by `docker-compose`) ([#8544](https://github.com/containers/podman/issues/8544)). -- Fixed a bug where the Compat Build endpoint for Images would sometimes use the `target` query parameter as the image's tag incorrectly. +- Fixed a bug where the Compat Build endpoint for Images would sometimes incorrectly use the `target` query parameter as the image's tag. ### Misc - Podman v2.2.0 vendored a non-released, custom version of the `github.com/spf13/cobra` package; this has been reverted to the latest upstream release to aid in packaging. +- Updated the containers/image library to v5.9.0 ## 2.2.0 ### Features diff --git a/go.mod b/go.mod index 8ca63323e1..a9e2fe5b74 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/containers/buildah v1.18.0 github.com/containers/common v0.29.0 github.com/containers/conmon v2.0.20+incompatible - github.com/containers/image/v5 v5.8.1 + github.com/containers/image/v5 v5.9.0 github.com/containers/psgo v1.5.1 github.com/containers/storage v1.24.1 github.com/coreos/go-systemd/v22 v22.1.0 diff --git a/go.sum b/go.sum index 453c1499fe..c0fb963e50 100644 --- a/go.sum +++ b/go.sum @@ -103,6 +103,8 @@ github.com/containers/image/v5 v5.8.0 h1:B3FGHi0bdGXgg698kBIGOlHCXN5n+scJr6/5354 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/image/v5 v5.9.0 h1:dRmUtcluQcmasNo3DpnRoZjfU0rOu1qZeL6wlDJr10Q= +github.com/containers/image/v5 v5.9.0/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= diff --git a/vendor/github.com/containers/image/v5/copy/copy.go b/vendor/github.com/containers/image/v5/copy/copy.go index 4d5b076891..485db4d30a 100644 --- a/vendor/github.com/containers/image/v5/copy/copy.go +++ b/vendor/github.com/containers/image/v5/copy/copy.go @@ -53,6 +53,14 @@ var ( // compressionBufferSize is the buffer size used to compress a blob var compressionBufferSize = 1048576 +// expectedCompressionFormats is used to check if a blob with a specified media type is compressed +// using the algorithm that the media type says it should be compressed with +var expectedCompressionFormats = map[string]*compression.Algorithm{ + imgspecv1.MediaTypeImageLayerGzip: &compression.Gzip, + imgspecv1.MediaTypeImageLayerZstd: &compression.Zstd, + manifest.DockerV2Schema2LayerMediaType: &compression.Gzip, +} + // newDigestingReader returns an io.Reader implementation with contents of source, which will eventually return a non-EOF error // or set validationSucceeded/validationFailed to true if the source stream does/does not match expectedDigest. // (neither is set if EOF is never reached). @@ -1234,6 +1242,10 @@ func (c *copier) copyBlobFromStream(ctx context.Context, srcStream io.Reader, sr isCompressed := decompressor != nil destStream = bar.ProxyReader(destStream) + if expectedCompressionFormat, known := expectedCompressionFormats[srcInfo.MediaType]; known && isCompressed && compressionFormat.Name() != expectedCompressionFormat.Name() { + logrus.Debugf("blob %s with type %s should be compressed with %s, but compressor appears to be %s", srcInfo.Digest.String(), srcInfo.MediaType, expectedCompressionFormat.Name(), compressionFormat.Name()) + } + // === Send a copy of the original, uncompressed, stream, to a separate path if necessary. var originalLayerReader io.Reader // DO NOT USE this other than to drain the input if no other consumer in the pipeline has done so. if getOriginalLayerCopyWriter != nil { diff --git a/vendor/github.com/containers/image/v5/pkg/compression/compression.go b/vendor/github.com/containers/image/v5/pkg/compression/compression.go index 04d231c6d6..d5cfd8d31c 100644 --- a/vendor/github.com/containers/image/v5/pkg/compression/compression.go +++ b/vendor/github.com/containers/image/v5/pkg/compression/compression.go @@ -91,7 +91,8 @@ func CompressStream(dest io.Writer, algo Algorithm, level *int) (io.WriteCloser, return internal.AlgorithmCompressor(algo)(dest, level) } -// DetectCompressionFormat returns a DecompressorFunc if the input is recognized as a compressed format, nil otherwise. +// DetectCompressionFormat returns an Algorithm and DecompressorFunc if the input is recognized as a compressed format, an invalid +// value and nil otherwise. // Because it consumes the start of input, other consumers must use the returned io.Reader instead to also read from the beginning. func DetectCompressionFormat(input io.Reader) (Algorithm, DecompressorFunc, io.Reader, error) { buffer := [8]byte{} diff --git a/vendor/github.com/containers/image/v5/pkg/shortnames/shortnames.go b/vendor/github.com/containers/image/v5/pkg/shortnames/shortnames.go index e02703d773..198ac1cc64 100644 --- a/vendor/github.com/containers/image/v5/pkg/shortnames/shortnames.go +++ b/vendor/github.com/containers/image/v5/pkg/shortnames/shortnames.go @@ -225,9 +225,8 @@ func (c *PullCandidate) Record() error { // Note that tags and digests are stripped from the specified name before // looking up an alias. Stripped off tags and digests are later on appended to // all candidates. If neither tag nor digest is specified, candidates are -// normalized with the "latest" tag. PullCandidates in the returned value may -// be empty if there is no matching alias and no unqualified-search registries -// are configured. +// normalized with the "latest" tag. An error is returned if there is no +// matching alias and no unqualified-search registries are configured. // // Note that callers *must* call `(PullCandidate).Record` after a returned // item has been pulled successfully; this callback will record a new @@ -312,6 +311,10 @@ func Resolve(ctx *types.SystemContext, name string) (*Resolved, error) { if err != nil { return nil, err } + // Error out if there's no matching alias and no search registries. + if len(unqualifiedSearchRegistries) == 0 { + return nil, errors.Errorf("short-name %q did not resolve to an alias and no unqualified-search registries are defined in %q", name, usrConfig) + } resolved.originDescription = usrConfig for _, reg := range unqualifiedSearchRegistries { @@ -331,10 +334,8 @@ func Resolve(ctx *types.SystemContext, name string) (*Resolved, error) { return resolved, nil } - // If we have only one candidate, there's no ambiguity. In case of an - // empty candidate slices, callers can implement custom logic or raise - // an error. - if len(resolved.PullCandidates) <= 1 { + // If we have only one candidate, there's no ambiguity. + if len(resolved.PullCandidates) == 1 { return resolved, nil } diff --git a/vendor/github.com/containers/image/v5/signature/policy_config.go b/vendor/github.com/containers/image/v5/signature/policy_config.go index a4873e9fab..d8cc4a09bf 100644 --- a/vendor/github.com/containers/image/v5/signature/policy_config.go +++ b/vendor/github.com/containers/image/v5/signature/policy_config.go @@ -19,6 +19,7 @@ import ( "io/ioutil" "os" "path/filepath" + "regexp" "github.com/containers/image/v5/docker/reference" "github.com/containers/image/v5/transports" @@ -507,6 +508,8 @@ func newPolicyReferenceMatchFromJSON(data []byte) (PolicyReferenceMatch, error) res = &prmExactReference{} case prmTypeExactRepository: res = &prmExactRepository{} + case prmTypeRemapIdentity: + res = &prmRemapIdentity{} default: return nil, InvalidPolicyFormatError(fmt.Sprintf("Unknown policy reference match type \"%s\"", typeField.Type)) } @@ -693,3 +696,76 @@ func (prm *prmExactRepository) UnmarshalJSON(data []byte) error { *prm = *res return nil } + +// Private objects for validateIdentityRemappingPrefix +var ( + // remapIdentityDomainRegexp matches exactly a reference domain (name[:port]) + remapIdentityDomainRegexp = regexp.MustCompile("^" + reference.DomainRegexp.String() + "$") + // remapIdentityDomainPrefixRegexp matches a reference that starts with a domain; + // we need this because reference.NameRegexp accepts short names with docker.io implied. + remapIdentityDomainPrefixRegexp = regexp.MustCompile("^" + reference.DomainRegexp.String() + "/") + // remapIdentityNameRegexp matches exactly a reference.Named name (possibly unnormalized) + remapIdentityNameRegexp = regexp.MustCompile("^" + reference.NameRegexp.String() + "$") +) + +// validateIdentityRemappingPrefix returns an InvalidPolicyFormatError if s is detected to be invalid +// for the Prefix or SignedPrefix values of prmRemapIdentity. +// Note that it may not recognize _all_ invalid values. +func validateIdentityRemappingPrefix(s string) error { + if remapIdentityDomainRegexp.MatchString(s) || + (remapIdentityNameRegexp.MatchString(s) && remapIdentityDomainPrefixRegexp.MatchString(s)) { + // FIXME? This does not reject "shortname" nor "ns/shortname", because docker/reference + // does not provide an API for the short vs. long name logic. + // It will either not match, or fail in the ParseNamed call of + // prmRemapIdentity.remapReferencePrefix when trying to use such a prefix. + return nil + } + return InvalidPolicyFormatError(fmt.Sprintf("prefix %q is not valid", s)) +} + +// newPRMRemapIdentity is NewPRMRemapIdentity, except it returns the private type. +func newPRMRemapIdentity(prefix, signedPrefix string) (*prmRemapIdentity, error) { + if err := validateIdentityRemappingPrefix(prefix); err != nil { + return nil, err + } + if err := validateIdentityRemappingPrefix(signedPrefix); err != nil { + return nil, err + } + return &prmRemapIdentity{ + prmCommon: prmCommon{Type: prmTypeRemapIdentity}, + Prefix: prefix, + SignedPrefix: signedPrefix, + }, nil +} + +// NewPRMRemapIdentity returns a new "remapIdentity" PolicyRepositoryMatch. +func NewPRMRemapIdentity(prefix, signedPrefix string) (PolicyReferenceMatch, error) { + return newPRMRemapIdentity(prefix, signedPrefix) +} + +// Compile-time check that prmRemapIdentity implements json.Unmarshaler. +var _ json.Unmarshaler = (*prmRemapIdentity)(nil) + +// UnmarshalJSON implements the json.Unmarshaler interface. +func (prm *prmRemapIdentity) UnmarshalJSON(data []byte) error { + *prm = prmRemapIdentity{} + var tmp prmRemapIdentity + if err := paranoidUnmarshalJSONObjectExactFields(data, map[string]interface{}{ + "type": &tmp.Type, + "prefix": &tmp.Prefix, + "signedPrefix": &tmp.SignedPrefix, + }); err != nil { + return err + } + + if tmp.Type != prmTypeRemapIdentity { + return InvalidPolicyFormatError(fmt.Sprintf("Unexpected policy requirement type \"%s\"", tmp.Type)) + } + + res, err := newPRMRemapIdentity(tmp.Prefix, tmp.SignedPrefix) + if err != nil { + return err + } + *prm = *res + return nil +} diff --git a/vendor/github.com/containers/image/v5/signature/policy_reference_match.go b/vendor/github.com/containers/image/v5/signature/policy_reference_match.go index e2a21f01d8..064866cf6c 100644 --- a/vendor/github.com/containers/image/v5/signature/policy_reference_match.go +++ b/vendor/github.com/containers/image/v5/signature/policy_reference_match.go @@ -4,6 +4,7 @@ package signature import ( "fmt" + "strings" "github.com/containers/image/v5/docker/reference" "github.com/containers/image/v5/transports" @@ -36,12 +37,9 @@ func (prm *prmMatchExact) matchesDockerReference(image types.UnparsedImage, sign return signature.String() == intended.String() } -func (prm *prmMatchRepoDigestOrExact) matchesDockerReference(image types.UnparsedImage, signatureDockerReference string) bool { - intended, signature, err := parseImageAndDockerReference(image, signatureDockerReference) - if err != nil { - return false - } - +// matchRepoDigestOrExactReferenceValues implements prmMatchRepoDigestOrExact.matchesDockerReference +// using reference.Named values. +func matchRepoDigestOrExactReferenceValues(intended, signature reference.Named) bool { // Do not add default tags: image.Reference().DockerReference() should contain it already, and signatureDockerReference should be exact; so, verify that now. if reference.IsNameOnly(signature) { return false @@ -58,6 +56,13 @@ func (prm *prmMatchRepoDigestOrExact) matchesDockerReference(image types.Unparse return false } } +func (prm *prmMatchRepoDigestOrExact) matchesDockerReference(image types.UnparsedImage, signatureDockerReference string) bool { + intended, signature, err := parseImageAndDockerReference(image, signatureDockerReference) + if err != nil { + return false + } + return matchRepoDigestOrExactReferenceValues(intended, signature) +} func (prm *prmMatchRepository) matchesDockerReference(image types.UnparsedImage, signatureDockerReference string) bool { intended, signature, err := parseImageAndDockerReference(image, signatureDockerReference) @@ -99,3 +104,51 @@ func (prm *prmExactRepository) matchesDockerReference(image types.UnparsedImage, } return signature.Name() == intended.Name() } + +// refMatchesPrefix returns true if ref matches prm.Prefix. +func (prm *prmRemapIdentity) refMatchesPrefix(ref reference.Named) bool { + name := ref.Name() + switch { + case len(name) < len(prm.Prefix): + return false + case len(name) == len(prm.Prefix): + return name == prm.Prefix + case len(name) > len(prm.Prefix): + // We are matching only ref.Name(), not ref.String(), so the only separator we are + // expecting is '/': + // - '@' is only valid to separate a digest, i.e. not a part of ref.Name() + // - similarly ':' to mark a tag would not be a part of ref.Name(); it can be a part of a + // host:port domain syntax, but we don't treat that specially and require an exact match + // of the domain. + return strings.HasPrefix(name, prm.Prefix) && name[len(prm.Prefix)] == '/' + default: + panic("Internal error: impossible comparison outcome") + } +} + +// remapReferencePrefix returns the result of remapping ref, if it matches prm.Prefix +// or the original ref if it does not. +func (prm *prmRemapIdentity) remapReferencePrefix(ref reference.Named) (reference.Named, error) { + if !prm.refMatchesPrefix(ref) { + return ref, nil + } + refString := ref.String() + newNamedRef := strings.Replace(refString, prm.Prefix, prm.SignedPrefix, 1) + newParsedRef, err := reference.ParseNamed(newNamedRef) + if err != nil { + return nil, fmt.Errorf(`error rewriting reference from "%s" to "%s": %v`, refString, newNamedRef, err) + } + return newParsedRef, nil +} + +func (prm *prmRemapIdentity) matchesDockerReference(image types.UnparsedImage, signatureDockerReference string) bool { + intended, signature, err := parseImageAndDockerReference(image, signatureDockerReference) + if err != nil { + return false + } + intended, err = prm.remapReferencePrefix(intended) + if err != nil { + return false + } + return matchRepoDigestOrExactReferenceValues(intended, signature) +} diff --git a/vendor/github.com/containers/image/v5/signature/policy_types.go b/vendor/github.com/containers/image/v5/signature/policy_types.go index d3b33bb7ac..c6819929bd 100644 --- a/vendor/github.com/containers/image/v5/signature/policy_types.go +++ b/vendor/github.com/containers/image/v5/signature/policy_types.go @@ -121,6 +121,7 @@ const ( prmTypeMatchRepository prmTypeIdentifier = "matchRepository" prmTypeExactReference prmTypeIdentifier = "exactReference" prmTypeExactRepository prmTypeIdentifier = "exactRepository" + prmTypeRemapIdentity prmTypeIdentifier = "remapIdentity" ) // prmMatchExact is a PolicyReferenceMatch with type = prmMatchExact: the two references must match exactly. @@ -150,3 +151,13 @@ type prmExactRepository struct { prmCommon DockerRepository string `json:"dockerRepository"` } + +// prmRemapIdentity is a PolicyReferenceMatch with type = prmRemapIdentity: like prmMatchRepoDigestOrExact, +// except that a namespace (at least a host:port, at most a single repository) is substituted before matching the two references. +type prmRemapIdentity struct { + prmCommon + Prefix string `json:"prefix"` + SignedPrefix string `json:"signedPrefix"` + // Possibly let the users make a choice for tag/digest matching behavior + // similar to prmMatchExact/prmMatchRepository? +} diff --git a/vendor/github.com/containers/image/v5/version/version.go b/vendor/github.com/containers/image/v5/version/version.go index 14e553c9f1..48ecf938c1 100644 --- a/vendor/github.com/containers/image/v5/version/version.go +++ b/vendor/github.com/containers/image/v5/version/version.go @@ -6,9 +6,9 @@ const ( // VersionMajor is for an API incompatible changes VersionMajor = 5 // VersionMinor is for functionality in a backwards-compatible manner - VersionMinor = 8 + VersionMinor = 9 // VersionPatch is for backwards-compatible bug fixes - VersionPatch = 1 + VersionPatch = 0 // VersionDev indicates development branch. Releases will be empty string. VersionDev = "" diff --git a/vendor/modules.txt b/vendor/modules.txt index 557fe263f5..b4222596a0 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -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.1 +# github.com/containers/image/v5 v5.9.0 github.com/containers/image/v5/copy github.com/containers/image/v5/directory github.com/containers/image/v5/directory/explicitfilepath