summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVladimir Kochnev <hashtable@yandex.ru>2022-08-19 00:41:22 +0300
committerVladimir Kochnev <hashtable@yandex.ru>2022-08-19 00:41:22 +0300
commit3bf52aa338b33de719e087e15402081568453284 (patch)
tree7ec3736282de122eba75ebd9b1870908be9b3b28
parentec9508ea177a50ff3361a9ef14efbe1ff6baf05d (diff)
downloadpodman-3bf52aa338b33de719e087e15402081568453284.tar.gz
podman-3bf52aa338b33de719e087e15402081568453284.tar.bz2
podman-3bf52aa338b33de719e087e15402081568453284.zip
Add ProgressWriter to PullOptions
Signed-off-by: Vladimir Kochnev <hashtable@yandex.ru>
-rw-r--r--cmd/podman/images/pull.go5
-rw-r--r--pkg/bindings/images/pull.go13
-rw-r--r--pkg/bindings/images/push.go9
-rw-r--r--pkg/bindings/images/types.go2
-rw-r--r--pkg/bindings/images/types_pull_options.go16
-rw-r--r--pkg/bindings/manifests/manifests.go6
-rw-r--r--pkg/bindings/test/images_test.go24
-rw-r--r--pkg/bindings/test/manifests_test.go23
-rw-r--r--pkg/domain/entities/images.go2
-rw-r--r--pkg/domain/infra/abi/images.go3
-rw-r--r--pkg/domain/infra/tunnel/images.go1
-rw-r--r--test/e2e/manifest_test.go27
-rw-r--r--test/e2e/pull_test.go14
13 files changed, 128 insertions, 17 deletions
diff --git a/cmd/podman/images/pull.go b/cmd/podman/images/pull.go
index 8211ceba5..fe9d1e9b6 100644
--- a/cmd/podman/images/pull.go
+++ b/cmd/podman/images/pull.go
@@ -155,6 +155,11 @@ func imagePull(cmd *cobra.Command, args []string) error {
pullOptions.Username = creds.Username
pullOptions.Password = creds.Password
}
+
+ if !pullOptions.Quiet {
+ pullOptions.Writer = os.Stderr
+ }
+
// Let's do all the remaining Yoga in the API to prevent us from
// scattering logic across (too) many parts of the code.
var errs utils.OutputErrors
diff --git a/pkg/bindings/images/pull.go b/pkg/bindings/images/pull.go
index 1a4aa3038..109981c63 100644
--- a/pkg/bindings/images/pull.go
+++ b/pkg/bindings/images/pull.go
@@ -6,7 +6,6 @@ import (
"errors"
"fmt"
"io"
- "io/ioutil"
"net/http"
"os"
"strconv"
@@ -57,10 +56,14 @@ func Pull(ctx context.Context, rawImage string, options *PullOptions) ([]string,
return nil, response.Process(err)
}
- // Historically pull writes status to stderr
- stderr := io.Writer(os.Stderr)
+ var writer io.Writer
if options.GetQuiet() {
- stderr = ioutil.Discard
+ writer = io.Discard
+ } else if progressWriter := options.GetProgressWriter(); progressWriter != nil {
+ writer = progressWriter
+ } else {
+ // Historically push writes status to stderr
+ writer = os.Stderr
}
dec := json.NewDecoder(response.Body)
@@ -84,7 +87,7 @@ func Pull(ctx context.Context, rawImage string, options *PullOptions) ([]string,
switch {
case report.Stream != "":
- fmt.Fprint(stderr, report.Stream)
+ fmt.Fprint(writer, report.Stream)
case report.Error != "":
pullErrors = append(pullErrors, errors.New(report.Error))
case len(report.Images) > 0:
diff --git a/pkg/bindings/images/push.go b/pkg/bindings/images/push.go
index 5069dd780..f1e059f8c 100644
--- a/pkg/bindings/images/push.go
+++ b/pkg/bindings/images/push.go
@@ -6,7 +6,6 @@ import (
"errors"
"fmt"
"io"
- "io/ioutil"
"net/http"
"os"
"strconv"
@@ -58,12 +57,14 @@ func Push(ctx context.Context, source string, destination string, options *PushO
return response.Process(err)
}
- // Historically push writes status to stderr
- writer := io.Writer(os.Stderr)
+ var writer io.Writer
if options.GetQuiet() {
- writer = ioutil.Discard
+ writer = io.Discard
} else if progressWriter := options.GetProgressWriter(); progressWriter != nil {
writer = progressWriter
+ } else {
+ // Historically push writes status to stderr
+ writer = os.Stderr
}
dec := json.NewDecoder(response.Body)
diff --git a/pkg/bindings/images/types.go b/pkg/bindings/images/types.go
index 7b28c499e..3ecfb9e09 100644
--- a/pkg/bindings/images/types.go
+++ b/pkg/bindings/images/types.go
@@ -182,6 +182,8 @@ type PullOptions struct {
Policy *string
// Password for authenticating against the registry.
Password *string
+ // ProgressWriter is a writer where pull progress are sent.
+ ProgressWriter *io.Writer
// Quiet can be specified to suppress pull progress when pulling. Ignored
// for remote calls.
Quiet *bool
diff --git a/pkg/bindings/images/types_pull_options.go b/pkg/bindings/images/types_pull_options.go
index 4cd525185..c1a88fd9e 100644
--- a/pkg/bindings/images/types_pull_options.go
+++ b/pkg/bindings/images/types_pull_options.go
@@ -2,6 +2,7 @@
package images
import (
+ "io"
"net/url"
"github.com/containers/podman/v4/pkg/bindings/internal/util"
@@ -107,6 +108,21 @@ func (o *PullOptions) GetPassword() string {
return *o.Password
}
+// WithProgressWriter set field ProgressWriter to given value
+func (o *PullOptions) WithProgressWriter(value io.Writer) *PullOptions {
+ o.ProgressWriter = &value
+ return o
+}
+
+// GetProgressWriter returns value of field ProgressWriter
+func (o *PullOptions) GetProgressWriter() io.Writer {
+ if o.ProgressWriter == nil {
+ var z io.Writer
+ return z
+ }
+ return *o.ProgressWriter
+}
+
// WithQuiet set field Quiet to given value
func (o *PullOptions) WithQuiet(value bool) *PullOptions {
o.Quiet = &value
diff --git a/pkg/bindings/manifests/manifests.go b/pkg/bindings/manifests/manifests.go
index 49e4089f5..0163d21a0 100644
--- a/pkg/bindings/manifests/manifests.go
+++ b/pkg/bindings/manifests/manifests.go
@@ -182,12 +182,14 @@ func Push(ctx context.Context, name, destination string, options *images.PushOpt
return "", response.Process(err)
}
- // Historically push writes status to stderr
- writer := io.Writer(os.Stderr)
+ var writer io.Writer
if options.GetQuiet() {
writer = io.Discard
} else if progressWriter := options.GetProgressWriter(); progressWriter != nil {
writer = progressWriter
+ } else {
+ // Historically push writes status to stderr
+ writer = os.Stderr
}
dec := json.NewDecoder(response.Body)
diff --git a/pkg/bindings/test/images_test.go b/pkg/bindings/test/images_test.go
index 9c9796661..53c5a1e83 100644
--- a/pkg/bindings/test/images_test.go
+++ b/pkg/bindings/test/images_test.go
@@ -1,11 +1,14 @@
package bindings_test
import (
+ "bytes"
+ "fmt"
"net/http"
"os"
"path/filepath"
"time"
+ podmanRegistry "github.com/containers/podman/v4/hack/podman-registry-go"
"github.com/containers/podman/v4/pkg/bindings"
"github.com/containers/podman/v4/pkg/bindings/containers"
"github.com/containers/podman/v4/pkg/bindings/images"
@@ -362,9 +365,14 @@ var _ = Describe("Podman images", func() {
It("Image Pull", func() {
rawImage := "docker.io/library/busybox:latest"
- pulledImages, err := images.Pull(bt.conn, rawImage, nil)
+ var writer bytes.Buffer
+ pullOpts := new(images.PullOptions).WithProgressWriter(&writer)
+ pulledImages, err := images.Pull(bt.conn, rawImage, pullOpts)
Expect(err).NotTo(HaveOccurred())
Expect(len(pulledImages)).To(Equal(1))
+ output := writer.String()
+ Expect(output).To(ContainSubstring("Trying to pull "))
+ Expect(output).To(ContainSubstring("Getting image source signatures"))
exists, err := images.Exists(bt.conn, rawImage, nil)
Expect(err).NotTo(HaveOccurred())
@@ -380,7 +388,19 @@ var _ = Describe("Podman images", func() {
})
It("Image Push", func() {
- Skip("TODO: implement test for image push to registry")
+ registry, err := podmanRegistry.Start()
+ Expect(err).To(BeNil())
+
+ var writer bytes.Buffer
+ pushOpts := new(images.PushOptions).WithUsername(registry.User).WithPassword(registry.Password).WithSkipTLSVerify(true).WithProgressWriter(&writer).WithQuiet(false)
+ err = images.Push(bt.conn, alpine.name, fmt.Sprintf("localhost:%s/test:latest", registry.Port), pushOpts)
+ Expect(err).ToNot(HaveOccurred())
+
+ output := writer.String()
+ Expect(output).To(ContainSubstring("Copying blob "))
+ Expect(output).To(ContainSubstring("Copying config "))
+ Expect(output).To(ContainSubstring("Writing manifest to image destination"))
+ Expect(output).To(ContainSubstring("Storing signatures"))
})
It("Build no options", func() {
diff --git a/pkg/bindings/test/manifests_test.go b/pkg/bindings/test/manifests_test.go
index 6a34ef5a6..d6749f920 100644
--- a/pkg/bindings/test/manifests_test.go
+++ b/pkg/bindings/test/manifests_test.go
@@ -1,9 +1,12 @@
package bindings_test
import (
+ "bytes"
+ "fmt"
"net/http"
"time"
+ podmanRegistry "github.com/containers/podman/v4/hack/podman-registry-go"
"github.com/containers/podman/v4/pkg/bindings"
"github.com/containers/podman/v4/pkg/bindings/images"
"github.com/containers/podman/v4/pkg/bindings/manifests"
@@ -12,7 +15,7 @@ import (
"github.com/onsi/gomega/gexec"
)
-var _ = Describe("podman manifest", func() {
+var _ = Describe("Podman manifests", func() {
var (
bt *bindingTest
s *gexec.Session
@@ -172,7 +175,21 @@ var _ = Describe("podman manifest", func() {
Expect(list.Manifests[0].Platform.OS).To(Equal("foo"))
})
- It("push manifest", func() {
- Skip("TODO: implement test for manifest push to registry")
+ It("Manifest Push", func() {
+ registry, err := podmanRegistry.Start()
+ Expect(err).To(BeNil())
+
+ name := "quay.io/libpod/foobar:latest"
+ _, err = manifests.Create(bt.conn, name, []string{alpine.name}, nil)
+ Expect(err).ToNot(HaveOccurred())
+
+ var writer bytes.Buffer
+ pushOpts := new(images.PushOptions).WithUsername(registry.User).WithPassword(registry.Password).WithAll(true).WithSkipTLSVerify(true).WithProgressWriter(&writer).WithQuiet(false)
+ _, err = manifests.Push(bt.conn, name, fmt.Sprintf("localhost:%s/test:latest", registry.Port), pushOpts)
+ Expect(err).ToNot(HaveOccurred())
+
+ output := writer.String()
+ Expect(output).To(ContainSubstring("Writing manifest list to image destination"))
+ Expect(output).To(ContainSubstring("Storing list signatures"))
})
})
diff --git a/pkg/domain/entities/images.go b/pkg/domain/entities/images.go
index 21c1372b9..cad11b0ab 100644
--- a/pkg/domain/entities/images.go
+++ b/pkg/domain/entities/images.go
@@ -156,6 +156,8 @@ type ImagePullOptions struct {
SkipTLSVerify types.OptionalBool
// PullPolicy whether to pull new image
PullPolicy config.PullPolicy
+ // Writer is used to display copy information including progress bars.
+ Writer io.Writer
}
// ImagePullReport is the response from pulling one or more images.
diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go
index 77d1bf0db..f9839f62f 100644
--- a/pkg/domain/infra/abi/images.go
+++ b/pkg/domain/infra/abi/images.go
@@ -237,8 +237,9 @@ func (ir *ImageEngine) Pull(ctx context.Context, rawImage string, options entiti
pullOptions.Variant = options.Variant
pullOptions.SignaturePolicyPath = options.SignaturePolicy
pullOptions.InsecureSkipTLSVerify = options.SkipTLSVerify
+ pullOptions.Writer = options.Writer
- if !options.Quiet {
+ if !options.Quiet && pullOptions.Writer == nil {
pullOptions.Writer = os.Stderr
}
diff --git a/pkg/domain/infra/tunnel/images.go b/pkg/domain/infra/tunnel/images.go
index bb3014099..2716aaf2a 100644
--- a/pkg/domain/infra/tunnel/images.go
+++ b/pkg/domain/infra/tunnel/images.go
@@ -110,6 +110,7 @@ func (ir *ImageEngine) Pull(ctx context.Context, rawImage string, opts entities.
options.WithAllTags(opts.AllTags).WithAuthfile(opts.Authfile).WithArch(opts.Arch).WithOS(opts.OS)
options.WithVariant(opts.Variant).WithPassword(opts.Password)
options.WithQuiet(opts.Quiet).WithUsername(opts.Username).WithPolicy(opts.PullPolicy.String())
+ options.WithProgressWriter(opts.Writer)
if s := opts.SkipTLSVerify; s != types.OptionalBoolUndefined {
if s == types.OptionalBoolTrue {
options.WithSkipTLSVerify(true)
diff --git a/test/e2e/manifest_test.go b/test/e2e/manifest_test.go
index 1c4aad710..60b72dcaa 100644
--- a/test/e2e/manifest_test.go
+++ b/test/e2e/manifest_test.go
@@ -350,6 +350,33 @@ var _ = Describe("Podman manifest", func() {
Expect(foundZstdFile).To(BeTrue())
})
+ It("push progress", func() {
+ SkipIfRemote("manifest push to dir not supported in remote mode")
+
+ session := podmanTest.Podman([]string{"manifest", "create", "foo", imageList})
+ session.WaitWithDefaultTimeout()
+ Expect(session).Should(Exit(0))
+
+ dest := filepath.Join(podmanTest.TempDir, "pushed")
+ err := os.MkdirAll(dest, os.ModePerm)
+ Expect(err).To(BeNil())
+ defer func() {
+ os.RemoveAll(dest)
+ }()
+
+ session = podmanTest.Podman([]string{"push", "foo", "-q", "dir:" + dest})
+ session.WaitWithDefaultTimeout()
+ Expect(session).Should(Exit(0))
+ Expect(session.ErrorToString()).To(BeEmpty())
+
+ session = podmanTest.Podman([]string{"push", "foo", "dir:" + dest})
+ session.WaitWithDefaultTimeout()
+ Expect(session).Should(Exit(0))
+ output := session.ErrorToString()
+ Expect(output).To(ContainSubstring("Writing manifest list to image destination"))
+ Expect(output).To(ContainSubstring("Storing list signatures"))
+ })
+
It("authenticated push", func() {
registryOptions := &podmanRegistry.Options{
Image: "docker-archive:" + imageTarPath(REGISTRY_IMAGE),
diff --git a/test/e2e/pull_test.go b/test/e2e/pull_test.go
index 12f14fdc8..ba717f393 100644
--- a/test/e2e/pull_test.go
+++ b/test/e2e/pull_test.go
@@ -545,4 +545,18 @@ var _ = Describe("Podman pull", func() {
Expect(data[0]).To(HaveField("Os", runtime.GOOS))
Expect(data[0]).To(HaveField("Architecture", "arm64"))
})
+
+ It("podman pull progress", func() {
+ session := podmanTest.Podman([]string{"pull", ALPINE})
+ session.WaitWithDefaultTimeout()
+ Expect(session).Should(Exit(0))
+ output := session.ErrorToString()
+ Expect(output).To(ContainSubstring("Getting image source signatures"))
+ Expect(output).To(ContainSubstring("Copying blob "))
+
+ session = podmanTest.Podman([]string{"pull", "-q", ALPINE})
+ session.WaitWithDefaultTimeout()
+ Expect(session).Should(Exit(0))
+ Expect(session.ErrorToString()).To(BeEmpty())
+ })
})