diff options
author | Valentin Rothberg <rothberg@redhat.com> | 2020-05-13 13:44:29 +0200 |
---|---|---|
committer | Valentin Rothberg <rothberg@redhat.com> | 2020-05-29 15:39:37 +0200 |
commit | dc80267b594e41cf7e223821dc1446683f0cae36 (patch) | |
tree | 8ca8f81cdf302b1905d7a56f7c5c76ba5468c6f1 | |
parent | 78c38460eb8ba9190d414f2da6a1414990cc6cfd (diff) | |
download | podman-dc80267b594e41cf7e223821dc1446683f0cae36.tar.gz podman-dc80267b594e41cf7e223821dc1446683f0cae36.tar.bz2 podman-dc80267b594e41cf7e223821dc1446683f0cae36.zip |
compat handlers: add X-Registry-Auth header support
* Support the `X-Registry-Auth` http-request header.
* The content of the header is a base64 encoded JSON payload which can
either be a single auth config or a map of auth configs (user+pw or
token) with the corresponding registries being the keys. Vanilla
Docker, projectatomic Docker and the bindings are transparantly
supported.
* Add a hidden `--registries-conf` flag. Buildah exposes the same
flag, mostly for testing purposes.
* Do all credential parsing in the client (i.e., `cmd/podman`) pass
the username and password in the backend instead of unparsed
credentials.
* Add a `pkg/auth` which handles most of the heavy lifting.
* Go through the authentication-handling code of most commands, bindings
and endpoints. Migrate them to the new code and fix issues as seen.
A final evaluation and more tests is still required *after* this
change.
* The manifest-push endpoint is missing certain parameters and should
use the ABI function instead. Adding auth-support isn't really
possible without these parts working.
* The container commands and endpoints (i.e., create and run) have not
been changed yet. The APIs don't yet account for the authfile.
* Add authentication tests to `pkg/bindings`.
Fixes: #6384
Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
108 files changed, 2579 insertions, 609 deletions
diff --git a/cmd/podman/images/pull.go b/cmd/podman/images/pull.go index 7bb4f6d37..9e137b5d6 100644 --- a/cmd/podman/images/pull.go +++ b/cmd/podman/images/pull.go @@ -8,6 +8,7 @@ import ( "github.com/containers/image/v5/types" "github.com/containers/libpod/cmd/podman/registry" "github.com/containers/libpod/pkg/domain/entities" + "github.com/containers/libpod/pkg/util" "github.com/pkg/errors" "github.com/spf13/cobra" "github.com/spf13/pflag" @@ -17,7 +18,8 @@ import ( // CLI-only fields into the API types. type pullOptionsWrapper struct { entities.ImagePullOptions - TLSVerifyCLI bool // CLI only + TLSVerifyCLI bool // CLI only + CredentialsCLI string } var ( @@ -77,7 +79,7 @@ func pullFlags(flags *pflag.FlagSet) { flags.BoolVar(&pullOptions.AllTags, "all-tags", false, "All tagged images in the repository will be pulled") flags.StringVar(&pullOptions.Authfile, "authfile", auth.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") flags.StringVar(&pullOptions.CertDir, "cert-dir", "", "`Pathname` of a directory containing TLS certificates and keys") - flags.StringVar(&pullOptions.Credentials, "creds", "", "`Credentials` (USERNAME:PASSWORD) to use for authenticating to a registry") + flags.StringVar(&pullOptions.CredentialsCLI, "creds", "", "`Credentials` (USERNAME:PASSWORD) to use for authenticating to a registry") flags.StringVar(&pullOptions.OverrideArch, "override-arch", "", "Use `ARCH` instead of the architecture of the machine for choosing images") flags.StringVar(&pullOptions.OverrideOS, "override-os", "", "Use `OS` instead of the running OS for choosing images") flags.BoolVarP(&pullOptions.Quiet, "quiet", "q", false, "Suppress output information when pulling images") @@ -107,6 +109,14 @@ func imagePull(cmd *cobra.Command, args []string) error { } } + if pullOptions.CredentialsCLI != "" { + creds, err := util.ParseRegistryCreds(pullOptions.CredentialsCLI) + if err != nil { + return err + } + pullOptions.Username = creds.Username + pullOptions.Password = creds.Password + } // Let's do all the remaining Yoga in the API to prevent us from // scattering logic across (too) many parts of the code. pullReport, err := registry.ImageEngine().Pull(registry.GetContext(), args[0], pullOptions.ImagePullOptions) diff --git a/cmd/podman/images/push.go b/cmd/podman/images/push.go index 35a6254de..a1614dc7a 100644 --- a/cmd/podman/images/push.go +++ b/cmd/podman/images/push.go @@ -7,6 +7,7 @@ import ( "github.com/containers/image/v5/types" "github.com/containers/libpod/cmd/podman/registry" "github.com/containers/libpod/pkg/domain/entities" + "github.com/containers/libpod/pkg/util" "github.com/pkg/errors" "github.com/spf13/cobra" "github.com/spf13/pflag" @@ -16,7 +17,8 @@ import ( // CLI-only fields into the API types. type pushOptionsWrapper struct { entities.ImagePushOptions - TLSVerifyCLI bool // CLI only + TLSVerifyCLI bool // CLI only + CredentialsCLI string } var ( @@ -73,7 +75,7 @@ func pushFlags(flags *pflag.FlagSet) { flags.StringVar(&pushOptions.Authfile, "authfile", auth.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") flags.StringVar(&pushOptions.CertDir, "cert-dir", "", "Path to a directory containing TLS certificates and keys") flags.BoolVar(&pushOptions.Compress, "compress", false, "Compress tarball image layers when pushing to a directory using the 'dir' transport. (default is same compression type as source)") - flags.StringVar(&pushOptions.Credentials, "creds", "", "`Credentials` (USERNAME:PASSWORD) to use for authenticating to a registry") + flags.StringVar(&pushOptions.CredentialsCLI, "creds", "", "`Credentials` (USERNAME:PASSWORD) to use for authenticating to a registry") flags.StringVar(&pushOptions.DigestFile, "digestfile", "", "Write the digest of the pushed image to the specified file") flags.StringVarP(&pushOptions.Format, "format", "f", "", "Manifest type (oci, v2s1, or v2s2) to use when pushing an image using the 'dir' transport (default is manifest type of source)") flags.BoolVarP(&pushOptions.Quiet, "quiet", "q", false, "Suppress output information when pushing images") @@ -122,6 +124,15 @@ func imagePush(cmd *cobra.Command, args []string) error { } } + if pushOptions.CredentialsCLI != "" { + creds, err := util.ParseRegistryCreds(pushOptions.CredentialsCLI) + if err != nil { + return err + } + pushOptions.Username = creds.Username + pushOptions.Password = creds.Password + } + // Let's do all the remaining Yoga in the API to prevent us from scattering // logic across (too) many parts of the code. return registry.ImageEngine().Push(registry.GetContext(), source, destination, pushOptions.ImagePushOptions) diff --git a/cmd/podman/manifest/push.go b/cmd/podman/manifest/push.go index 49c76f40b..a2e68aff1 100644 --- a/cmd/podman/manifest/push.go +++ b/cmd/podman/manifest/push.go @@ -1,17 +1,26 @@ package manifest import ( - "context" - "github.com/containers/common/pkg/auth" + "github.com/containers/image/v5/types" "github.com/containers/libpod/cmd/podman/registry" "github.com/containers/libpod/pkg/domain/entities" + "github.com/containers/libpod/pkg/util" "github.com/pkg/errors" "github.com/spf13/cobra" ) +// manifestPushOptsWrapper wraps entities.ManifestPushOptions and prevents leaking +// CLI-only fields into the API types. +type manifestPushOptsWrapper struct { + entities.ManifestPushOptions + + TLSVerifyCLI bool // CLI only + CredentialsCLI string +} + var ( - manifestPushOpts = entities.ManifestPushOptions{} + manifestPushOpts = manifestPushOptsWrapper{} pushCmd = &cobra.Command{ Use: "push [flags] SOURCE DESTINATION", Short: "Push a manifest list or image index to a registry", @@ -33,12 +42,12 @@ func init() { flags.BoolVar(&manifestPushOpts.All, "all", false, "also push the images in the list") flags.StringVar(&manifestPushOpts.Authfile, "authfile", auth.GetDefaultAuthFile(), "path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") flags.StringVar(&manifestPushOpts.CertDir, "cert-dir", "", "use certificates at the specified path to access the registry") - flags.StringVar(&manifestPushOpts.Creds, "creds", "", "use `[username[:password]]` for accessing the registry") + flags.StringVar(&manifestPushOpts.CredentialsCLI, "creds", "", "use `[username[:password]]` for accessing the registry") flags.StringVar(&manifestPushOpts.DigestFile, "digestfile", "", "after copying the image, write the digest of the resulting digest to the file") flags.StringVarP(&manifestPushOpts.Format, "format", "f", "", "manifest type (oci or v2s2) to attempt to use when pushing the manifest list (default is manifest type of source)") flags.BoolVarP(&manifestPushOpts.RemoveSignatures, "remove-signatures", "", false, "don't copy signatures when pushing images") flags.StringVar(&manifestPushOpts.SignBy, "sign-by", "", "sign the image using a GPG key with the specified `FINGERPRINT`") - flags.BoolVar(&manifestPushOpts.TlsVerify, "tls-verify", true, "require HTTPS and verify certificates when accessing the registry") + flags.BoolVar(&manifestPushOpts.TLSVerifyCLI, "tls-verify", true, "require HTTPS and verify certificates when accessing the registry") flags.BoolVarP(&manifestPushOpts.Quiet, "quiet", "q", false, "don't output progress information when pushing lists") if registry.IsRemote() { _ = flags.MarkHidden("authfile") @@ -59,7 +68,24 @@ func push(cmd *cobra.Command, args []string) error { if destSpec == "" { return errors.Errorf(`invalid destination "%s"`, destSpec) } - if err := registry.ImageEngine().ManifestPush(context.Background(), args, manifestPushOpts); err != nil { + + if manifestPushOpts.CredentialsCLI != "" { + creds, err := util.ParseRegistryCreds(manifestPushOpts.CredentialsCLI) + if err != nil { + return err + } + manifestPushOpts.Username = creds.Username + manifestPushOpts.Password = creds.Password + } + + // TLS verification in c/image is controlled via a `types.OptionalBool` + // which allows for distinguishing among set-true, set-false, unspecified + // which is important to implement a sane way of dealing with defaults of + // boolean CLI flags. + if cmd.Flags().Changed("tls-verify") { + manifestPushOpts.SkipTLSVerify = types.NewOptionalBool(!manifestPushOpts.TLSVerifyCLI) + } + if err := registry.ImageEngine().ManifestPush(registry.Context(), args, manifestPushOpts.ManifestPushOptions); err != nil { return errors.Wrapf(err, "error pushing manifest %s to %s", listImageSpec, destSpec) } return nil diff --git a/cmd/podman/play/kube.go b/cmd/podman/play/kube.go index 5703cd314..1fbf24d5e 100644 --- a/cmd/podman/play/kube.go +++ b/cmd/podman/play/kube.go @@ -9,6 +9,7 @@ import ( "github.com/containers/libpod/cmd/podman/registry" "github.com/containers/libpod/cmd/podman/utils" "github.com/containers/libpod/pkg/domain/entities" + "github.com/containers/libpod/pkg/util" "github.com/pkg/errors" "github.com/spf13/cobra" ) @@ -18,7 +19,8 @@ import ( type playKubeOptionsWrapper struct { entities.PlayKubeOptions - TLSVerifyCLI bool + TLSVerifyCLI bool + CredentialsCLI string } var ( @@ -49,7 +51,7 @@ func init() { flags := kubeCmd.Flags() flags.SetNormalizeFunc(utils.AliasFlags) - flags.StringVar(&kubeOptions.Credentials, "creds", "", "`Credentials` (USERNAME:PASSWORD) to use for authenticating to a registry") + flags.StringVar(&kubeOptions.CredentialsCLI, "creds", "", "`Credentials` (USERNAME:PASSWORD) to use for authenticating to a registry") flags.StringVar(&kubeOptions.Network, "network", "", "Connect pod to CNI network(s)") flags.BoolVarP(&kubeOptions.Quiet, "quiet", "q", false, "Suppress output information when pulling images") if !registry.IsRemote() { @@ -76,6 +78,14 @@ func kube(cmd *cobra.Command, args []string) error { return errors.Wrapf(err, "error getting authfile %s", kubeOptions.Authfile) } } + if kubeOptions.CredentialsCLI != "" { + creds, err := util.ParseRegistryCreds(kubeOptions.CredentialsCLI) + if err != nil { + return err + } + kubeOptions.Username = creds.Username + kubeOptions.Password = creds.Password + } report, err := registry.ContainerEngine().PlayKube(registry.GetContext(), args[0], kubeOptions.PlayKubeOptions) if err != nil { diff --git a/cmd/podman/root.go b/cmd/podman/root.go index dffd9b534..3796b8e27 100644 --- a/cmd/podman/root.go +++ b/cmd/podman/root.go @@ -206,6 +206,7 @@ func rootFlags(opts *entities.PodmanConfig, flags *pflag.FlagSet) { flags.IntVar(&opts.MaxWorks, "max-workers", 0, "The maximum number of workers for parallel operations") flags.StringVar(&cfg.Engine.Namespace, "namespace", cfg.Engine.Namespace, "Set the libpod namespace, used to create separate views of the containers and pods on the system") flags.StringVar(&cfg.Engine.StaticDir, "root", "", "Path to the root directory in which data, including images, is stored") + flags.StringVar(&opts.RegistriesConf, "registries-conf", "", "Path to a registries.conf to use for image processing") flags.StringVar(&opts.Runroot, "runroot", "", "Path to the 'run directory' where all state information is stored") flags.StringVar(&opts.RuntimePath, "runtime", "", "Path to the OCI-compatible binary used to run containers, default is /usr/bin/runc") // -s is deprecated due to conflict with -s on subcommands @@ -225,6 +226,7 @@ func rootFlags(opts *entities.PodmanConfig, flags *pflag.FlagSet) { "cpu-profile", "default-mounts-file", "max-workers", + "registries-conf", "trace", } { if err := flags.MarkHidden(f); err != nil { diff --git a/contrib/cirrus/integration_test.sh b/contrib/cirrus/integration_test.sh index 0f2b2ab7e..c92f123fd 100755 --- a/contrib/cirrus/integration_test.sh +++ b/contrib/cirrus/integration_test.sh @@ -45,6 +45,7 @@ case "$SPECIALMODE" in bindings) make make install PREFIX=/usr ETCDIR=/etc + export PATH=$PATH:`pwd`/hack cd pkg/bindings/test && ginkgo -trace -noColor -debug -r ;; none) @@ -13,7 +13,7 @@ require ( github.com/containers/buildah v1.14.9-0.20200523094741-de0f541d9224 github.com/containers/common v0.12.0 github.com/containers/conmon v2.0.16+incompatible - github.com/containers/image/v5 v5.4.4 + github.com/containers/image/v5 v5.4.5-0.20200529084758-46b2ee6aebb0 github.com/containers/psgo v1.5.0 github.com/containers/storage v1.20.1 github.com/coreos/go-systemd/v22 v22.0.0 @@ -74,8 +74,9 @@ github.com/containers/common v0.12.0/go.mod h1:PKlahPDnQQYcXuIw5qq8mq6yNuCHBtgAB github.com/containers/conmon v2.0.16+incompatible h1:QFOlb9Id4WoJ24BelCFWwDSPTquwKMp3L3g2iGmRTq4= github.com/containers/conmon v2.0.16+incompatible/go.mod h1:hgwZ2mtuDrppv78a/cOBNiCm6O0UMWGx1mu7P00nu5I= github.com/containers/image/v5 v5.4.3/go.mod h1:pN0tvp3YbDd7BWavK2aE0mvJUqVd2HmhPjekyWSFm0U= -github.com/containers/image/v5 v5.4.4 h1:JSanNn3v/BMd3o0MEvO4R4OKNuoJUSzVGQAI1+0FMXE= github.com/containers/image/v5 v5.4.4/go.mod h1:g7cxNXitiLi6pEr9/L9n/0wfazRuhDKXU15kV86N8h8= +github.com/containers/image/v5 v5.4.5-0.20200529084758-46b2ee6aebb0 h1:K1ez+qAi9hCMHv/akPF4ddZumQTq/PBGf2Nzc7e+7lI= +github.com/containers/image/v5 v5.4.5-0.20200529084758-46b2ee6aebb0/go.mod h1:XRf/UlTCkDBQONudBhSFXiLCouFKPU/oXwIjWw/tPpo= 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.2 h1:Q0/IPs8ohfbXNxEfyJ2pFVmvJu5BhqJUAmc6ES9NKbo= @@ -253,7 +254,8 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.10.5 h1:7q6vHIqubShURwQz8cQK6yIe/xC3IF0Vm7TGfqjewrc= github.com/klauspost/compress v1.10.5/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/pgzip v1.2.3 h1:Ce2to9wvs/cuJ2b86/CKQoTYr9VHfpanYosZ0UBJqdw= +github.com/klauspost/compress v1.10.6 h1:SP6zavvTG3YjOosWePXFDlExpKIWMTO4SE/Y8MZB2vI= +github.com/klauspost/compress v1.10.6/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/pgzip v1.2.3/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/klauspost/pgzip v1.2.4 h1:TQ7CNpYKovDOmqzRHKxJh0BeaBI7UdQZYc6p7pMQh1A= github.com/klauspost/pgzip v1.2.4/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= @@ -268,6 +270,10 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= github.com/mattn/go-shellwords v1.0.10 h1:Y7Xqm8piKOO3v10Thp7Z36h4FYFjt5xB//6XvOrs2Gw= github.com/mattn/go-shellwords v1.0.10/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= @@ -319,7 +325,6 @@ github.com/onsi/gomega v1.10.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGco github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= @@ -332,7 +337,6 @@ github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59P github.com/opencontainers/runc v1.0.0-rc9 h1:/k06BMULKF5hidyoZymkoDCzdJzltZpz/UU4LguQVtc= github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v0.1.2-0.20190618234442-a950415649c7 h1:Dliu5QO+4JYWu/yMshaMU7G3JN2POGpwjJN7gjy10Go= github.com/opencontainers/runtime-spec v0.1.2-0.20190618234442-a950415649c7/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.3-0.20200520003142-237cc4f519e2 h1:9mv9SC7GWmRWE0J/+oD8w3GsN2KYGKtg6uwLN7hfP5E= github.com/opencontainers/runtime-spec v1.0.3-0.20200520003142-237cc4f519e2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= @@ -452,8 +456,9 @@ github.com/varlink/go v0.0.0-20190502142041-0f1d566d194b/go.mod h1:YHaw8N660ESgM github.com/vbatts/tar-split v0.11.1 h1:0Odu65rhcZ3JZaPHxl7tCI3V/C/Q9Zf82UFravl02dE= github.com/vbatts/tar-split v0.11.1/go.mod h1:LEuURwDEiWjRjwu46yU3KVGuUdVv/dcnpcEPSzR8z6g= github.com/vbauerster/mpb/v5 v5.0.3/go.mod h1:h3YxU5CSr8rZP4Q3xZPVB3jJLhWPou63lHEdr9ytH4Y= -github.com/vbauerster/mpb/v5 v5.0.4 h1:w7l/tJfHmtIOKZkU+bhbDZOUxj1kln9jy4DUOp3Tl14= github.com/vbauerster/mpb/v5 v5.0.4/go.mod h1:fvzasBUyuo35UyuA6sSOlVhpLoNQsp2nBdHw7OiSUU8= +github.com/vbauerster/mpb/v5 v5.2.2 h1:zIICVOm+XD+uV6crpSORaL6I0Q1WqOdvxZTp+r3L9cw= +github.com/vbauerster/mpb/v5 v5.2.2/go.mod h1:W5Fvgw4dm3/0NhqzV8j6EacfuTe5SvnzBRwiXxDR9ww= github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= @@ -549,6 +554,7 @@ golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191127021746-63cb32ae39b2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/libpod/image/docker_registry_options.go b/libpod/image/docker_registry_options.go index 01b5558af..081e7ef4f 100644 --- a/libpod/image/docker_registry_options.go +++ b/libpod/image/docker_registry_options.go @@ -30,6 +30,8 @@ type DockerRegistryOptions struct { OSChoice string // If not "", overrides the use of platform.GOARCH when choosing an image or verifying architecture match. ArchitectureChoice string + // RegistriesConfPath can be used to override the default path of registries.conf. + RegistriesConfPath string } // GetSystemContext constructs a new system context from a parent context. the values in the DockerRegistryOptions, and other parameters. diff --git a/libpod/options.go b/libpod/options.go index ff5e29335..8e0d3df86 100644 --- a/libpod/options.go +++ b/libpod/options.go @@ -8,6 +8,7 @@ import ( "github.com/containers/common/pkg/config" "github.com/containers/image/v5/manifest" + "github.com/containers/image/v5/types" "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/libpod/events" "github.com/containers/libpod/pkg/namespaces" @@ -17,6 +18,7 @@ import ( "github.com/containers/storage/pkg/idtools" "github.com/cri-o/ocicni/pkg/ocicni" "github.com/pkg/errors" + "github.com/sirupsen/logrus" ) // Runtime Creation Options @@ -244,6 +246,22 @@ func WithStaticDir(dir string) RuntimeOption { } } +// WithRegistriesConf configures the runtime to always use specified +// registries.conf for image processing. +func WithRegistriesConf(path string) RuntimeOption { + logrus.Debugf("Setting custom registries.conf: %q", path) + return func(rt *Runtime) error { + if _, err := os.Stat(path); err != nil { + return errors.Wrap(err, "error locating specified registries.conf") + } + if rt.imageContext == nil { + rt.imageContext = &types.SystemContext{} + } + rt.imageContext.SystemRegistriesConfPath = path + return nil + } +} + // WithHooksDir sets the directories to look for OCI runtime hook configuration. func WithHooksDir(hooksDirs ...string) RuntimeOption { return func(rt *Runtime) error { diff --git a/libpod/runtime.go b/libpod/runtime.go index c1864683f..4744de1a2 100644 --- a/libpod/runtime.go +++ b/libpod/runtime.go @@ -339,9 +339,10 @@ func makeRuntime(ctx context.Context, runtime *Runtime) (err error) { } // Set up containers/image - runtime.imageContext = &types.SystemContext{ - SignaturePolicyPath: runtime.config.Engine.SignaturePolicyPath, + if runtime.imageContext == nil { + runtime.imageContext = &types.SystemContext{} } + runtime.imageContext.SignaturePolicyPath = runtime.config.Engine.SignaturePolicyPath // Create the tmpDir if err := os.MkdirAll(runtime.config.Engine.TmpDir, 0751); err != nil { diff --git a/pkg/api/handlers/compat/images.go b/pkg/api/handlers/compat/images.go index ea9cbd691..b64ed0036 100644 --- a/pkg/api/handlers/compat/images.go +++ b/pkg/api/handlers/compat/images.go @@ -15,6 +15,7 @@ import ( image2 "github.com/containers/libpod/libpod/image" "github.com/containers/libpod/pkg/api/handlers" "github.com/containers/libpod/pkg/api/handlers/utils" + "github.com/containers/libpod/pkg/auth" "github.com/containers/libpod/pkg/domain/entities" "github.com/containers/libpod/pkg/util" "github.com/docker/docker/api/types" @@ -251,19 +252,32 @@ func CreateImageFromImage(w http.ResponseWriter, r *http.Request) { return } - /* - fromImage – Name of the image to pull. The name may include a tag or digest. This parameter may only be used when pulling an image. The pull is cancelled if the HTTP connection is closed. - repo – Repository name given to an image when it is imported. The repo may include a tag. This parameter may only be used when importing an image. - tag – Tag or digest. If empty when pulling an image, this causes all tags for the given image to be pulled. - */ fromImage := query.FromImage if len(query.Tag) >= 1 { fromImage = fmt.Sprintf("%s:%s", fromImage, query.Tag) } - // TODO - // We are eating the output right now because we haven't talked about how to deal with multiple responses yet - img, err := runtime.ImageRuntime().New(r.Context(), fromImage, "", "", nil, &image2.DockerRegistryOptions{}, image2.SigningOptions{}, nil, util.PullImageMissing) + authConf, authfile, err := auth.GetCredentials(r) + if err != nil { + utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "Failed to parse %q header for %s", auth.XRegistryAuthHeader, r.URL.String())) + return + } + defer auth.RemoveAuthfile(authfile) + + registryOpts := image2.DockerRegistryOptions{DockerRegistryCreds: authConf} + if sys := runtime.SystemContext(); sys != nil { + registryOpts.DockerCertPath = sys.DockerCertPath + } + img, err := runtime.ImageRuntime().New(r.Context(), + fromImage, + "", // signature policy + authfile, + nil, // writer + ®istryOpts, + image2.SigningOptions{}, + nil, // label + util.PullImageMissing, + ) if err != nil { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err) return diff --git a/pkg/api/handlers/compat/images_push.go b/pkg/api/handlers/compat/images_push.go index 2260d5557..47976b7c9 100644 --- a/pkg/api/handlers/compat/images_push.go +++ b/pkg/api/handlers/compat/images_push.go @@ -9,6 +9,7 @@ import ( "github.com/containers/libpod/libpod" "github.com/containers/libpod/libpod/image" "github.com/containers/libpod/pkg/api/handlers/utils" + "github.com/containers/libpod/pkg/auth" "github.com/gorilla/schema" "github.com/pkg/errors" ) @@ -48,13 +49,17 @@ func PushImage(w http.ResponseWriter, r *http.Request) { return } - // TODO: the X-Registry-Auth header is not checked yet here nor in any other - // endpoint. Pushing does NOT work with authentication at the moment. - dockerRegistryOptions := &image.DockerRegistryOptions{} - authfile := "" + authConf, authfile, err := auth.GetCredentials(r) + if err != nil { + utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "Failed to parse %q header for %s", auth.XRegistryAuthHeader, r.URL.String())) + return + } + defer auth.RemoveAuthfile(authfile) + + dockerRegistryOptions := &image.DockerRegistryOptions{DockerRegistryCreds: authConf} if sys := runtime.SystemContext(); sys != nil { dockerRegistryOptions.DockerCertPath = sys.DockerCertPath - authfile = sys.AuthFilePath + dockerRegistryOptions.RegistriesConfPath = sys.SystemRegistriesConfPath } err = newImage.PushImageToHeuristicDestination( diff --git a/pkg/api/handlers/libpod/images.go b/pkg/api/handlers/libpod/images.go index 1cbcfb52c..4b277d39c 100644 --- a/pkg/api/handlers/libpod/images.go +++ b/pkg/api/handlers/libpod/images.go @@ -21,6 +21,7 @@ import ( image2 "github.com/containers/libpod/libpod/image" "github.com/containers/libpod/pkg/api/handlers" "github.com/containers/libpod/pkg/api/handlers/utils" + "github.com/containers/libpod/pkg/auth" "github.com/containers/libpod/pkg/domain/entities" "github.com/containers/libpod/pkg/domain/infra/abi" "github.com/containers/libpod/pkg/errorhandling" @@ -28,6 +29,7 @@ import ( utils2 "github.com/containers/libpod/utils" "github.com/gorilla/schema" "github.com/pkg/errors" + "github.com/sirupsen/logrus" ) // Commit @@ -339,7 +341,6 @@ func ImagesPull(w http.ResponseWriter, r *http.Request) { decoder := r.Context().Value("decoder").(*schema.Decoder) query := struct { Reference string `schema:"reference"` - Credentials string `schema:"credentials"` OverrideOS string `schema:"overrideOS"` OverrideArch string `schema:"overrideArch"` TLSVerify bool `schema:"tlsVerify"` @@ -382,20 +383,16 @@ func ImagesPull(w http.ResponseWriter, r *http.Request) { return } - var registryCreds *types.DockerAuthConfig - if len(query.Credentials) != 0 { - creds, err := util.ParseRegistryCreds(query.Credentials) - if err != nil { - utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, - errors.Wrapf(err, "error parsing credentials %q", query.Credentials)) - return - } - registryCreds = creds + authConf, authfile, err := auth.GetCredentials(r) + if err != nil { + utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "Failed to parse %q header for %s", auth.XRegistryAuthHeader, r.URL.String())) + return } + defer auth.RemoveAuthfile(authfile) // Setup the registry options dockerRegistryOptions := image.DockerRegistryOptions{ - DockerRegistryCreds: registryCreds, + DockerRegistryCreds: authConf, OSChoice: query.OverrideOS, ArchitectureChoice: query.OverrideArch, } @@ -403,6 +400,13 @@ func ImagesPull(w http.ResponseWriter, r *http.Request) { dockerRegistryOptions.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!query.TLSVerify) } + sys := runtime.SystemContext() + if sys == nil { + sys = image.GetSystemContext("", authfile, false) + } + dockerRegistryOptions.DockerCertPath = sys.DockerCertPath + sys.DockerAuthConfig = authConf + // Prepare the images we want to pull imagesToPull := []string{} res := []handlers.LibpodImagesPullReport{} @@ -411,8 +415,7 @@ func ImagesPull(w http.ResponseWriter, r *http.Request) { if !query.AllTags { imagesToPull = append(imagesToPull, imageName) } else { - systemContext := image.GetSystemContext("", "", false) - tags, err := docker.GetRepositoryTags(context.Background(), systemContext, imageRef) + tags, err := docker.GetRepositoryTags(context.Background(), sys, imageRef) if err != nil { utils.InternalServerError(w, errors.Wrap(err, "error getting repository tags")) return @@ -422,12 +425,6 @@ func ImagesPull(w http.ResponseWriter, r *http.Request) { } } - authfile := "" - if sys := runtime.SystemContext(); sys != nil { - dockerRegistryOptions.DockerCertPath = sys.DockerCertPath - authfile = sys.AuthFilePath - } - // Finally pull the images for _, img := range imagesToPull { newImage, err := runtime.ImageRuntime().New( @@ -456,7 +453,6 @@ func PushImage(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value("runtime").(*libpod.Runtime) query := struct { - Credentials string `schema:"credentials"` Destination string `schema:"destination"` TLSVerify bool `schema:"tlsVerify"` }{ @@ -492,26 +488,20 @@ func PushImage(w http.ResponseWriter, r *http.Request) { return } - var registryCreds *types.DockerAuthConfig - if len(query.Credentials) != 0 { - creds, err := util.ParseRegistryCreds(query.Credentials) - if err != nil { - utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, - errors.Wrapf(err, "error parsing credentials %q", query.Credentials)) - return - } - registryCreds = creds + authConf, authfile, err := auth.GetCredentials(r) + if err != nil { + utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "Failed to parse %q header for %s", auth.XRegistryAuthHeader, r.URL.String())) + return } + defer auth.RemoveAuthfile(authfile) + logrus.Errorf("AuthConf: %v", authConf) - // TODO: the X-Registry-Auth header is not checked yet here nor in any other - // endpoint. Pushing does NOT work with authentication at the moment. dockerRegistryOptions := &image.DockerRegistryOptions{ - DockerRegistryCreds: registryCreds, + DockerRegistryCreds: authConf, } - authfile := "" if sys := runtime.SystemContext(); sys != nil { dockerRegistryOptions.DockerCertPath = sys.DockerCertPath - authfile = sys.AuthFilePath + dockerRegistryOptions.RegistriesConfPath = sys.SystemRegistriesConfPath } if _, found := r.URL.Query()["tlsVerify"]; found { dockerRegistryOptions.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!query.TLSVerify) diff --git a/pkg/api/handlers/libpod/manifests.go b/pkg/api/handlers/libpod/manifests.go index 93ca367f7..aef92368b 100644 --- a/pkg/api/handlers/libpod/manifests.go +++ b/pkg/api/handlers/libpod/manifests.go @@ -120,6 +120,10 @@ func ManifestRemove(w http.ResponseWriter, r *http.Request) { utils.WriteResponse(w, http.StatusOK, handlers.IDResponse{ID: newID}) } func ManifestPush(w http.ResponseWriter, r *http.Request) { + // FIXME: parameters are missing (tlsVerify, format). + // Also, we should use the ABI function to avoid duplicate code. + // Also, support for XRegistryAuth headers are missing. + runtime := r.Context().Value("runtime").(*libpod.Runtime) decoder := r.Context().Value("decoder").(*schema.Decoder) query := struct { diff --git a/pkg/api/handlers/libpod/play.go b/pkg/api/handlers/libpod/play.go index 26e02bf4f..1cb5cdb6c 100644 --- a/pkg/api/handlers/libpod/play.go +++ b/pkg/api/handlers/libpod/play.go @@ -9,6 +9,7 @@ import ( "github.com/containers/image/v5/types" "github.com/containers/libpod/libpod" "github.com/containers/libpod/pkg/api/handlers/utils" + "github.com/containers/libpod/pkg/auth" "github.com/containers/libpod/pkg/domain/entities" "github.com/containers/libpod/pkg/domain/infra/abi" "github.com/gorilla/schema" @@ -47,9 +48,26 @@ func PlayKube(w http.ResponseWriter, r *http.Request) { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "error closing temporary file")) return } + authConf, authfile, err := auth.GetCredentials(r) + if err != nil { + utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "Failed to parse %q header for %s", auth.XRegistryAuthHeader, r.URL.String())) + return + } + defer auth.RemoveAuthfile(authfile) + var username, password string + if authConf != nil { + username = authConf.Username + password = authConf.Password + } containerEngine := abi.ContainerEngine{Libpod: runtime} - options := entities.PlayKubeOptions{Network: query.Network, Quiet: true} + options := entities.PlayKubeOptions{ + Authfile: authfile, + Username: username, + Password: password, + Network: query.Network, + Quiet: true, + } if _, found := r.URL.Query()["tlsVerify"]; found { options.SkipTLSVerify = types.NewOptionalBool(!query.TLSVerify) } diff --git a/pkg/api/server/register_images.go b/pkg/api/server/register_images.go index 64094e8e4..83584d0f3 100644 --- a/pkg/api/server/register_images.go +++ b/pkg/api/server/register_images.go @@ -8,6 +8,10 @@ import ( "github.com/gorilla/mux" ) +// TODO +// +// * /images/create is missing the "message" and "platform" parameters + func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // swagger:operation POST /images/create compat createImage // --- @@ -635,10 +639,6 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // type: string // description: Allows for pushing the image to a different destintation than the image refers to. // - in: query - // name: credentials - // description: username:password for the registry. - // type: string - // - in: query // name: tlsVerify // description: Require TLS verification. // type: boolean diff --git a/pkg/api/server/swagger.go b/pkg/api/server/swagger.go index ebd99ba27..c463f809e 100644 --- a/pkg/api/server/swagger.go +++ b/pkg/api/server/swagger.go @@ -139,6 +139,13 @@ type swagImageSummary struct { Body []entities.ImageSummary } +// Registries summary +// swagger:response DocsRegistriesList +type swagRegistriesList struct { + // in:body + Body entities.ListRegistriesReport +} + // List Containers // swagger:response DocsListContainer type swagListContainers struct { diff --git a/pkg/auth/auth.go b/pkg/auth/auth.go new file mode 100644 index 000000000..ffa65f7e5 --- /dev/null +++ b/pkg/auth/auth.go @@ -0,0 +1,216 @@ +package auth + +import ( + "encoding/base64" + "encoding/json" + "io/ioutil" + "net/http" + "os" + "strings" + + imageAuth "github.com/containers/image/v5/pkg/docker/config" + "github.com/containers/image/v5/types" + dockerAPITypes "github.com/docker/docker/api/types" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +// XRegistryAuthHeader is the key to the encoded registry authentication +// configuration in an http-request header. +const XRegistryAuthHeader = "X-Registry-Auth" + +// GetCredentials extracts one or more DockerAuthConfigs from the request's +// header. The header could specify a single-auth config in which case the +// first return value is set. In case of a multi-auth header, the contents are +// stored in a temporary auth file (2nd return value). Note that the auth file +// should be removed after usage. +func GetCredentials(r *http.Request) (*types.DockerAuthConfig, string, error) { + authHeader := r.Header.Get(XRegistryAuthHeader) + if len(authHeader) == 0 { + return nil, "", nil + } + + // First look for a multi-auth header (i.e., a map). + authConfigs, err := multiAuthHeader(r) + if err == nil { + authfile, err := authConfigsToAuthFile(authConfigs) + return nil, authfile, err + } + + // Fallback to looking for a single-auth header (i.e., one config). + authConfigs, err = singleAuthHeader(r) + if err != nil { + return nil, "", err + } + var conf *types.DockerAuthConfig + for k := range authConfigs { + c := authConfigs[k] + conf = &c + break + } + return conf, "", nil +} + +// Header returns a map with the XRegistryAuthHeader set which can +// conveniently be used in the http stack. +func Header(sys *types.SystemContext, authfile, username, password string) (map[string]string, error) { + var content string + var err error + + if username != "" { + content, err = encodeSingleAuthConfig(types.DockerAuthConfig{Username: username, Password: password}) + if err != nil { + return nil, err + } + } else { + if sys == nil { + sys = &types.SystemContext{} + } + if authfile != "" { + sys.AuthFilePath = authfile + } + authConfigs, err := imageAuth.GetAllCredentials(sys) + if err != nil { + return nil, err + } + content, err = encodeMultiAuthConfigs(authConfigs) + if err != nil { + return nil, err + } + } + + header := make(map[string]string) + header[XRegistryAuthHeader] = content + + return header, nil +} + +// RemoveAuthfile is a convenience function that is meant to be called in a +// deferred statement. If non-empty, it removes the specified authfile and log +// errors. It's meant to reduce boilerplate code at call sites of +// `GetCredentials`. +func RemoveAuthfile(authfile string) { + if authfile == "" { + return + } + if err := os.Remove(authfile); err != nil { + logrus.Errorf("Error removing temporary auth file %q: %v", authfile, err) + } +} + +// encodeSingleAuthConfig serializes the auth configuration as a base64 encoded JSON payload. +func encodeSingleAuthConfig(authConfig types.DockerAuthConfig) (string, error) { + conf := imageAuthToDockerAuth(authConfig) + buf, err := json.Marshal(conf) + if err != nil { + return "", err + } + return base64.URLEncoding.EncodeToString(buf), nil +} + +// encodeMultiAuthConfigs serializes the auth configurations as a base64 encoded JSON payload. +func encodeMultiAuthConfigs(authConfigs map[string]types.DockerAuthConfig) (string, error) { + confs := make(map[string]dockerAPITypes.AuthConfig) + for registry, authConf := range authConfigs { + confs[registry] = imageAuthToDockerAuth(authConf) + } + buf, err := json.Marshal(confs) + if err != nil { + return "", err + } + return base64.URLEncoding.EncodeToString(buf), nil +} + +// authConfigsToAuthFile stores the specified auth configs in a temporary files +// and returns its path. The file can later be used an auth file for contacting +// one or more container registries. If tmpDir is empty, the system's default +// TMPDIR will be used. +func authConfigsToAuthFile(authConfigs map[string]types.DockerAuthConfig) (string, error) { + // Intitialize an empty temporary JSON file. + tmpFile, err := ioutil.TempFile("", "auth.json.") + if err != nil { + return "", err + } + if _, err := tmpFile.Write([]byte{'{', '}'}); err != nil { + return "", errors.Wrap(err, "error initializing temporary auth file") + } + if err := tmpFile.Close(); err != nil { + return "", errors.Wrap(err, "error closing temporary auth file") + } + authFilePath := tmpFile.Name() + + // TODO: It would be nice if c/image could dump the map at once. + // + // Now use the c/image packages to store the credentials. It's battle + // tested, and we make sure to use the same code as the image backend. + sys := types.SystemContext{AuthFilePath: authFilePath} + for server, config := range authConfigs { + // Note that we do not validate the credentials here. Wassume + // that all credentials are valid. They'll be used on demand + // later. + if err := imageAuth.SetAuthentication(&sys, server, config.Username, config.Password); err != nil { + return "", errors.Wrapf(err, "error storing credentials in temporary auth file (server: %q, user: %q)", server, config.Username) + } + } + + return authFilePath, nil +} + +// dockerAuthToImageAuth converts a docker auth config to one we're using +// internally from c/image. Note that the Docker types look slightly +// different, so we need to convert to be extra sure we're not running into +// undesired side-effects when unmarhalling directly to our types. +func dockerAuthToImageAuth(authConfig dockerAPITypes.AuthConfig) types.DockerAuthConfig { + return types.DockerAuthConfig{ + Username: authConfig.Username, + Password: authConfig.Password, + IdentityToken: authConfig.IdentityToken, + } +} + +// reverse conversion of `dockerAuthToImageAuth`. +func imageAuthToDockerAuth(authConfig types.DockerAuthConfig) dockerAPITypes.AuthConfig { + return dockerAPITypes.AuthConfig{ + Username: authConfig.Username, + Password: authConfig.Password, + IdentityToken: authConfig.IdentityToken, + } +} + +// singleAuthHeader extracts a DockerAuthConfig from the request's header. +// The header content is a single DockerAuthConfig. +func singleAuthHeader(r *http.Request) (map[string]types.DockerAuthConfig, error) { + authHeader := r.Header.Get(XRegistryAuthHeader) + authConfig := dockerAPITypes.AuthConfig{} + if len(authHeader) > 0 { + authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authHeader)) + if err := json.NewDecoder(authJSON).Decode(&authConfig); err != nil { + return nil, err + } + } + authConfigs := make(map[string]types.DockerAuthConfig) + authConfigs["0"] = dockerAuthToImageAuth(authConfig) + return authConfigs, nil +} + +// multiAuthHeader extracts a DockerAuthConfig from the request's header. +// The header content is a map[string]DockerAuthConfigs. +func multiAuthHeader(r *http.Request) (map[string]types.DockerAuthConfig, error) { + authHeader := r.Header.Get(XRegistryAuthHeader) + if len(authHeader) == 0 { + return nil, nil + } + + dockerAuthConfigs := make(map[string]dockerAPITypes.AuthConfig) + authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authHeader)) + if err := json.NewDecoder(authJSON).Decode(&dockerAuthConfigs); err != nil { + return nil, err + } + + // Now convert to the internal types. + authConfigs := make(map[string]types.DockerAuthConfig) + for server := range dockerAuthConfigs { + authConfigs[server] = dockerAuthToImageAuth(dockerAuthConfigs[server]) + } + return authConfigs, nil +} diff --git a/pkg/bindings/connection.go b/pkg/bindings/connection.go index d30698c1d..e9032f083 100644 --- a/pkg/bindings/connection.go +++ b/pkg/bindings/connection.go @@ -150,7 +150,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) + response, err := client.DoRequest(nil, http.MethodGet, "../../../_ping", nil, nil) if err != nil { return err } @@ -250,7 +250,7 @@ func unixClient(_url *url.URL) (Connection, error) { } // DoRequest assembles the http request and returns the response -func (c *Connection) DoRequest(httpBody io.Reader, httpMethod, endpoint string, queryParams url.Values, pathValues ...string) (*APIResponse, error) { +func (c *Connection) DoRequest(httpBody io.Reader, httpMethod, endpoint string, queryParams url.Values, header map[string]string, pathValues ...string) (*APIResponse, error) { var ( err error response *http.Response @@ -271,6 +271,9 @@ func (c *Connection) DoRequest(httpBody io.Reader, httpMethod, endpoint string, if len(queryParams) > 0 { req.URL.RawQuery = queryParams.Encode() } + for key, val := range header { + req.Header.Set(key, val) + } req = req.WithContext(context.WithValue(context.Background(), clientKey, c)) // Give the Do three chances in the case of a comm/service hiccup for i := 0; i < 3; i++ { diff --git a/pkg/bindings/containers/checkpoint.go b/pkg/bindings/containers/checkpoint.go index 84924587b..f483a9297 100644 --- a/pkg/bindings/containers/checkpoint.go +++ b/pkg/bindings/containers/checkpoint.go @@ -34,7 +34,7 @@ func Checkpoint(ctx context.Context, nameOrId string, keep, leaveRunning, tcpEst if export != nil { params.Set("export", *export) } - response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/checkpoint", params, nameOrId) + response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/checkpoint", params, nil, nameOrId) if err != nil { return nil, err } @@ -71,7 +71,7 @@ func Restore(ctx context.Context, nameOrId string, keep, tcpEstablished, ignoreR if importArchive != nil { params.Set("import", *importArchive) } - response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/restore", params, nameOrId) + response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/restore", params, nil, nameOrId) if err != nil { return nil, err } diff --git a/pkg/bindings/containers/commit.go b/pkg/bindings/containers/commit.go index 12c25f842..780d42272 100644 --- a/pkg/bindings/containers/commit.go +++ b/pkg/bindings/containers/commit.go @@ -41,7 +41,7 @@ func Commit(ctx context.Context, nameOrId string, options CommitOptions) (handle if options.Tag != nil { params.Set("tag", *options.Tag) } - response, err := conn.DoRequest(nil, http.MethodPost, "/commit", params) + response, err := conn.DoRequest(nil, http.MethodPost, "/commit", params, nil) if err != nil { return id, err } diff --git a/pkg/bindings/containers/containers.go b/pkg/bindings/containers/containers.go index 81e213d2b..516f3d282 100644 --- a/pkg/bindings/containers/containers.go +++ b/pkg/bindings/containers/containers.go @@ -61,7 +61,7 @@ func List(ctx context.Context, filters map[string][]string, all *bool, last *int } params.Set("filters", filterString) } - response, err := conn.DoRequest(nil, http.MethodGet, "/containers/json", params) + response, err := conn.DoRequest(nil, http.MethodGet, "/containers/json", params, nil) if err != nil { return containers, err } @@ -86,7 +86,7 @@ func Prune(ctx context.Context, filters map[string][]string) (*entities.Containe } params.Set("filters", filterString) } - response, err := conn.DoRequest(nil, http.MethodPost, "/containers/prune", params) + response, err := conn.DoRequest(nil, http.MethodPost, "/containers/prune", params, nil) if err != nil { return nil, err } @@ -108,7 +108,7 @@ func Remove(ctx context.Context, nameOrID string, force, volumes *bool) error { if volumes != nil { params.Set("vols", strconv.FormatBool(*volumes)) } - response, err := conn.DoRequest(nil, http.MethodDelete, "/containers/%s", params, nameOrID) + response, err := conn.DoRequest(nil, http.MethodDelete, "/containers/%s", params, nil, nameOrID) if err != nil { return err } @@ -128,7 +128,7 @@ func Inspect(ctx context.Context, nameOrID string, size *bool) (*define.InspectC if size != nil { params.Set("size", strconv.FormatBool(*size)) } - response, err := conn.DoRequest(nil, http.MethodGet, "/containers/%s/json", params, nameOrID) + response, err := conn.DoRequest(nil, http.MethodGet, "/containers/%s/json", params, nil, nameOrID) if err != nil { return nil, err } @@ -146,7 +146,7 @@ func Kill(ctx context.Context, nameOrID string, sig string) error { } params := url.Values{} params.Set("signal", sig) - response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/kill", params, nameOrID) + response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/kill", params, nil, nameOrID) if err != nil { return err } @@ -161,7 +161,7 @@ func Pause(ctx context.Context, nameOrID string) error { if err != nil { return err } - response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/pause", nil, nameOrID) + response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/pause", nil, nil, nameOrID) if err != nil { return err } @@ -180,7 +180,7 @@ func Restart(ctx context.Context, nameOrID string, timeout *int) error { if timeout != nil { params.Set("t", strconv.Itoa(*timeout)) } - response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/restart", params, nameOrID) + response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/restart", params, nil, nameOrID) if err != nil { return err } @@ -199,7 +199,7 @@ func Start(ctx context.Context, nameOrID string, detachKeys *string) error { if detachKeys != nil { params.Set("detachKeys", *detachKeys) } - response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/start", params, nameOrID) + response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/start", params, nil, nameOrID) if err != nil { return err } @@ -221,7 +221,7 @@ func Top(ctx context.Context, nameOrID string, descriptors []string) ([]string, // flatten the slice into one string params.Set("ps_args", strings.Join(descriptors, ",")) } - response, err := conn.DoRequest(nil, http.MethodGet, "/containers/%s/top", params, nameOrID) + response, err := conn.DoRequest(nil, http.MethodGet, "/containers/%s/top", params, nil, nameOrID) if err != nil { return nil, err } @@ -249,7 +249,7 @@ func Unpause(ctx context.Context, nameOrID string) error { if err != nil { return err } - response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/unpause", nil, nameOrID) + response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/unpause", nil, nil, nameOrID) if err != nil { return err } @@ -269,7 +269,7 @@ func Wait(ctx context.Context, nameOrID string, condition *define.ContainerStatu if condition != nil { params.Set("condition", condition.String()) } - response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/wait", params, nameOrID) + response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/wait", params, nil, nameOrID) if err != nil { return exitCode, err } @@ -284,7 +284,7 @@ func Exists(ctx context.Context, nameOrID string) (bool, error) { if err != nil { return false, err } - response, err := conn.DoRequest(nil, http.MethodGet, "/containers/%s/exists", nil, nameOrID) + response, err := conn.DoRequest(nil, http.MethodGet, "/containers/%s/exists", nil, nil, nameOrID) if err != nil { return false, err } @@ -302,7 +302,7 @@ func Stop(ctx context.Context, nameOrID string, timeout *uint) error { if timeout != nil { params.Set("t", strconv.Itoa(int(*timeout))) } - response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/stop", params, nameOrID) + response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/stop", params, nil, nameOrID) if err != nil { return err } @@ -317,7 +317,7 @@ func Export(ctx context.Context, nameOrID string, w io.Writer) error { if err != nil { return err } - response, err := conn.DoRequest(nil, http.MethodGet, "/containers/%s/export", params, nameOrID) + response, err := conn.DoRequest(nil, http.MethodGet, "/containers/%s/export", params, nil, nameOrID) if err != nil { return err } @@ -336,7 +336,7 @@ func ContainerInit(ctx context.Context, nameOrID string) error { if err != nil { return err } - response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/init", nil, nameOrID) + response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/init", nil, nil, nameOrID) if err != nil { return err } @@ -443,7 +443,7 @@ func Attach(ctx context.Context, nameOrId string, detachKeys *string, logs, stre }() } - response, err := conn.DoRequest(stdin, http.MethodPost, "/containers/%s/attach", params, nameOrId) + response, err := conn.DoRequest(stdin, http.MethodPost, "/containers/%s/attach", params, nil, nameOrId) if err != nil { return err } @@ -572,7 +572,7 @@ func resizeTTY(ctx context.Context, endpoint string, height *int, width *int) er if width != nil { params.Set("w", strconv.Itoa(*width)) } - rsp, err := conn.DoRequest(nil, http.MethodPost, endpoint, params) + rsp, err := conn.DoRequest(nil, http.MethodPost, endpoint, params, nil) if err != nil { return err } diff --git a/pkg/bindings/containers/create.go b/pkg/bindings/containers/create.go index 21355f24b..4603b8653 100644 --- a/pkg/bindings/containers/create.go +++ b/pkg/bindings/containers/create.go @@ -22,7 +22,7 @@ func CreateWithSpec(ctx context.Context, s *specgen.SpecGenerator) (entities.Con return ccr, err } stringReader := strings.NewReader(specgenString) - response, err := conn.DoRequest(stringReader, http.MethodPost, "/containers/create", nil) + response, err := conn.DoRequest(stringReader, http.MethodPost, "/containers/create", nil, nil) if err != nil { return ccr, err } diff --git a/pkg/bindings/containers/diff.go b/pkg/bindings/containers/diff.go index 82070ca9a..06a828c30 100644 --- a/pkg/bindings/containers/diff.go +++ b/pkg/bindings/containers/diff.go @@ -15,7 +15,7 @@ func Diff(ctx context.Context, nameOrId string) ([]archive.Change, error) { return nil, err } - response, err := conn.DoRequest(nil, http.MethodGet, "/containers/%s/changes", nil, nameOrId) + response, err := conn.DoRequest(nil, http.MethodGet, "/containers/%s/changes", nil, nil, nameOrId) if err != nil { return nil, err } diff --git a/pkg/bindings/containers/exec.go b/pkg/bindings/containers/exec.go index 48f9ed697..2aeeae1f8 100644 --- a/pkg/bindings/containers/exec.go +++ b/pkg/bindings/containers/exec.go @@ -34,7 +34,7 @@ func ExecCreate(ctx context.Context, nameOrID string, config *handlers.ExecCreat } jsonReader := strings.NewReader(string(requestJSON)) - resp, err := conn.DoRequest(jsonReader, http.MethodPost, "/containers/%s/exec", nil, nameOrID) + resp, err := conn.DoRequest(jsonReader, http.MethodPost, "/containers/%s/exec", nil, nil, nameOrID) if err != nil { return "", err } @@ -57,7 +57,7 @@ func ExecInspect(ctx context.Context, sessionID string) (*define.InspectExecSess logrus.Debugf("Inspecting session ID %q", sessionID) - resp, err := conn.DoRequest(nil, http.MethodGet, "/exec/%s/json", nil, sessionID) + resp, err := conn.DoRequest(nil, http.MethodGet, "/exec/%s/json", nil, nil, sessionID) if err != nil { return nil, err } diff --git a/pkg/bindings/containers/healthcheck.go b/pkg/bindings/containers/healthcheck.go index 2b783ac73..b726acf49 100644 --- a/pkg/bindings/containers/healthcheck.go +++ b/pkg/bindings/containers/healthcheck.go @@ -18,7 +18,7 @@ func RunHealthCheck(ctx context.Context, nameOrID string) (*define.HealthCheckRe var ( status define.HealthCheckResults ) - response, err := conn.DoRequest(nil, http.MethodGet, "/containers/%s/healthcheck", nil, nameOrID) + response, err := conn.DoRequest(nil, http.MethodGet, "/containers/%s/healthcheck", nil, nil, nameOrID) if err != nil { return nil, err } diff --git a/pkg/bindings/containers/logs.go b/pkg/bindings/containers/logs.go index 7fea30003..bec4ebb3c 100644 --- a/pkg/bindings/containers/logs.go +++ b/pkg/bindings/containers/logs.go @@ -46,7 +46,7 @@ func Logs(ctx context.Context, nameOrID string, opts LogOptions, stdoutChan, std if opts.Stdout == nil && opts.Stderr == nil { params.Set("stdout", strconv.FormatBool(true)) } - response, err := conn.DoRequest(nil, http.MethodGet, "/containers/%s/logs", params, nameOrID) + response, err := conn.DoRequest(nil, http.MethodGet, "/containers/%s/logs", params, nil, nameOrID) if err != nil { return err } diff --git a/pkg/bindings/containers/mount.go b/pkg/bindings/containers/mount.go index e0627d9a3..2d553142f 100644 --- a/pkg/bindings/containers/mount.go +++ b/pkg/bindings/containers/mount.go @@ -17,7 +17,7 @@ func Mount(ctx context.Context, nameOrID string) (string, error) { var ( path string ) - response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/mount", nil, nameOrID) + response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/mount", nil, nil, nameOrID) if err != nil { return path, err } @@ -31,7 +31,7 @@ func Unmount(ctx context.Context, nameOrID string) error { if err != nil { return err } - response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/unmount", nil, nameOrID) + response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/unmount", nil, nil, nameOrID) if err != nil { return err } @@ -45,7 +45,7 @@ func GetMountedContainerPaths(ctx context.Context) (map[string]string, error) { return nil, err } mounts := make(map[string]string) - response, err := conn.DoRequest(nil, http.MethodGet, "/containers/showmounted", nil) + response, err := conn.DoRequest(nil, http.MethodGet, "/containers/showmounted", nil, nil) if err != nil { return mounts, err } diff --git a/pkg/bindings/generate/generate.go b/pkg/bindings/generate/generate.go index d3177133f..161b722f3 100644 --- a/pkg/bindings/generate/generate.go +++ b/pkg/bindings/generate/generate.go @@ -18,7 +18,7 @@ func GenerateKube(ctx context.Context, nameOrID string, options entities.Generat params := url.Values{} params.Set("service", strconv.FormatBool(options.Service)) - response, err := conn.DoRequest(nil, http.MethodGet, "/generate/%s/kube", params, nameOrID) + response, err := conn.DoRequest(nil, http.MethodGet, "/generate/%s/kube", params, nil, nameOrID) if err != nil { return nil, err } diff --git a/pkg/bindings/images/diff.go b/pkg/bindings/images/diff.go index cfdd06a97..e2d344ea0 100644 --- a/pkg/bindings/images/diff.go +++ b/pkg/bindings/images/diff.go @@ -15,7 +15,7 @@ func Diff(ctx context.Context, nameOrId string) ([]archive.Change, error) { return nil, err } - response, err := conn.DoRequest(nil, http.MethodGet, "/images/%s/changes", nil, nameOrId) + response, err := conn.DoRequest(nil, http.MethodGet, "/images/%s/changes", nil, nil, nameOrId) if err != nil { return nil, err } diff --git a/pkg/bindings/images/images.go b/pkg/bindings/images/images.go index f9c02d199..e0802a6e1 100644 --- a/pkg/bindings/images/images.go +++ b/pkg/bindings/images/images.go @@ -12,6 +12,7 @@ import ( "github.com/containers/buildah" "github.com/containers/image/v5/types" "github.com/containers/libpod/pkg/api/handlers" + "github.com/containers/libpod/pkg/auth" "github.com/containers/libpod/pkg/bindings" "github.com/containers/libpod/pkg/domain/entities" "github.com/docker/go-units" @@ -26,7 +27,7 @@ func Exists(ctx context.Context, nameOrID string) (bool, error) { if err != nil { return false, err } - response, err := conn.DoRequest(nil, http.MethodGet, "/images/%s/exists", nil, nameOrID) + response, err := conn.DoRequest(nil, http.MethodGet, "/images/%s/exists", nil, nil, nameOrID) if err != nil { return false, err } @@ -52,7 +53,7 @@ func List(ctx context.Context, all *bool, filters map[string][]string) ([]*entit } params.Set("filters", strFilters) } - response, err := conn.DoRequest(nil, http.MethodGet, "/images/json", params) + response, err := conn.DoRequest(nil, http.MethodGet, "/images/json", params, nil) if err != nil { return imageSummary, err } @@ -71,7 +72,7 @@ func GetImage(ctx context.Context, nameOrID string, size *bool) (*entities.Image params.Set("size", strconv.FormatBool(*size)) } inspectedData := entities.ImageInspectReport{} - response, err := conn.DoRequest(nil, http.MethodGet, "/images/%s/json", params, nameOrID) + response, err := conn.DoRequest(nil, http.MethodGet, "/images/%s/json", params, nil, nameOrID) if err != nil { return &inspectedData, err } @@ -89,7 +90,7 @@ func Tree(ctx context.Context, nameOrId string, whatRequires *bool) (*entities.I if whatRequires != nil { params.Set("size", strconv.FormatBool(*whatRequires)) } - response, err := conn.DoRequest(nil, http.MethodGet, "/images/%s/tree", params, nameOrId) + response, err := conn.DoRequest(nil, http.MethodGet, "/images/%s/tree", params, nil, nameOrId) if err != nil { return nil, err } @@ -103,7 +104,7 @@ func History(ctx context.Context, nameOrID string) ([]*handlers.HistoryResponse, if err != nil { return nil, err } - response, err := conn.DoRequest(nil, http.MethodGet, "/images/%s/history", nil, nameOrID) + response, err := conn.DoRequest(nil, http.MethodGet, "/images/%s/history", nil, nil, nameOrID) if err != nil { return history, err } @@ -120,7 +121,7 @@ func Load(ctx context.Context, r io.Reader, name *string) (*entities.ImageLoadRe if name != nil { params.Set("reference", *name) } - response, err := conn.DoRequest(r, http.MethodPost, "/images/load", params) + response, err := conn.DoRequest(r, http.MethodPost, "/images/load", params, nil) if err != nil { return nil, err } @@ -141,7 +142,7 @@ func Export(ctx context.Context, nameOrID string, w io.Writer, format *string, c if compress != nil { params.Set("compress", strconv.FormatBool(*compress)) } - response, err := conn.DoRequest(nil, http.MethodGet, "/images/%s/get", params, nameOrID) + response, err := conn.DoRequest(nil, http.MethodGet, "/images/%s/get", params, nil, nameOrID) if err != nil { return err } @@ -174,7 +175,7 @@ func Prune(ctx context.Context, all *bool, filters map[string][]string) ([]strin } params.Set("filters", stringFilter) } - response, err := conn.DoRequest(nil, http.MethodPost, "/images/prune", params) + response, err := conn.DoRequest(nil, http.MethodPost, "/images/prune", params, nil) if err != nil { return deleted, err } @@ -190,7 +191,7 @@ func Tag(ctx context.Context, nameOrID, tag, repo string) error { params := url.Values{} params.Set("tag", tag) params.Set("repo", repo) - response, err := conn.DoRequest(nil, http.MethodPost, "/images/%s/tag", params, nameOrID) + response, err := conn.DoRequest(nil, http.MethodPost, "/images/%s/tag", params, nil, nameOrID) if err != nil { return err } @@ -206,7 +207,7 @@ func Untag(ctx context.Context, nameOrID, tag, repo string) error { params := url.Values{} params.Set("tag", tag) params.Set("repo", repo) - response, err := conn.DoRequest(nil, http.MethodPost, "/images/%s/untag", params, nameOrID) + response, err := conn.DoRequest(nil, http.MethodPost, "/images/%s/untag", params, nil, nameOrID) if err != nil { return err } @@ -297,7 +298,7 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO } // TODO outputs? - response, err := conn.DoRequest(tarfile, http.MethodPost, "/build", params) + response, err := conn.DoRequest(tarfile, http.MethodPost, "/build", params, nil) if err != nil { return nil, err } @@ -341,7 +342,7 @@ func Import(ctx context.Context, changes []string, message, reference, u *string if u != nil { params.Set("url", *u) } - response, err := conn.DoRequest(r, http.MethodPost, "/images/import", params) + response, err := conn.DoRequest(r, http.MethodPost, "/images/import", params, nil) if err != nil { return nil, err } @@ -359,7 +360,6 @@ func Pull(ctx context.Context, rawImage string, options entities.ImagePullOption } params := url.Values{} params.Set("reference", rawImage) - params.Set("credentials", options.Credentials) params.Set("overrideArch", options.OverrideArch) params.Set("overrideOS", options.OverrideOS) if options.SkipTLSVerify != types.OptionalBoolUndefined { @@ -369,7 +369,13 @@ func Pull(ctx context.Context, rawImage string, options entities.ImagePullOption } params.Set("allTags", strconv.FormatBool(options.AllTags)) - response, err := conn.DoRequest(nil, http.MethodPost, "/images/pull", params) + // TODO: have a global system context we can pass around (1st argument) + header, err := auth.Header(nil, options.Authfile, options.Username, options.Password) + if err != nil { + return nil, err + } + + response, err := conn.DoRequest(nil, http.MethodPost, "/images/pull", params, header) if err != nil { return nil, err } @@ -397,8 +403,14 @@ func Push(ctx context.Context, source string, destination string, options entiti if err != nil { return err } + + // TODO: have a global system context we can pass around (1st argument) + header, err := auth.Header(nil, options.Authfile, options.Username, options.Password) + if err != nil { + return err + } + params := url.Values{} - params.Set("credentials", options.Credentials) params.Set("destination", destination) if options.SkipTLSVerify != types.OptionalBoolUndefined { // Note: we have to verify if skipped is false. @@ -407,8 +419,12 @@ func Push(ctx context.Context, source string, destination string, options entiti } path := fmt.Sprintf("/images/%s/push", source) - _, err = conn.DoRequest(nil, http.MethodPost, path, params) - return err + response, err := conn.DoRequest(nil, http.MethodPost, path, params, header) + if err != nil { + return err + } + + return response.Process(err) } // Search is the binding for libpod's v2 endpoints for Search images. @@ -430,7 +446,13 @@ func Search(ctx context.Context, term string, opts entities.ImageSearchOptions) params.Set("tlsVerify", strconv.FormatBool(verifyTLS)) } - response, err := conn.DoRequest(nil, http.MethodGet, "/images/search", params) + // TODO: have a global system context we can pass around (1st argument) + header, err := auth.Header(nil, opts.Authfile, "", "") + if err != nil { + return nil, err + } + + response, err := conn.DoRequest(nil, http.MethodGet, "/images/search", params, header) if err != nil { return nil, err } diff --git a/pkg/bindings/images/rm.go b/pkg/bindings/images/rm.go index 05aa3f9ca..c315bfce7 100644 --- a/pkg/bindings/images/rm.go +++ b/pkg/bindings/images/rm.go @@ -30,7 +30,7 @@ func BatchRemove(ctx context.Context, images []string, opts entities.ImageRemove params.Add("images", i) } - response, err := conn.DoRequest(nil, http.MethodDelete, "/images/remove", params) + response, err := conn.DoRequest(nil, http.MethodDelete, "/images/remove", params, nil) if err != nil { return nil, []error{err} } @@ -52,7 +52,7 @@ func Remove(ctx context.Context, nameOrID string, force bool) (*entities.ImageRe params := url.Values{} params.Set("force", strconv.FormatBool(force)) - response, err := conn.DoRequest(nil, http.MethodDelete, "/images/%s", params, nameOrID) + response, err := conn.DoRequest(nil, http.MethodDelete, "/images/%s", params, nil, nameOrID) if err != nil { return nil, err } diff --git a/pkg/bindings/manifests/manifests.go b/pkg/bindings/manifests/manifests.go index f5ee31d93..e89624667 100644 --- a/pkg/bindings/manifests/manifests.go +++ b/pkg/bindings/manifests/manifests.go @@ -39,7 +39,7 @@ func Create(ctx context.Context, names, images []string, all *bool) (string, err params.Add("image", i) } - response, err := conn.DoRequest(nil, http.MethodPost, "/manifests/create", params) + response, err := conn.DoRequest(nil, http.MethodPost, "/manifests/create", params, nil) if err != nil { return "", err } @@ -53,7 +53,7 @@ func Inspect(ctx context.Context, name string) (*manifest.Schema2List, error) { if err != nil { return nil, err } - response, err := conn.DoRequest(nil, http.MethodGet, "/manifests/%s/json", nil, name) + response, err := conn.DoRequest(nil, http.MethodGet, "/manifests/%s/json", nil, nil, name) if err != nil { return nil, err } @@ -73,7 +73,7 @@ func Add(ctx context.Context, name string, options image.ManifestAddOpts) (strin return "", err } stringReader := strings.NewReader(optionsString) - response, err := conn.DoRequest(stringReader, http.MethodPost, "/manifests/%s/add", nil, name) + response, err := conn.DoRequest(stringReader, http.MethodPost, "/manifests/%s/add", nil, nil, name) if err != nil { return "", err } @@ -90,7 +90,7 @@ func Remove(ctx context.Context, name, digest string) (string, error) { } params := url.Values{} params.Set("digest", digest) - response, err := conn.DoRequest(nil, http.MethodDelete, "/manifests/%s", params, name) + response, err := conn.DoRequest(nil, http.MethodDelete, "/manifests/%s", params, nil, name) if err != nil { return "", err } @@ -118,7 +118,7 @@ func Push(ctx context.Context, name string, destination *string, all *bool) (str if all != nil { params.Set("all", strconv.FormatBool(*all)) } - _, err = conn.DoRequest(nil, http.MethodPost, "/manifests/%s/push", params, name) + _, err = conn.DoRequest(nil, http.MethodPost, "/manifests/%s/push", params, nil, name) if err != nil { return "", err } diff --git a/pkg/bindings/network/network.go b/pkg/bindings/network/network.go index 7bba4f478..34881b524 100644 --- a/pkg/bindings/network/network.go +++ b/pkg/bindings/network/network.go @@ -28,7 +28,7 @@ func Create(ctx context.Context, options entities.NetworkCreateOptions, name *st return nil, err } stringReader := strings.NewReader(networkConfig) - response, err := conn.DoRequest(stringReader, http.MethodPost, "/networks/create", params) + response, err := conn.DoRequest(stringReader, http.MethodPost, "/networks/create", params, nil) if err != nil { return nil, err } @@ -42,7 +42,7 @@ func Inspect(ctx context.Context, nameOrID string) ([]entities.NetworkInspectRep if err != nil { return nil, err } - response, err := conn.DoRequest(nil, http.MethodGet, "/networks/%s/json", nil, nameOrID) + response, err := conn.DoRequest(nil, http.MethodGet, "/networks/%s/json", nil, nil, nameOrID) if err != nil { return nil, err } @@ -62,7 +62,7 @@ func Remove(ctx context.Context, nameOrID string, force *bool) ([]*entities.Netw if force != nil { params.Set("size", strconv.FormatBool(*force)) } - response, err := conn.DoRequest(nil, http.MethodDelete, "/networks/%s", params, nameOrID) + response, err := conn.DoRequest(nil, http.MethodDelete, "/networks/%s", params, nil, nameOrID) if err != nil { return nil, err } @@ -78,7 +78,7 @@ func List(ctx context.Context) ([]*entities.NetworkListReport, error) { if err != nil { return nil, err } - response, err := conn.DoRequest(nil, http.MethodGet, "/networks/json", nil) + response, err := conn.DoRequest(nil, http.MethodGet, "/networks/json", nil, nil) if err != nil { return netList, err } diff --git a/pkg/bindings/play/play.go b/pkg/bindings/play/play.go index 653558a3c..288cca454 100644 --- a/pkg/bindings/play/play.go +++ b/pkg/bindings/play/play.go @@ -8,6 +8,7 @@ import ( "strconv" "github.com/containers/image/v5/types" + "github.com/containers/libpod/pkg/auth" "github.com/containers/libpod/pkg/bindings" "github.com/containers/libpod/pkg/domain/entities" ) @@ -31,7 +32,13 @@ func PlayKube(ctx context.Context, path string, options entities.PlayKubeOptions params.Set("tlsVerify", strconv.FormatBool(options.SkipTLSVerify == types.OptionalBoolTrue)) } - response, err := conn.DoRequest(f, http.MethodPost, "/play/kube", params) + // TODO: have a global system context we can pass around (1st argument) + header, err := auth.Header(nil, options.Authfile, options.Username, options.Password) + if err != nil { + return nil, err + } + + response, err := conn.DoRequest(f, http.MethodPost, "/play/kube", params, header) if err != nil { return nil, err } diff --git a/pkg/bindings/pods/pods.go b/pkg/bindings/pods/pods.go index b213c8c73..fb273fdf3 100644 --- a/pkg/bindings/pods/pods.go +++ b/pkg/bindings/pods/pods.go @@ -28,7 +28,7 @@ func CreatePodFromSpec(ctx context.Context, s *specgen.PodSpecGenerator) (*entit return nil, err } stringReader := strings.NewReader(specgenString) - response, err := conn.DoRequest(stringReader, http.MethodPost, "/pods/create", nil) + response, err := conn.DoRequest(stringReader, http.MethodPost, "/pods/create", nil, nil) if err != nil { return nil, err } @@ -41,7 +41,7 @@ func Exists(ctx context.Context, nameOrID string) (bool, error) { if err != nil { return false, err } - response, err := conn.DoRequest(nil, http.MethodGet, "/pods/%s/exists", nil, nameOrID) + response, err := conn.DoRequest(nil, http.MethodGet, "/pods/%s/exists", nil, nil, nameOrID) if err != nil { return false, err } @@ -57,7 +57,7 @@ func Inspect(ctx context.Context, nameOrID string) (*entities.PodInspectReport, if err != nil { return nil, err } - response, err := conn.DoRequest(nil, http.MethodGet, "/pods/%s/json", nil, nameOrID) + response, err := conn.DoRequest(nil, http.MethodGet, "/pods/%s/json", nil, nil, nameOrID) if err != nil { return nil, err } @@ -78,7 +78,7 @@ func Kill(ctx context.Context, nameOrID string, signal *string) (*entities.PodKi if signal != nil { params.Set("signal", *signal) } - response, err := conn.DoRequest(nil, http.MethodPost, "/pods/%s/kill", params, nameOrID) + response, err := conn.DoRequest(nil, http.MethodPost, "/pods/%s/kill", params, nil, nameOrID) if err != nil { return nil, err } @@ -92,7 +92,7 @@ func Pause(ctx context.Context, nameOrID string) (*entities.PodPauseReport, erro if err != nil { return nil, err } - response, err := conn.DoRequest(nil, http.MethodPost, "/pods/%s/pause", nil, nameOrID) + response, err := conn.DoRequest(nil, http.MethodPost, "/pods/%s/pause", nil, nil, nameOrID) if err != nil { return nil, err } @@ -107,7 +107,7 @@ func Prune(ctx context.Context) ([]*entities.PodPruneReport, error) { if err != nil { return nil, err } - response, err := conn.DoRequest(nil, http.MethodPost, "/pods/prune", nil) + response, err := conn.DoRequest(nil, http.MethodPost, "/pods/prune", nil, nil) if err != nil { return nil, err } @@ -132,7 +132,7 @@ func List(ctx context.Context, filters map[string][]string) ([]*entities.ListPod } params.Set("filters", stringFilter) } - response, err := conn.DoRequest(nil, http.MethodGet, "/pods/json", params) + response, err := conn.DoRequest(nil, http.MethodGet, "/pods/json", params, nil) if err != nil { return podsReports, err } @@ -146,7 +146,7 @@ func Restart(ctx context.Context, nameOrID string) (*entities.PodRestartReport, if err != nil { return nil, err } - response, err := conn.DoRequest(nil, http.MethodPost, "/pods/%s/restart", nil, nameOrID) + response, err := conn.DoRequest(nil, http.MethodPost, "/pods/%s/restart", nil, nil, nameOrID) if err != nil { return nil, err } @@ -165,7 +165,7 @@ func Remove(ctx context.Context, nameOrID string, force *bool) (*entities.PodRmR if force != nil { params.Set("force", strconv.FormatBool(*force)) } - response, err := conn.DoRequest(nil, http.MethodDelete, "/pods/%s", params, nameOrID) + response, err := conn.DoRequest(nil, http.MethodDelete, "/pods/%s", params, nil, nameOrID) if err != nil { return nil, err } @@ -179,7 +179,7 @@ func Start(ctx context.Context, nameOrID string) (*entities.PodStartReport, erro if err != nil { return nil, err } - response, err := conn.DoRequest(nil, http.MethodPost, "/pods/%s/start", nil, nameOrID) + response, err := conn.DoRequest(nil, http.MethodPost, "/pods/%s/start", nil, nil, nameOrID) if err != nil { return nil, err } @@ -202,7 +202,7 @@ func Stop(ctx context.Context, nameOrID string, timeout *int) (*entities.PodStop if timeout != nil { params.Set("t", strconv.Itoa(*timeout)) } - response, err := conn.DoRequest(nil, http.MethodPost, "/pods/%s/stop", params, nameOrID) + response, err := conn.DoRequest(nil, http.MethodPost, "/pods/%s/stop", params, nil, nameOrID) if err != nil { return nil, err } @@ -226,7 +226,7 @@ func Top(ctx context.Context, nameOrID string, descriptors []string) ([]string, // flatten the slice into one string params.Set("ps_args", strings.Join(descriptors, ",")) } - response, err := conn.DoRequest(nil, http.MethodGet, "/pods/%s/top", params, nameOrID) + response, err := conn.DoRequest(nil, http.MethodGet, "/pods/%s/top", params, nil, nameOrID) if err != nil { return nil, err } @@ -254,7 +254,7 @@ func Unpause(ctx context.Context, nameOrID string) (*entities.PodUnpauseReport, if err != nil { return nil, err } - response, err := conn.DoRequest(nil, http.MethodPost, "/pods/%s/unpause", nil, nameOrID) + response, err := conn.DoRequest(nil, http.MethodPost, "/pods/%s/unpause", nil, nil, nameOrID) if err != nil { return nil, err } @@ -277,7 +277,7 @@ func Stats(ctx context.Context, namesOrIDs []string, options entities.PodStatsOp params.Set("all", strconv.FormatBool(options.All)) var reports []*entities.PodStatsReport - response, err := conn.DoRequest(nil, http.MethodGet, "/pods/stats", params) + response, err := conn.DoRequest(nil, http.MethodGet, "/pods/stats", params, nil) if err != nil { return nil, err } diff --git a/pkg/bindings/system/info.go b/pkg/bindings/system/info.go index 13e12645d..8ad704f84 100644 --- a/pkg/bindings/system/info.go +++ b/pkg/bindings/system/info.go @@ -15,7 +15,7 @@ func Info(ctx context.Context) (*define.Info, error) { if err != nil { return nil, err } - response, err := conn.DoRequest(nil, http.MethodGet, "/info", nil) + response, err := conn.DoRequest(nil, http.MethodGet, "/info", nil, nil) if err != nil { return nil, err } diff --git a/pkg/bindings/system/system.go b/pkg/bindings/system/system.go index 2cd894228..010762bef 100644 --- a/pkg/bindings/system/system.go +++ b/pkg/bindings/system/system.go @@ -42,7 +42,7 @@ func Events(ctx context.Context, eventChan chan entities.Event, cancelChan chan } params.Set("filters", filterString) } - response, err := conn.DoRequest(nil, http.MethodGet, "/events", params) + response, err := conn.DoRequest(nil, http.MethodGet, "/events", params, nil) if err != nil { return err } @@ -89,7 +89,7 @@ func Prune(ctx context.Context, all, volumes *bool) (*entities.SystemPruneReport if volumes != nil { params.Set("Volumes", strconv.FormatBool(*volumes)) } - response, err := conn.DoRequest(nil, http.MethodPost, "/system/prune", params) + response, err := conn.DoRequest(nil, http.MethodPost, "/system/prune", params, nil) if err != nil { return nil, err } @@ -110,7 +110,7 @@ func Version(ctx context.Context) (*entities.SystemVersionReport, error) { if err != nil { return nil, err } - response, err := conn.DoRequest(nil, http.MethodGet, "/version", nil) + response, err := conn.DoRequest(nil, http.MethodGet, "/version", nil, nil) if err != nil { return nil, err } @@ -139,7 +139,7 @@ func DiskUsage(ctx context.Context) (*entities.SystemDfReport, error) { if err != nil { return nil, err } - response, err := conn.DoRequest(nil, http.MethodGet, "/system/df", nil) + response, err := conn.DoRequest(nil, http.MethodGet, "/system/df", nil, nil) if err != nil { return nil, err } diff --git a/pkg/bindings/test/auth_test.go b/pkg/bindings/test/auth_test.go new file mode 100644 index 000000000..fdb190551 --- /dev/null +++ b/pkg/bindings/test/auth_test.go @@ -0,0 +1,143 @@ +package test_bindings + +import ( + "io/ioutil" + "os" + "time" + + "github.com/containers/common/pkg/auth" + "github.com/containers/image/v5/types" + podmanRegistry "github.com/containers/libpod/hack/podman-registry-go" + "github.com/containers/libpod/pkg/bindings/images" + "github.com/containers/libpod/pkg/domain/entities" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "github.com/onsi/gomega/gexec" +) + +var _ = Describe("Podman images", func() { + var ( + registry *podmanRegistry.Registry + bt *bindingTest + s *gexec.Session + err error + ) + + BeforeEach(func() { + // Note: we need to start the registry **before** setting up + // the test. Otherwise, the registry is not reachable for + // currently unknown reasons. + registry, err = podmanRegistry.Start() + Expect(err).To(BeNil()) + + bt = newBindingTest() + bt.RestoreImagesFromCache() + s = bt.startAPIService() + time.Sleep(1 * time.Second) + err := bt.NewConnection() + Expect(err).To(BeNil()) + }) + + AfterEach(func() { + s.Kill() + bt.cleanup() + registry.Stop() + }) + + // Test using credentials. + It("tag + push + pull (with credentials)", func() { + + imageRep := "localhost:" + registry.Port + "/test" + imageTag := "latest" + imageRef := imageRep + ":" + imageTag + + // Tag the alpine image and verify it has worked. + err = images.Tag(bt.conn, alpine.shortName, imageTag, imageRep) + Expect(err).To(BeNil()) + _, err = images.GetImage(bt.conn, imageRef, nil) + Expect(err).To(BeNil()) + + // Now push the image. + pushOpts := entities.ImagePushOptions{ + Username: registry.User, + Password: registry.Password, + SkipTLSVerify: types.OptionalBoolTrue, + } + err = images.Push(bt.conn, imageRef, imageRef, pushOpts) + Expect(err).To(BeNil()) + + // Now pull the image. + pullOpts := entities.ImagePullOptions{ + Username: registry.User, + Password: registry.Password, + SkipTLSVerify: types.OptionalBoolTrue, + } + _, err = images.Pull(bt.conn, imageRef, pullOpts) + Expect(err).To(BeNil()) + }) + + // Test using authfile. + It("tag + push + pull + search (with authfile)", func() { + + imageRep := "localhost:" + registry.Port + "/test" + imageTag := "latest" + imageRef := imageRep + ":" + imageTag + + // Create a temporary authentication file. + tmpFile, err := ioutil.TempFile("", "auth.json.") + Expect(err).To(BeNil()) + _, err = tmpFile.Write([]byte{'{', '}'}) + Expect(err).To(BeNil()) + err = tmpFile.Close() + Expect(err).To(BeNil()) + + authFilePath := tmpFile.Name() + + // Now login to a) test the credentials and to b) store them in + // the authfile for later use. + sys := types.SystemContext{ + AuthFilePath: authFilePath, + DockerInsecureSkipTLSVerify: types.OptionalBoolTrue, + } + loginOptions := auth.LoginOptions{ + Username: registry.User, + Password: registry.Password, + AuthFile: authFilePath, + Stdin: os.Stdin, + Stdout: os.Stdout, + } + err = auth.Login(bt.conn, &sys, &loginOptions, []string{imageRep}) + Expect(err).To(BeNil()) + + // Tag the alpine image and verify it has worked. + err = images.Tag(bt.conn, alpine.shortName, imageTag, imageRep) + Expect(err).To(BeNil()) + _, err = images.GetImage(bt.conn, imageRef, nil) + Expect(err).To(BeNil()) + + // Now push the image. + pushOpts := entities.ImagePushOptions{ + Authfile: authFilePath, + SkipTLSVerify: types.OptionalBoolTrue, + } + err = images.Push(bt.conn, imageRef, imageRef, pushOpts) + Expect(err).To(BeNil()) + + // Now pull the image. + pullOpts := entities.ImagePullOptions{ + Authfile: authFilePath, + SkipTLSVerify: types.OptionalBoolTrue, + } + _, err = images.Pull(bt.conn, imageRef, pullOpts) + Expect(err).To(BeNil()) + + // Last, but not least, exercise search. + searchOptions := entities.ImageSearchOptions{ + Authfile: authFilePath, + SkipTLSVerify: types.OptionalBoolTrue, + } + _, err = images.Search(bt.conn, imageRef, searchOptions) + Expect(err).To(BeNil()) + }) + +}) diff --git a/pkg/bindings/volumes/volumes.go b/pkg/bindings/volumes/volumes.go index cef9246cb..ebe19794a 100644 --- a/pkg/bindings/volumes/volumes.go +++ b/pkg/bindings/volumes/volumes.go @@ -26,7 +26,7 @@ func Create(ctx context.Context, config entities.VolumeCreateOptions) (*entities return nil, err } stringReader := strings.NewReader(createString) - response, err := conn.DoRequest(stringReader, http.MethodPost, "/volumes/create", nil) + response, err := conn.DoRequest(stringReader, http.MethodPost, "/volumes/create", nil, nil) if err != nil { return nil, err } @@ -42,7 +42,7 @@ func Inspect(ctx context.Context, nameOrID string) (*entities.VolumeConfigRespon if err != nil { return nil, err } - response, err := conn.DoRequest(nil, http.MethodGet, "/volumes/%s/json", nil, nameOrID) + response, err := conn.DoRequest(nil, http.MethodGet, "/volumes/%s/json", nil, nil, nameOrID) if err != nil { return &inspect, err } @@ -67,7 +67,7 @@ func List(ctx context.Context, filters map[string][]string) ([]*entities.VolumeL } params.Set("filters", strFilters) } - response, err := conn.DoRequest(nil, http.MethodGet, "/volumes/json", params) + response, err := conn.DoRequest(nil, http.MethodGet, "/volumes/json", params, nil) if err != nil { return vols, err } @@ -83,7 +83,7 @@ func Prune(ctx context.Context) ([]*entities.VolumePruneReport, error) { if err != nil { return nil, err } - response, err := conn.DoRequest(nil, http.MethodPost, "/volumes/prune", nil) + response, err := conn.DoRequest(nil, http.MethodPost, "/volumes/prune", nil, nil) if err != nil { return nil, err } @@ -101,7 +101,7 @@ func Remove(ctx context.Context, nameOrID string, force *bool) error { if force != nil { params.Set("force", strconv.FormatBool(*force)) } - response, err := conn.DoRequest(nil, http.MethodDelete, "/volumes/%s", params, nameOrID) + response, err := conn.DoRequest(nil, http.MethodDelete, "/volumes/%s", params, nil, nameOrID) if err != nil { return err } diff --git a/pkg/domain/entities/engine.go b/pkg/domain/entities/engine.go index 265c9f36f..db58befa5 100644 --- a/pkg/domain/entities/engine.go +++ b/pkg/domain/entities/engine.go @@ -37,19 +37,20 @@ type PodmanConfig struct { *config.Config *pflag.FlagSet - CGroupUsage string // rootless code determines Usage message - ConmonPath string // --conmon flag will set Engine.ConmonPath - CpuProfile string // Hidden: Should CPU profile be taken - EngineMode EngineMode // ABI or Tunneling mode - Identities []string // ssh identities for connecting to server - MaxWorks int // maximum number of parallel threads - RuntimePath string // --runtime flag will set Engine.RuntimePath - SpanCloser io.Closer // Close() for tracing object - SpanCtx context.Context // context to use when tracing - Span opentracing.Span // tracing object - Syslog bool // write to StdOut and Syslog, not supported when tunneling - Trace bool // Hidden: Trace execution - Uri string // URI to API Service + CGroupUsage string // rootless code determines Usage message + ConmonPath string // --conmon flag will set Engine.ConmonPath + CpuProfile string // Hidden: Should CPU profile be taken + EngineMode EngineMode // ABI or Tunneling mode + Identities []string // ssh identities for connecting to server + MaxWorks int // maximum number of parallel threads + RegistriesConf string // allows for specifying a custom registries.conf + RuntimePath string // --runtime flag will set Engine.RuntimePath + SpanCloser io.Closer // Close() for tracing object + SpanCtx context.Context // context to use when tracing + Span opentracing.Span // tracing object + Syslog bool // write to StdOut and Syslog, not supported when tunneling + Trace bool // Hidden: Trace execution + Uri string // URI to API Service Runroot string StorageDriver string diff --git a/pkg/domain/entities/images.go b/pkg/domain/entities/images.go index 968bcff3b..19a2c87f5 100644 --- a/pkg/domain/entities/images.go +++ b/pkg/domain/entities/images.go @@ -128,9 +128,10 @@ type ImagePullOptions struct { // CertDir is the path to certificate directories. Ignored for remote // calls. CertDir string - // Credentials for authenticating against the registry in the format - // USERNAME:PASSWORD. - Credentials string + // Username for authenticating against the registry. + Username string + // Password for authenticating against the registry. + Password string // OverrideArch will overwrite the local architecture for image pulls. OverrideArch string // OverrideOS will overwrite the local operating system (OS) for image @@ -162,9 +163,10 @@ type ImagePushOptions struct { // transport. Default is same compression type as source. Ignored for remote // calls. Compress bool - // Credentials for authenticating against the registry in the format - // USERNAME:PASSWORD. - Credentials string + // Username for authenticating against the registry. + Username string + // Password for authenticating against the registry. + Password string // DigestFile, after copying the image, write the digest of the resulting // image to the file. Ignored for remote calls. DigestFile string diff --git a/pkg/domain/entities/manifest.go b/pkg/domain/entities/manifest.go index 273052bb9..853619b19 100644 --- a/pkg/domain/entities/manifest.go +++ b/pkg/domain/entities/manifest.go @@ -1,5 +1,9 @@ package entities +import "github.com/containers/image/v5/types" + +// TODO: add comments to *all* types and fields. + type ManifestCreateOptions struct { All bool `schema:"all"` } @@ -26,6 +30,9 @@ type ManifestAnnotateOptions struct { } type ManifestPushOptions struct { - Purge, Quiet, All, TlsVerify, RemoveSignatures bool - Authfile, CertDir, Creds, DigestFile, Format, SignBy string + Purge, Quiet, All, RemoveSignatures bool + + Authfile, CertDir, Username, Password, DigestFile, Format, SignBy string + + SkipTLSVerify types.OptionalBool } diff --git a/pkg/domain/entities/play.go b/pkg/domain/entities/play.go index 93864c23b..4f485cbee 100644 --- a/pkg/domain/entities/play.go +++ b/pkg/domain/entities/play.go @@ -8,9 +8,10 @@ type PlayKubeOptions struct { Authfile string // CertDir - to a directory containing TLS certifications and keys. CertDir string - // Credentials - `username:password` for authentication against a - // container registry. - Credentials string + // Username for authenticating against the registry. + Username string + // Password for authenticating against the registry. + Password string // Network - name of the CNI network to connect to. Network string // Quiet - suppress output when pulling images. diff --git a/pkg/domain/entities/system.go b/pkg/domain/entities/system.go index 5e4760d12..79a90be48 100644 --- a/pkg/domain/entities/system.go +++ b/pkg/domain/entities/system.go @@ -97,3 +97,9 @@ type SystemVersionReport struct { type ComponentVersion struct { types.Version } + +// ListRegistriesReport is the report when querying for a sorted list of +// registries which may be contacted during certain operations. +type ListRegistriesReport struct { + Registries []string +} diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go index 6e774df8e..d8af4d339 100644 --- a/pkg/domain/infra/abi/images.go +++ b/pkg/domain/infra/abi/images.go @@ -121,12 +121,11 @@ func (ir *ImageEngine) Pull(ctx context.Context, rawImage string, options entiti } var registryCreds *types.DockerAuthConfig - if options.Credentials != "" { - creds, err := util.ParseRegistryCreds(options.Credentials) - if err != nil { - return nil, err + if len(options.Username) > 0 && len(options.Password) > 0 { + registryCreds = &types.DockerAuthConfig{ + Username: options.Username, + Password: options.Password, } - registryCreds = creds } dockerRegistryOptions := image.DockerRegistryOptions{ DockerRegistryCreds: registryCreds, @@ -226,12 +225,11 @@ func (ir *ImageEngine) Push(ctx context.Context, source string, destination stri } var registryCreds *types.DockerAuthConfig - if options.Credentials != "" { - creds, err := util.ParseRegistryCreds(options.Credentials) - if err != nil { - return err + if len(options.Username) > 0 && len(options.Password) > 0 { + registryCreds = &types.DockerAuthConfig{ + Username: options.Username, + Password: options.Password, } - registryCreds = creds } dockerRegistryOptions := image.DockerRegistryOptions{ DockerRegistryCreds: registryCreds, diff --git a/pkg/domain/infra/abi/manifest.go b/pkg/domain/infra/abi/manifest.go index fca34dda2..6e311dec7 100644 --- a/pkg/domain/infra/abi/manifest.go +++ b/pkg/domain/infra/abi/manifest.go @@ -16,6 +16,7 @@ import ( "github.com/containers/image/v5/docker" "github.com/containers/image/v5/manifest" "github.com/containers/image/v5/transports/alltransports" + "github.com/containers/image/v5/types" libpodImage "github.com/containers/libpod/libpod/image" "github.com/containers/libpod/pkg/domain/entities" "github.com/containers/libpod/pkg/util" @@ -179,12 +180,28 @@ func (ir *ImageEngine) ManifestPush(ctx context.Context, names []string, opts en case "v2s2", "docker": manifestType = manifest.DockerV2Schema2MediaType default: - return errors.Errorf("unknown format %q. Choose on of the supported formats: 'oci' or 'v2s2'", opts.Format) + return errors.Errorf("unknown format %q. Choose one of the supported formats: 'oci' or 'v2s2'", opts.Format) } } + + // Set the system context. + sys := ir.Libpod.SystemContext() + if sys != nil { + sys = &types.SystemContext{} + } + sys.AuthFilePath = opts.Authfile + sys.DockerInsecureSkipTLSVerify = opts.SkipTLSVerify + + if opts.Username != "" && opts.Password != "" { + sys.DockerAuthConfig = &types.DockerAuthConfig{ + Username: opts.Username, + Password: opts.Password, + } + } + options := manifests.PushOptions{ Store: ir.Libpod.GetStore(), - SystemContext: ir.Libpod.SystemContext(), + SystemContext: sys, ImageListSelection: cp.CopySpecificImages, Instances: nil, RemoveSignatures: opts.RemoveSignatures, diff --git a/pkg/domain/infra/abi/play.go b/pkg/domain/infra/abi/play.go index cd7eec7e6..6d0919d2b 100644 --- a/pkg/domain/infra/abi/play.go +++ b/pkg/domain/infra/abi/play.go @@ -56,6 +56,9 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, path string, options en return nil, errors.Wrapf(err, "unable to read %q as YAML", path) } + // NOTE: pkg/bindings/play is also parsing the file. + // A pkg/kube would be nice to refactor and abstract + // parts of the K8s-related code. if podYAML.Kind != "Pod" { return nil, errors.Errorf("invalid YAML kind: %q. Pod is the only supported Kubernetes YAML kind", podYAML.Kind) } @@ -147,6 +150,13 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, path string, options en writer = os.Stderr } + if len(options.Username) > 0 && len(options.Password) > 0 { + registryCreds = &types.DockerAuthConfig{ + Username: options.Username, + Password: options.Password, + } + } + dockerRegistryOptions := image.DockerRegistryOptions{ DockerRegistryCreds: registryCreds, DockerCertPath: options.CertDir, diff --git a/pkg/domain/infra/runtime_libpod.go b/pkg/domain/infra/runtime_libpod.go index 7c9180d43..a57eadc63 100644 --- a/pkg/domain/infra/runtime_libpod.go +++ b/pkg/domain/infra/runtime_libpod.go @@ -213,6 +213,9 @@ func getRuntime(ctx context.Context, fs *flag.FlagSet, opts *engineOpts) (*libpo if fs.Changed("hooks-dir") { options = append(options, libpod.WithHooksDir(cfg.Engine.HooksDir...)) } + if fs.Changed("registries-conf") { + options = append(options, libpod.WithRegistriesConf(cfg.RegistriesConf)) + } // TODO flag to set CNI plugins dir? diff --git a/vendor/github.com/containers/image/v5/docker/docker_image_src.go b/vendor/github.com/containers/image/v5/docker/docker_image_src.go index 10aff615e..55eb38824 100644 --- a/vendor/github.com/containers/image/v5/docker/docker_image_src.go +++ b/vendor/github.com/containers/image/v5/docker/docker_image_src.go @@ -190,6 +190,7 @@ func (s *dockerImageSource) fetchManifest(ctx context.Context, tagOrDigest strin if err != nil { return nil, "", err } + logrus.Debugf("Content-Type from manifest GET is %q", res.Header.Get("Content-Type")) defer res.Body.Close() if res.StatusCode != http.StatusOK { return nil, "", errors.Wrapf(client.HandleErrorResponse(res), "Error reading manifest %s in %s", tagOrDigest, s.physicalRef.ref.Name()) diff --git a/vendor/github.com/containers/image/v5/manifest/oci.go b/vendor/github.com/containers/image/v5/manifest/oci.go index 40c40dee8..292614593 100644 --- a/vendor/github.com/containers/image/v5/manifest/oci.go +++ b/vendor/github.com/containers/image/v5/manifest/oci.go @@ -172,7 +172,7 @@ func (m *OCI1) Inspect(configGetter func(types.BlobInfo) ([]byte, error)) (*type Architecture: v1.Architecture, Os: v1.OS, Layers: layerInfosToStrings(m.LayerInfos()), - Env: d1.Config.Env, + Env: v1.Config.Env, } return i, nil } diff --git a/vendor/github.com/containers/image/v5/pkg/docker/config/config.go b/vendor/github.com/containers/image/v5/pkg/docker/config/config.go index dae3eb586..ce85af18a 100644 --- a/vendor/github.com/containers/image/v5/pkg/docker/config/config.go +++ b/vendor/github.com/containers/image/v5/pkg/docker/config/config.go @@ -7,6 +7,7 @@ import ( "io/ioutil" "os" "path/filepath" + "runtime" "strings" "github.com/containers/image/v5/types" @@ -37,7 +38,12 @@ var ( xdgRuntimeDirPath = filepath.FromSlash("containers/auth.json") dockerHomePath = filepath.FromSlash(".docker/config.json") dockerLegacyHomePath = ".dockercfg" + nonLinuxAuthFilePath = filepath.FromSlash(".config/containers/auth.json") + // Note that the keyring support has been disabled as it was causing + // regressions. Before enabling, please revisit TODO(keyring) comments + // which need to be addressed if the need remerged to support the + // kernel keyring. enableKeyring = false // ErrNotLoggedIn is returned for users not logged into a registry @@ -73,6 +79,70 @@ func SetAuthentication(sys *types.SystemContext, registry, username, password st }) } +// GetAllCredentials returns the registry credentials for all registries stored +// in either the auth.json file or the docker/config.json. +func GetAllCredentials(sys *types.SystemContext) (map[string]types.DockerAuthConfig, error) { + // Note: we need to read the auth files in the inverse order to prevent + // a priority inversion when writing to the map. + authConfigs := make(map[string]types.DockerAuthConfig) + paths := getAuthFilePaths(sys) + for i := len(paths) - 1; i >= 0; i-- { + path := paths[i] + // readJSONFile returns an empty map in case the path doesn't exist. + auths, err := readJSONFile(path.path, path.legacyFormat) + if err != nil { + return nil, errors.Wrapf(err, "error reading JSON file %q", path.path) + } + + for registry, data := range auths.AuthConfigs { + conf, err := decodeDockerAuth(data) + if err != nil { + return nil, err + } + authConfigs[normalizeRegistry(registry)] = conf + } + + // Credential helpers may override credentials from the auth file. + for registry, credHelper := range auths.CredHelpers { + username, password, err := getAuthFromCredHelper(credHelper, registry) + if err != nil { + if credentials.IsErrCredentialsNotFoundMessage(err.Error()) { + continue + } + return nil, err + } + + conf := types.DockerAuthConfig{Username: username, Password: password} + authConfigs[normalizeRegistry(registry)] = conf + } + } + + // TODO(keyring): if we ever reenable the keyring support, we had to + // query all credentials from the keyring here. + + return authConfigs, nil +} + +// getAuthFilePaths returns a slice of authPaths based on the system context +// in the order they should be searched. Note that some paths may not exist. +func getAuthFilePaths(sys *types.SystemContext) []authPath { + paths := []authPath{} + pathToAuth, lf, err := getPathToAuth(sys) + if err == nil { + paths = append(paths, authPath{path: pathToAuth, legacyFormat: lf}) + } else { + // Error means that the path set for XDG_RUNTIME_DIR does not exist + // but we don't want to completely fail in the case that the user is pulling a public image + // Logging the error as a warning instead and moving on to pulling the image + logrus.Warnf("%v: Trying to pull image in the event that it is a public image.", err) + } + paths = append(paths, + authPath{path: filepath.Join(homedir.Get(), dockerHomePath), legacyFormat: false}, + authPath{path: filepath.Join(homedir.Get(), dockerLegacyHomePath), legacyFormat: true}, + ) + return paths +} + // GetCredentials returns the registry credentials stored in either auth.json // file or .docker/config.json, including support for OAuth2 and IdentityToken. // If an entry is not found, an empty struct is returned. @@ -93,21 +163,7 @@ func GetCredentials(sys *types.SystemContext, registry string) (types.DockerAuth } } - paths := []authPath{} - pathToAuth, lf, err := getPathToAuth(sys) - if err == nil { - paths = append(paths, authPath{path: pathToAuth, legacyFormat: lf}) - } else { - // Error means that the path set for XDG_RUNTIME_DIR does not exist - // but we don't want to completely fail in the case that the user is pulling a public image - // Logging the error as a warning instead and moving on to pulling the image - logrus.Warnf("%v: Trying to pull image in the event that it is a public image.", err) - } - paths = append(paths, - authPath{path: filepath.Join(homedir.Get(), dockerHomePath), legacyFormat: false}, - authPath{path: filepath.Join(homedir.Get(), dockerLegacyHomePath), legacyFormat: true}) - - for _, path := range paths { + for _, path := range getAuthFilePaths(sys) { authConfig, err := findAuthentication(registry, path.path, path.legacyFormat) if err != nil { logrus.Debugf("Credentials not found") @@ -189,10 +245,8 @@ func RemoveAllAuthentication(sys *types.SystemContext) error { }) } -// getPath gets the path of the auth.json file -// The path can be overriden by the user if the overwrite-path flag is set -// If the flag is not set and XDG_RUNTIME_DIR is set, the auth.json file is saved in XDG_RUNTIME_DIR/containers -// Otherwise, the auth.json file is stored in /run/containers/UID +// getPathToAuth gets the path of the auth.json file used for reading and writting credentials +// returns the path, and a bool specifies whether the file is in legacy format func getPathToAuth(sys *types.SystemContext) (string, bool, error) { if sys != nil { if sys.AuthFilePath != "" { @@ -205,6 +259,9 @@ func getPathToAuth(sys *types.SystemContext) (string, bool, error) { return filepath.Join(sys.RootForImplicitAbsolutePaths, fmt.Sprintf(defaultPerUIDPathFormat, os.Getuid())), false, nil } } + if runtime.GOOS == "windows" || runtime.GOOS == "darwin" { + return filepath.Join(homedir.Get(), nonLinuxAuthFilePath), false, nil + } runtimeDir := os.Getenv("XDG_RUNTIME_DIR") if runtimeDir != "" { @@ -248,6 +305,13 @@ func readJSONFile(path string, legacyFormat bool) (dockerConfigFile, error) { return dockerConfigFile{}, errors.Wrapf(err, "error unmarshaling JSON at %q", path) } + if auths.AuthConfigs == nil { + auths.AuthConfigs = map[string]dockerAuthConfig{} + } + if auths.CredHelpers == nil { + auths.CredHelpers = make(map[string]string) + } + return auths, nil } @@ -257,17 +321,15 @@ func modifyJSON(sys *types.SystemContext, editor func(auths *dockerConfigFile) ( if err != nil { return err } + if legacyFormat { + return fmt.Errorf("writes to %s using legacy format are not supported", path) + } dir := filepath.Dir(path) - if _, err := os.Stat(dir); os.IsNotExist(err) { - if err = os.MkdirAll(dir, 0700); err != nil { - return errors.Wrapf(err, "error creating directory %q", dir) - } + if err = os.MkdirAll(dir, 0700); err != nil { + return err } - if legacyFormat { - return fmt.Errorf("writes to %s using legacy format are not supported", path) - } auths, err := readJSONFile(path, false) if err != nil { return errors.Wrapf(err, "error reading JSON file %q", path) 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 3eee70bc2..72fcf618a 100644 --- a/vendor/github.com/containers/image/v5/signature/policy_config.go +++ b/vendor/github.com/containers/image/v5/signature/policy_config.go @@ -17,11 +17,13 @@ import ( "encoding/json" "fmt" "io/ioutil" + "os" "path/filepath" "github.com/containers/image/v5/docker/reference" "github.com/containers/image/v5/transports" "github.com/containers/image/v5/types" + "github.com/containers/storage/pkg/homedir" "github.com/pkg/errors" ) @@ -34,6 +36,9 @@ var systemDefaultPolicyPath = builtinDefaultPolicyPath // DO NOT change this, instead see systemDefaultPolicyPath above. const builtinDefaultPolicyPath = "/etc/containers/policy.json" +// userPolicyFile is the path to the per user policy path. +var userPolicyFile = filepath.FromSlash(".config/containers/policy.json") + // InvalidPolicyFormatError is returned when parsing an invalid policy configuration. type InvalidPolicyFormatError string @@ -53,13 +58,15 @@ func DefaultPolicy(sys *types.SystemContext) (*Policy, error) { // defaultPolicyPath returns a path to the default policy of the system. func defaultPolicyPath(sys *types.SystemContext) string { - if sys != nil { - if sys.SignaturePolicyPath != "" { - return sys.SignaturePolicyPath - } - if sys.RootForImplicitAbsolutePaths != "" { - return filepath.Join(sys.RootForImplicitAbsolutePaths, systemDefaultPolicyPath) - } + if sys != nil && sys.SignaturePolicyPath != "" { + return sys.SignaturePolicyPath + } + userPolicyFilePath := filepath.Join(homedir.Get(), userPolicyFile) + if _, err := os.Stat(userPolicyFilePath); err == nil { + return userPolicyFilePath + } + if sys != nil && sys.RootForImplicitAbsolutePaths != "" { + return filepath.Join(sys.RootForImplicitAbsolutePaths, systemDefaultPolicyPath) } return systemDefaultPolicyPath } diff --git a/vendor/github.com/containers/image/v5/version/version.go b/vendor/github.com/containers/image/v5/version/version.go index 67f57e03e..7cf412723 100644 --- a/vendor/github.com/containers/image/v5/version/version.go +++ b/vendor/github.com/containers/image/v5/version/version.go @@ -6,12 +6,12 @@ const ( // VersionMajor is for an API incompatible changes VersionMajor = 5 // VersionMinor is for functionality in a backwards-compatible manner - VersionMinor = 4 + VersionMinor = 5 // VersionPatch is for backwards-compatible bug fixes - VersionPatch = 4 + VersionPatch = 0 // VersionDev indicates development branch. Releases will be empty string. - VersionDev = "" + VersionDev = "-dev" ) // Version is the specification version that the package types support. diff --git a/vendor/github.com/klauspost/compress/zstd/blockdec.go b/vendor/github.com/klauspost/compress/zstd/blockdec.go index c2f855e75..19181caea 100644 --- a/vendor/github.com/klauspost/compress/zstd/blockdec.go +++ b/vendor/github.com/klauspost/compress/zstd/blockdec.go @@ -156,8 +156,12 @@ func (b *blockDec) reset(br byteBuffer, windowSize uint64) error { } return ErrCompressedSizeTooBig } - default: + case blockTypeRaw: b.RLESize = 0 + // We do not need a destination for raw blocks. + maxSize = -1 + default: + panic("Invalid block type") } // Read block data. diff --git a/vendor/github.com/klauspost/compress/zstd/decoder.go b/vendor/github.com/klauspost/compress/zstd/decoder.go index 234025505..324347623 100644 --- a/vendor/github.com/klauspost/compress/zstd/decoder.go +++ b/vendor/github.com/klauspost/compress/zstd/decoder.go @@ -461,6 +461,7 @@ func (d *Decoder) startStreamDecoder(inStream chan decodeStream) { br := readerWrapper{r: stream.r} decodeStream: for { + frame.history.reset() err := frame.reset(&br) if debug && err != nil { println("Frame decoder returned", err) diff --git a/vendor/github.com/klauspost/compress/zstd/seqdec.go b/vendor/github.com/klauspost/compress/zstd/seqdec.go index 15a45f7b5..39238e16a 100644 --- a/vendor/github.com/klauspost/compress/zstd/seqdec.go +++ b/vendor/github.com/klauspost/compress/zstd/seqdec.go @@ -64,6 +64,7 @@ type sequenceDecs struct { hist []byte literals []byte out []byte + windowSize int maxBits uint8 } @@ -82,6 +83,7 @@ func (s *sequenceDecs) initialize(br *bitReader, hist *history, literals, out [] s.hist = hist.b s.prevOffset = hist.recentOffsets s.maxBits = s.litLengths.fse.maxBits + s.offsets.fse.maxBits + s.matchLengths.fse.maxBits + s.windowSize = hist.windowSize s.out = out return nil } @@ -131,6 +133,9 @@ func (s *sequenceDecs) decode(seqs int, br *bitReader, hist []byte) error { if matchOff > len(s.out)+len(hist)+litLen { return fmt.Errorf("match offset (%d) bigger than current history (%d)", matchOff, len(s.out)+len(hist)+litLen) } + if matchOff > s.windowSize { + return fmt.Errorf("match offset (%d) bigger than window size (%d)", matchOff, s.windowSize) + } if matchOff == 0 && matchLen > 0 { return fmt.Errorf("zero matchoff and matchlen > 0") } diff --git a/vendor/github.com/mattn/go-isatty/.travis.yml b/vendor/github.com/mattn/go-isatty/.travis.yml new file mode 100644 index 000000000..604314dd4 --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/.travis.yml @@ -0,0 +1,14 @@ +language: go +sudo: false +go: + - 1.13.x + - tip + +before_install: + - go get -t -v ./... + +script: + - ./go.test.sh + +after_success: + - bash <(curl -s https://codecov.io/bash) diff --git a/vendor/github.com/mattn/go-isatty/LICENSE b/vendor/github.com/mattn/go-isatty/LICENSE new file mode 100644 index 000000000..65dc692b6 --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/LICENSE @@ -0,0 +1,9 @@ +Copyright (c) Yasuhiro MATSUMOTO <mattn.jp@gmail.com> + +MIT License (Expat) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/mattn/go-isatty/README.md b/vendor/github.com/mattn/go-isatty/README.md new file mode 100644 index 000000000..38418353e --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/README.md @@ -0,0 +1,50 @@ +# go-isatty + +[![Godoc Reference](https://godoc.org/github.com/mattn/go-isatty?status.svg)](http://godoc.org/github.com/mattn/go-isatty) +[![Codecov](https://codecov.io/gh/mattn/go-isatty/branch/master/graph/badge.svg)](https://codecov.io/gh/mattn/go-isatty) +[![Coverage Status](https://coveralls.io/repos/github/mattn/go-isatty/badge.svg?branch=master)](https://coveralls.io/github/mattn/go-isatty?branch=master) +[![Go Report Card](https://goreportcard.com/badge/mattn/go-isatty)](https://goreportcard.com/report/mattn/go-isatty) + +isatty for golang + +## Usage + +```go +package main + +import ( + "fmt" + "github.com/mattn/go-isatty" + "os" +) + +func main() { + if isatty.IsTerminal(os.Stdout.Fd()) { + fmt.Println("Is Terminal") + } else if isatty.IsCygwinTerminal(os.Stdout.Fd()) { + fmt.Println("Is Cygwin/MSYS2 Terminal") + } else { + fmt.Println("Is Not Terminal") + } +} +``` + +## Installation + +``` +$ go get github.com/mattn/go-isatty +``` + +## License + +MIT + +## Author + +Yasuhiro Matsumoto (a.k.a mattn) + +## Thanks + +* k-takata: base idea for IsCygwinTerminal + + https://github.com/k-takata/go-iscygpty diff --git a/vendor/github.com/mattn/go-isatty/doc.go b/vendor/github.com/mattn/go-isatty/doc.go new file mode 100644 index 000000000..17d4f90eb --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/doc.go @@ -0,0 +1,2 @@ +// Package isatty implements interface to isatty +package isatty diff --git a/vendor/github.com/mattn/go-isatty/go.mod b/vendor/github.com/mattn/go-isatty/go.mod new file mode 100644 index 000000000..605c4c221 --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/go.mod @@ -0,0 +1,5 @@ +module github.com/mattn/go-isatty + +go 1.12 + +require golang.org/x/sys v0.0.0-20200116001909-b77594299b42 diff --git a/vendor/github.com/mattn/go-isatty/go.sum b/vendor/github.com/mattn/go-isatty/go.sum new file mode 100644 index 000000000..912e29cbc --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/go.sum @@ -0,0 +1,2 @@ +golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/vendor/github.com/mattn/go-isatty/go.test.sh b/vendor/github.com/mattn/go-isatty/go.test.sh new file mode 100644 index 000000000..012162b07 --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/go.test.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +set -e +echo "" > coverage.txt + +for d in $(go list ./... | grep -v vendor); do + go test -race -coverprofile=profile.out -covermode=atomic "$d" + if [ -f profile.out ]; then + cat profile.out >> coverage.txt + rm profile.out + fi +done diff --git a/vendor/github.com/mattn/go-isatty/isatty_bsd.go b/vendor/github.com/mattn/go-isatty/isatty_bsd.go new file mode 100644 index 000000000..711f28808 --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/isatty_bsd.go @@ -0,0 +1,18 @@ +// +build darwin freebsd openbsd netbsd dragonfly +// +build !appengine + +package isatty + +import "golang.org/x/sys/unix" + +// IsTerminal return true if the file descriptor is terminal. +func IsTerminal(fd uintptr) bool { + _, err := unix.IoctlGetTermios(int(fd), unix.TIOCGETA) + return err == nil +} + +// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2 +// terminal. This is also always false on this environment. +func IsCygwinTerminal(fd uintptr) bool { + return false +} diff --git a/vendor/github.com/mattn/go-isatty/isatty_others.go b/vendor/github.com/mattn/go-isatty/isatty_others.go new file mode 100644 index 000000000..ff714a376 --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/isatty_others.go @@ -0,0 +1,15 @@ +// +build appengine js nacl + +package isatty + +// IsTerminal returns true if the file descriptor is terminal which +// is always false on js and appengine classic which is a sandboxed PaaS. +func IsTerminal(fd uintptr) bool { + return false +} + +// IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2 +// terminal. This is also always false on this environment. +func IsCygwinTerminal(fd uintptr) bool { + return false +} diff --git a/vendor/github.com/mattn/go-isatty/isatty_plan9.go b/vendor/github.com/mattn/go-isatty/isatty_plan9.go new file mode 100644 index 000000000..c5b6e0c08 --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/isatty_plan9.go @@ -0,0 +1,22 @@ +// +build plan9 + +package isatty + +import ( + "syscall" +) + +// IsTerminal returns true if the given file descriptor is a terminal. +func IsTerminal(fd uintptr) bool { + path, err := syscall.Fd2path(int(fd)) + if err != nil { + return false + } + return path == "/dev/cons" || path == "/mnt/term/dev/cons" +} + +// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2 +// terminal. This is also always false on this environment. +func IsCygwinTerminal(fd uintptr) bool { + return false +} diff --git a/vendor/github.com/mattn/go-isatty/isatty_solaris.go b/vendor/github.com/mattn/go-isatty/isatty_solaris.go new file mode 100644 index 000000000..bdd5c79a0 --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/isatty_solaris.go @@ -0,0 +1,22 @@ +// +build solaris +// +build !appengine + +package isatty + +import ( + "golang.org/x/sys/unix" +) + +// IsTerminal returns true if the given file descriptor is a terminal. +// see: http://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libbc/libc/gen/common/isatty.c +func IsTerminal(fd uintptr) bool { + var termio unix.Termio + err := unix.IoctlSetTermio(int(fd), unix.TCGETA, &termio) + return err == nil +} + +// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2 +// terminal. This is also always false on this environment. +func IsCygwinTerminal(fd uintptr) bool { + return false +} diff --git a/vendor/github.com/mattn/go-isatty/isatty_tcgets.go b/vendor/github.com/mattn/go-isatty/isatty_tcgets.go new file mode 100644 index 000000000..31a1ca973 --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/isatty_tcgets.go @@ -0,0 +1,18 @@ +// +build linux aix +// +build !appengine + +package isatty + +import "golang.org/x/sys/unix" + +// IsTerminal return true if the file descriptor is terminal. +func IsTerminal(fd uintptr) bool { + _, err := unix.IoctlGetTermios(int(fd), unix.TCGETS) + return err == nil +} + +// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2 +// terminal. This is also always false on this environment. +func IsCygwinTerminal(fd uintptr) bool { + return false +} diff --git a/vendor/github.com/mattn/go-isatty/isatty_windows.go b/vendor/github.com/mattn/go-isatty/isatty_windows.go new file mode 100644 index 000000000..1fa869154 --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/isatty_windows.go @@ -0,0 +1,125 @@ +// +build windows +// +build !appengine + +package isatty + +import ( + "errors" + "strings" + "syscall" + "unicode/utf16" + "unsafe" +) + +const ( + objectNameInfo uintptr = 1 + fileNameInfo = 2 + fileTypePipe = 3 +) + +var ( + kernel32 = syscall.NewLazyDLL("kernel32.dll") + ntdll = syscall.NewLazyDLL("ntdll.dll") + procGetConsoleMode = kernel32.NewProc("GetConsoleMode") + procGetFileInformationByHandleEx = kernel32.NewProc("GetFileInformationByHandleEx") + procGetFileType = kernel32.NewProc("GetFileType") + procNtQueryObject = ntdll.NewProc("NtQueryObject") +) + +func init() { + // Check if GetFileInformationByHandleEx is available. + if procGetFileInformationByHandleEx.Find() != nil { + procGetFileInformationByHandleEx = nil + } +} + +// IsTerminal return true if the file descriptor is terminal. +func IsTerminal(fd uintptr) bool { + var st uint32 + r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, fd, uintptr(unsafe.Pointer(&st)), 0) + return r != 0 && e == 0 +} + +// Check pipe name is used for cygwin/msys2 pty. +// Cygwin/MSYS2 PTY has a name like: +// \{cygwin,msys}-XXXXXXXXXXXXXXXX-ptyN-{from,to}-master +func isCygwinPipeName(name string) bool { + token := strings.Split(name, "-") + if len(token) < 5 { + return false + } + + if token[0] != `\msys` && + token[0] != `\cygwin` && + token[0] != `\Device\NamedPipe\msys` && + token[0] != `\Device\NamedPipe\cygwin` { + return false + } + + if token[1] == "" { + return false + } + + if !strings.HasPrefix(token[2], "pty") { + return false + } + + if token[3] != `from` && token[3] != `to` { + return false + } + + if token[4] != "master" { + return false + } + + return true +} + +// getFileNameByHandle use the undocomented ntdll NtQueryObject to get file full name from file handler +// since GetFileInformationByHandleEx is not avilable under windows Vista and still some old fashion +// guys are using Windows XP, this is a workaround for those guys, it will also work on system from +// Windows vista to 10 +// see https://stackoverflow.com/a/18792477 for details +func getFileNameByHandle(fd uintptr) (string, error) { + if procNtQueryObject == nil { + return "", errors.New("ntdll.dll: NtQueryObject not supported") + } + + var buf [4 + syscall.MAX_PATH]uint16 + var result int + r, _, e := syscall.Syscall6(procNtQueryObject.Addr(), 5, + fd, objectNameInfo, uintptr(unsafe.Pointer(&buf)), uintptr(2*len(buf)), uintptr(unsafe.Pointer(&result)), 0) + if r != 0 { + return "", e + } + return string(utf16.Decode(buf[4 : 4+buf[0]/2])), nil +} + +// IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2 +// terminal. +func IsCygwinTerminal(fd uintptr) bool { + if procGetFileInformationByHandleEx == nil { + name, err := getFileNameByHandle(fd) + if err != nil { + return false + } + return isCygwinPipeName(name) + } + + // Cygwin/msys's pty is a pipe. + ft, _, e := syscall.Syscall(procGetFileType.Addr(), 1, fd, 0, 0) + if ft != fileTypePipe || e != 0 { + return false + } + + var buf [2 + syscall.MAX_PATH]uint16 + r, _, e := syscall.Syscall6(procGetFileInformationByHandleEx.Addr(), + 4, fd, fileNameInfo, uintptr(unsafe.Pointer(&buf)), + uintptr(len(buf)*2), 0, 0) + if r == 0 || e != 0 { + return false + } + + l := *(*uint32)(unsafe.Pointer(&buf)) + return isCygwinPipeName(string(utf16.Decode(buf[2 : 2+l/2]))) +} diff --git a/vendor/github.com/mattn/go-isatty/renovate.json b/vendor/github.com/mattn/go-isatty/renovate.json new file mode 100644 index 000000000..5ae9d96b7 --- /dev/null +++ b/vendor/github.com/mattn/go-isatty/renovate.json @@ -0,0 +1,8 @@ +{ + "extends": [ + "config:base" + ], + "postUpdateOptions": [ + "gomodTidy" + ] +} diff --git a/vendor/github.com/mattn/go-runewidth/.travis.yml b/vendor/github.com/mattn/go-runewidth/.travis.yml new file mode 100644 index 000000000..6a21813a3 --- /dev/null +++ b/vendor/github.com/mattn/go-runewidth/.travis.yml @@ -0,0 +1,16 @@ +language: go +sudo: false +go: + - 1.13.x + - tip + +before_install: + - go get -t -v ./... + +script: + - go generate + - git diff --cached --exit-code + - ./go.test.sh + +after_success: + - bash <(curl -s https://codecov.io/bash) diff --git a/vendor/github.com/mattn/go-runewidth/LICENSE b/vendor/github.com/mattn/go-runewidth/LICENSE new file mode 100644 index 000000000..91b5cef30 --- /dev/null +++ b/vendor/github.com/mattn/go-runewidth/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Yasuhiro Matsumoto + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/mattn/go-runewidth/README.md b/vendor/github.com/mattn/go-runewidth/README.md new file mode 100644 index 000000000..aa56ab96c --- /dev/null +++ b/vendor/github.com/mattn/go-runewidth/README.md @@ -0,0 +1,27 @@ +go-runewidth +============ + +[![Build Status](https://travis-ci.org/mattn/go-runewidth.png?branch=master)](https://travis-ci.org/mattn/go-runewidth) +[![Codecov](https://codecov.io/gh/mattn/go-runewidth/branch/master/graph/badge.svg)](https://codecov.io/gh/mattn/go-runewidth) +[![GoDoc](https://godoc.org/github.com/mattn/go-runewidth?status.svg)](http://godoc.org/github.com/mattn/go-runewidth) +[![Go Report Card](https://goreportcard.com/badge/github.com/mattn/go-runewidth)](https://goreportcard.com/report/github.com/mattn/go-runewidth) + +Provides functions to get fixed width of the character or string. + +Usage +----- + +```go +runewidth.StringWidth("つのだ☆HIRO") == 12 +``` + + +Author +------ + +Yasuhiro Matsumoto + +License +------- + +under the MIT License: http://mattn.mit-license.org/2013 diff --git a/vendor/github.com/mattn/go-runewidth/go.mod b/vendor/github.com/mattn/go-runewidth/go.mod new file mode 100644 index 000000000..fa7f4d864 --- /dev/null +++ b/vendor/github.com/mattn/go-runewidth/go.mod @@ -0,0 +1,3 @@ +module github.com/mattn/go-runewidth + +go 1.9 diff --git a/vendor/github.com/mattn/go-runewidth/go.test.sh b/vendor/github.com/mattn/go-runewidth/go.test.sh new file mode 100644 index 000000000..012162b07 --- /dev/null +++ b/vendor/github.com/mattn/go-runewidth/go.test.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +set -e +echo "" > coverage.txt + +for d in $(go list ./... | grep -v vendor); do + go test -race -coverprofile=profile.out -covermode=atomic "$d" + if [ -f profile.out ]; then + cat profile.out >> coverage.txt + rm profile.out + fi +done diff --git a/vendor/github.com/mattn/go-runewidth/runewidth.go b/vendor/github.com/mattn/go-runewidth/runewidth.go new file mode 100644 index 000000000..19f8e0449 --- /dev/null +++ b/vendor/github.com/mattn/go-runewidth/runewidth.go @@ -0,0 +1,257 @@ +package runewidth + +import ( + "os" +) + +//go:generate go run script/generate.go + +var ( + // EastAsianWidth will be set true if the current locale is CJK + EastAsianWidth bool + + // ZeroWidthJoiner is flag to set to use UTR#51 ZWJ + ZeroWidthJoiner bool + + // DefaultCondition is a condition in current locale + DefaultCondition = &Condition{} +) + +func init() { + handleEnv() +} + +func handleEnv() { + env := os.Getenv("RUNEWIDTH_EASTASIAN") + if env == "" { + EastAsianWidth = IsEastAsian() + } else { + EastAsianWidth = env == "1" + } + // update DefaultCondition + DefaultCondition.EastAsianWidth = EastAsianWidth + DefaultCondition.ZeroWidthJoiner = ZeroWidthJoiner +} + +type interval struct { + first rune + last rune +} + +type table []interval + +func inTables(r rune, ts ...table) bool { + for _, t := range ts { + if inTable(r, t) { + return true + } + } + return false +} + +func inTable(r rune, t table) bool { + if r < t[0].first { + return false + } + + bot := 0 + top := len(t) - 1 + for top >= bot { + mid := (bot + top) >> 1 + + switch { + case t[mid].last < r: + bot = mid + 1 + case t[mid].first > r: + top = mid - 1 + default: + return true + } + } + + return false +} + +var private = table{ + {0x00E000, 0x00F8FF}, {0x0F0000, 0x0FFFFD}, {0x100000, 0x10FFFD}, +} + +var nonprint = table{ + {0x0000, 0x001F}, {0x007F, 0x009F}, {0x00AD, 0x00AD}, + {0x070F, 0x070F}, {0x180B, 0x180E}, {0x200B, 0x200F}, + {0x2028, 0x202E}, {0x206A, 0x206F}, {0xD800, 0xDFFF}, + {0xFEFF, 0xFEFF}, {0xFFF9, 0xFFFB}, {0xFFFE, 0xFFFF}, +} + +// Condition have flag EastAsianWidth whether the current locale is CJK or not. +type Condition struct { + EastAsianWidth bool + ZeroWidthJoiner bool +} + +// NewCondition return new instance of Condition which is current locale. +func NewCondition() *Condition { + return &Condition{ + EastAsianWidth: EastAsianWidth, + ZeroWidthJoiner: ZeroWidthJoiner, + } +} + +// RuneWidth returns the number of cells in r. +// See http://www.unicode.org/reports/tr11/ +func (c *Condition) RuneWidth(r rune) int { + switch { + case r < 0 || r > 0x10FFFF || inTables(r, nonprint, combining, notassigned): + return 0 + case (c.EastAsianWidth && IsAmbiguousWidth(r)) || inTables(r, doublewidth): + return 2 + default: + return 1 + } +} + +func (c *Condition) stringWidth(s string) (width int) { + for _, r := range []rune(s) { + width += c.RuneWidth(r) + } + return width +} + +func (c *Condition) stringWidthZeroJoiner(s string) (width int) { + r1, r2 := rune(0), rune(0) + for _, r := range []rune(s) { + if r == 0xFE0E || r == 0xFE0F { + continue + } + w := c.RuneWidth(r) + if r2 == 0x200D && inTables(r, emoji) && inTables(r1, emoji) { + if width < w { + width = w + } + } else { + width += w + } + r1, r2 = r2, r + } + return width +} + +// StringWidth return width as you can see +func (c *Condition) StringWidth(s string) (width int) { + if c.ZeroWidthJoiner { + return c.stringWidthZeroJoiner(s) + } + return c.stringWidth(s) +} + +// Truncate return string truncated with w cells +func (c *Condition) Truncate(s string, w int, tail string) string { + if c.StringWidth(s) <= w { + return s + } + r := []rune(s) + tw := c.StringWidth(tail) + w -= tw + width := 0 + i := 0 + for ; i < len(r); i++ { + cw := c.RuneWidth(r[i]) + if width+cw > w { + break + } + width += cw + } + return string(r[0:i]) + tail +} + +// Wrap return string wrapped with w cells +func (c *Condition) Wrap(s string, w int) string { + width := 0 + out := "" + for _, r := range []rune(s) { + cw := RuneWidth(r) + if r == '\n' { + out += string(r) + width = 0 + continue + } else if width+cw > w { + out += "\n" + width = 0 + out += string(r) + width += cw + continue + } + out += string(r) + width += cw + } + return out +} + +// FillLeft return string filled in left by spaces in w cells +func (c *Condition) FillLeft(s string, w int) string { + width := c.StringWidth(s) + count := w - width + if count > 0 { + b := make([]byte, count) + for i := range b { + b[i] = ' ' + } + return string(b) + s + } + return s +} + +// FillRight return string filled in left by spaces in w cells +func (c *Condition) FillRight(s string, w int) string { + width := c.StringWidth(s) + count := w - width + if count > 0 { + b := make([]byte, count) + for i := range b { + b[i] = ' ' + } + return s + string(b) + } + return s +} + +// RuneWidth returns the number of cells in r. +// See http://www.unicode.org/reports/tr11/ +func RuneWidth(r rune) int { + return DefaultCondition.RuneWidth(r) +} + +// IsAmbiguousWidth returns whether is ambiguous width or not. +func IsAmbiguousWidth(r rune) bool { + return inTables(r, private, ambiguous) +} + +// IsNeutralWidth returns whether is neutral width or not. +func IsNeutralWidth(r rune) bool { + return inTable(r, neutral) +} + +// StringWidth return width as you can see +func StringWidth(s string) (width int) { + return DefaultCondition.StringWidth(s) +} + +// Truncate return string truncated with w cells +func Truncate(s string, w int, tail string) string { + return DefaultCondition.Truncate(s, w, tail) +} + +// Wrap return string wrapped with w cells +func Wrap(s string, w int) string { + return DefaultCondition.Wrap(s, w) +} + +// FillLeft return string filled in left by spaces in w cells +func FillLeft(s string, w int) string { + return DefaultCondition.FillLeft(s, w) +} + +// FillRight return string filled in left by spaces in w cells +func FillRight(s string, w int) string { + return DefaultCondition.FillRight(s, w) +} diff --git a/vendor/github.com/mattn/go-runewidth/runewidth_appengine.go b/vendor/github.com/mattn/go-runewidth/runewidth_appengine.go new file mode 100644 index 000000000..7d99f6e52 --- /dev/null +++ b/vendor/github.com/mattn/go-runewidth/runewidth_appengine.go @@ -0,0 +1,8 @@ +// +build appengine + +package runewidth + +// IsEastAsian return true if the current locale is CJK +func IsEastAsian() bool { + return false +} diff --git a/vendor/github.com/mattn/go-runewidth/runewidth_js.go b/vendor/github.com/mattn/go-runewidth/runewidth_js.go new file mode 100644 index 000000000..c5fdf40ba --- /dev/null +++ b/vendor/github.com/mattn/go-runewidth/runewidth_js.go @@ -0,0 +1,9 @@ +// +build js +// +build !appengine + +package runewidth + +func IsEastAsian() bool { + // TODO: Implement this for the web. Detect east asian in a compatible way, and return true. + return false +} diff --git a/vendor/github.com/mattn/go-runewidth/runewidth_posix.go b/vendor/github.com/mattn/go-runewidth/runewidth_posix.go new file mode 100644 index 000000000..480ad7485 --- /dev/null +++ b/vendor/github.com/mattn/go-runewidth/runewidth_posix.go @@ -0,0 +1,82 @@ +// +build !windows +// +build !js +// +build !appengine + +package runewidth + +import ( + "os" + "regexp" + "strings" +) + +var reLoc = regexp.MustCompile(`^[a-z][a-z][a-z]?(?:_[A-Z][A-Z])?\.(.+)`) + +var mblenTable = map[string]int{ + "utf-8": 6, + "utf8": 6, + "jis": 8, + "eucjp": 3, + "euckr": 2, + "euccn": 2, + "sjis": 2, + "cp932": 2, + "cp51932": 2, + "cp936": 2, + "cp949": 2, + "cp950": 2, + "big5": 2, + "gbk": 2, + "gb2312": 2, +} + +func isEastAsian(locale string) bool { + charset := strings.ToLower(locale) + r := reLoc.FindStringSubmatch(locale) + if len(r) == 2 { + charset = strings.ToLower(r[1]) + } + + if strings.HasSuffix(charset, "@cjk_narrow") { + return false + } + + for pos, b := range []byte(charset) { + if b == '@' { + charset = charset[:pos] + break + } + } + max := 1 + if m, ok := mblenTable[charset]; ok { + max = m + } + if max > 1 && (charset[0] != 'u' || + strings.HasPrefix(locale, "ja") || + strings.HasPrefix(locale, "ko") || + strings.HasPrefix(locale, "zh")) { + return true + } + return false +} + +// IsEastAsian return true if the current locale is CJK +func IsEastAsian() bool { + locale := os.Getenv("LC_ALL") + if locale == "" { + locale = os.Getenv("LC_CTYPE") + } + if locale == "" { + locale = os.Getenv("LANG") + } + + // ignore C locale + if locale == "POSIX" || locale == "C" { + return false + } + if len(locale) > 1 && locale[0] == 'C' && (locale[1] == '.' || locale[1] == '-') { + return false + } + + return isEastAsian(locale) +} diff --git a/vendor/github.com/mattn/go-runewidth/runewidth_table.go b/vendor/github.com/mattn/go-runewidth/runewidth_table.go new file mode 100644 index 000000000..b27d77d89 --- /dev/null +++ b/vendor/github.com/mattn/go-runewidth/runewidth_table.go @@ -0,0 +1,437 @@ +// Code generated by script/generate.go. DO NOT EDIT. + +package runewidth + +var combining = table{ + {0x0300, 0x036F}, {0x0483, 0x0489}, {0x07EB, 0x07F3}, + {0x0C00, 0x0C00}, {0x0C04, 0x0C04}, {0x0D00, 0x0D01}, + {0x135D, 0x135F}, {0x1A7F, 0x1A7F}, {0x1AB0, 0x1AC0}, + {0x1B6B, 0x1B73}, {0x1DC0, 0x1DF9}, {0x1DFB, 0x1DFF}, + {0x20D0, 0x20F0}, {0x2CEF, 0x2CF1}, {0x2DE0, 0x2DFF}, + {0x3099, 0x309A}, {0xA66F, 0xA672}, {0xA674, 0xA67D}, + {0xA69E, 0xA69F}, {0xA6F0, 0xA6F1}, {0xA8E0, 0xA8F1}, + {0xFE20, 0xFE2F}, {0x101FD, 0x101FD}, {0x10376, 0x1037A}, + {0x10EAB, 0x10EAC}, {0x10F46, 0x10F50}, {0x11300, 0x11301}, + {0x1133B, 0x1133C}, {0x11366, 0x1136C}, {0x11370, 0x11374}, + {0x16AF0, 0x16AF4}, {0x1D165, 0x1D169}, {0x1D16D, 0x1D172}, + {0x1D17B, 0x1D182}, {0x1D185, 0x1D18B}, {0x1D1AA, 0x1D1AD}, + {0x1D242, 0x1D244}, {0x1E000, 0x1E006}, {0x1E008, 0x1E018}, + {0x1E01B, 0x1E021}, {0x1E023, 0x1E024}, {0x1E026, 0x1E02A}, + {0x1E8D0, 0x1E8D6}, +} + +var doublewidth = table{ + {0x1100, 0x115F}, {0x231A, 0x231B}, {0x2329, 0x232A}, + {0x23E9, 0x23EC}, {0x23F0, 0x23F0}, {0x23F3, 0x23F3}, + {0x25FD, 0x25FE}, {0x2614, 0x2615}, {0x2648, 0x2653}, + {0x267F, 0x267F}, {0x2693, 0x2693}, {0x26A1, 0x26A1}, + {0x26AA, 0x26AB}, {0x26BD, 0x26BE}, {0x26C4, 0x26C5}, + {0x26CE, 0x26CE}, {0x26D4, 0x26D4}, {0x26EA, 0x26EA}, + {0x26F2, 0x26F3}, {0x26F5, 0x26F5}, {0x26FA, 0x26FA}, + {0x26FD, 0x26FD}, {0x2705, 0x2705}, {0x270A, 0x270B}, + {0x2728, 0x2728}, {0x274C, 0x274C}, {0x274E, 0x274E}, + {0x2753, 0x2755}, {0x2757, 0x2757}, {0x2795, 0x2797}, + {0x27B0, 0x27B0}, {0x27BF, 0x27BF}, {0x2B1B, 0x2B1C}, + {0x2B50, 0x2B50}, {0x2B55, 0x2B55}, {0x2E80, 0x2E99}, + {0x2E9B, 0x2EF3}, {0x2F00, 0x2FD5}, {0x2FF0, 0x2FFB}, + {0x3000, 0x303E}, {0x3041, 0x3096}, {0x3099, 0x30FF}, + {0x3105, 0x312F}, {0x3131, 0x318E}, {0x3190, 0x31E3}, + {0x31F0, 0x321E}, {0x3220, 0x3247}, {0x3250, 0x4DBF}, + {0x4E00, 0xA48C}, {0xA490, 0xA4C6}, {0xA960, 0xA97C}, + {0xAC00, 0xD7A3}, {0xF900, 0xFAFF}, {0xFE10, 0xFE19}, + {0xFE30, 0xFE52}, {0xFE54, 0xFE66}, {0xFE68, 0xFE6B}, + {0xFF01, 0xFF60}, {0xFFE0, 0xFFE6}, {0x16FE0, 0x16FE4}, + {0x16FF0, 0x16FF1}, {0x17000, 0x187F7}, {0x18800, 0x18CD5}, + {0x18D00, 0x18D08}, {0x1B000, 0x1B11E}, {0x1B150, 0x1B152}, + {0x1B164, 0x1B167}, {0x1B170, 0x1B2FB}, {0x1F004, 0x1F004}, + {0x1F0CF, 0x1F0CF}, {0x1F18E, 0x1F18E}, {0x1F191, 0x1F19A}, + {0x1F200, 0x1F202}, {0x1F210, 0x1F23B}, {0x1F240, 0x1F248}, + {0x1F250, 0x1F251}, {0x1F260, 0x1F265}, {0x1F300, 0x1F320}, + {0x1F32D, 0x1F335}, {0x1F337, 0x1F37C}, {0x1F37E, 0x1F393}, + {0x1F3A0, 0x1F3CA}, {0x1F3CF, 0x1F3D3}, {0x1F3E0, 0x1F3F0}, + {0x1F3F4, 0x1F3F4}, {0x1F3F8, 0x1F43E}, {0x1F440, 0x1F440}, + {0x1F442, 0x1F4FC}, {0x1F4FF, 0x1F53D}, {0x1F54B, 0x1F54E}, + {0x1F550, 0x1F567}, {0x1F57A, 0x1F57A}, {0x1F595, 0x1F596}, + {0x1F5A4, 0x1F5A4}, {0x1F5FB, 0x1F64F}, {0x1F680, 0x1F6C5}, + {0x1F6CC, 0x1F6CC}, {0x1F6D0, 0x1F6D2}, {0x1F6D5, 0x1F6D7}, + {0x1F6EB, 0x1F6EC}, {0x1F6F4, 0x1F6FC}, {0x1F7E0, 0x1F7EB}, + {0x1F90C, 0x1F93A}, {0x1F93C, 0x1F945}, {0x1F947, 0x1F978}, + {0x1F97A, 0x1F9CB}, {0x1F9CD, 0x1F9FF}, {0x1FA70, 0x1FA74}, + {0x1FA78, 0x1FA7A}, {0x1FA80, 0x1FA86}, {0x1FA90, 0x1FAA8}, + {0x1FAB0, 0x1FAB6}, {0x1FAC0, 0x1FAC2}, {0x1FAD0, 0x1FAD6}, + {0x20000, 0x2FFFD}, {0x30000, 0x3FFFD}, +} + +var ambiguous = table{ + {0x00A1, 0x00A1}, {0x00A4, 0x00A4}, {0x00A7, 0x00A8}, + {0x00AA, 0x00AA}, {0x00AD, 0x00AE}, {0x00B0, 0x00B4}, + {0x00B6, 0x00BA}, {0x00BC, 0x00BF}, {0x00C6, 0x00C6}, + {0x00D0, 0x00D0}, {0x00D7, 0x00D8}, {0x00DE, 0x00E1}, + {0x00E6, 0x00E6}, {0x00E8, 0x00EA}, {0x00EC, 0x00ED}, + {0x00F0, 0x00F0}, {0x00F2, 0x00F3}, {0x00F7, 0x00FA}, + {0x00FC, 0x00FC}, {0x00FE, 0x00FE}, {0x0101, 0x0101}, + {0x0111, 0x0111}, {0x0113, 0x0113}, {0x011B, 0x011B}, + {0x0126, 0x0127}, {0x012B, 0x012B}, {0x0131, 0x0133}, + {0x0138, 0x0138}, {0x013F, 0x0142}, {0x0144, 0x0144}, + {0x0148, 0x014B}, {0x014D, 0x014D}, {0x0152, 0x0153}, + {0x0166, 0x0167}, {0x016B, 0x016B}, {0x01CE, 0x01CE}, + {0x01D0, 0x01D0}, {0x01D2, 0x01D2}, {0x01D4, 0x01D4}, + {0x01D6, 0x01D6}, {0x01D8, 0x01D8}, {0x01DA, 0x01DA}, + {0x01DC, 0x01DC}, {0x0251, 0x0251}, {0x0261, 0x0261}, + {0x02C4, 0x02C4}, {0x02C7, 0x02C7}, {0x02C9, 0x02CB}, + {0x02CD, 0x02CD}, {0x02D0, 0x02D0}, {0x02D8, 0x02DB}, + {0x02DD, 0x02DD}, {0x02DF, 0x02DF}, {0x0300, 0x036F}, + {0x0391, 0x03A1}, {0x03A3, 0x03A9}, {0x03B1, 0x03C1}, + {0x03C3, 0x03C9}, {0x0401, 0x0401}, {0x0410, 0x044F}, + {0x0451, 0x0451}, {0x2010, 0x2010}, {0x2013, 0x2016}, + {0x2018, 0x2019}, {0x201C, 0x201D}, {0x2020, 0x2022}, + {0x2024, 0x2027}, {0x2030, 0x2030}, {0x2032, 0x2033}, + {0x2035, 0x2035}, {0x203B, 0x203B}, {0x203E, 0x203E}, + {0x2074, 0x2074}, {0x207F, 0x207F}, {0x2081, 0x2084}, + {0x20AC, 0x20AC}, {0x2103, 0x2103}, {0x2105, 0x2105}, + {0x2109, 0x2109}, {0x2113, 0x2113}, {0x2116, 0x2116}, + {0x2121, 0x2122}, {0x2126, 0x2126}, {0x212B, 0x212B}, + {0x2153, 0x2154}, {0x215B, 0x215E}, {0x2160, 0x216B}, + {0x2170, 0x2179}, {0x2189, 0x2189}, {0x2190, 0x2199}, + {0x21B8, 0x21B9}, {0x21D2, 0x21D2}, {0x21D4, 0x21D4}, + {0x21E7, 0x21E7}, {0x2200, 0x2200}, {0x2202, 0x2203}, + {0x2207, 0x2208}, {0x220B, 0x220B}, {0x220F, 0x220F}, + {0x2211, 0x2211}, {0x2215, 0x2215}, {0x221A, 0x221A}, + {0x221D, 0x2220}, {0x2223, 0x2223}, {0x2225, 0x2225}, + {0x2227, 0x222C}, {0x222E, 0x222E}, {0x2234, 0x2237}, + {0x223C, 0x223D}, {0x2248, 0x2248}, {0x224C, 0x224C}, + {0x2252, 0x2252}, {0x2260, 0x2261}, {0x2264, 0x2267}, + {0x226A, 0x226B}, {0x226E, 0x226F}, {0x2282, 0x2283}, + {0x2286, 0x2287}, {0x2295, 0x2295}, {0x2299, 0x2299}, + {0x22A5, 0x22A5}, {0x22BF, 0x22BF}, {0x2312, 0x2312}, + {0x2460, 0x24E9}, {0x24EB, 0x254B}, {0x2550, 0x2573}, + {0x2580, 0x258F}, {0x2592, 0x2595}, {0x25A0, 0x25A1}, + {0x25A3, 0x25A9}, {0x25B2, 0x25B3}, {0x25B6, 0x25B7}, + {0x25BC, 0x25BD}, {0x25C0, 0x25C1}, {0x25C6, 0x25C8}, + {0x25CB, 0x25CB}, {0x25CE, 0x25D1}, {0x25E2, 0x25E5}, + {0x25EF, 0x25EF}, {0x2605, 0x2606}, {0x2609, 0x2609}, + {0x260E, 0x260F}, {0x261C, 0x261C}, {0x261E, 0x261E}, + {0x2640, 0x2640}, {0x2642, 0x2642}, {0x2660, 0x2661}, + {0x2663, 0x2665}, {0x2667, 0x266A}, {0x266C, 0x266D}, + {0x266F, 0x266F}, {0x269E, 0x269F}, {0x26BF, 0x26BF}, + {0x26C6, 0x26CD}, {0x26CF, 0x26D3}, {0x26D5, 0x26E1}, + {0x26E3, 0x26E3}, {0x26E8, 0x26E9}, {0x26EB, 0x26F1}, + {0x26F4, 0x26F4}, {0x26F6, 0x26F9}, {0x26FB, 0x26FC}, + {0x26FE, 0x26FF}, {0x273D, 0x273D}, {0x2776, 0x277F}, + {0x2B56, 0x2B59}, {0x3248, 0x324F}, {0xE000, 0xF8FF}, + {0xFE00, 0xFE0F}, {0xFFFD, 0xFFFD}, {0x1F100, 0x1F10A}, + {0x1F110, 0x1F12D}, {0x1F130, 0x1F169}, {0x1F170, 0x1F18D}, + {0x1F18F, 0x1F190}, {0x1F19B, 0x1F1AC}, {0xE0100, 0xE01EF}, + {0xF0000, 0xFFFFD}, {0x100000, 0x10FFFD}, +} +var notassigned = table{ + {0x27E6, 0x27ED}, {0x2985, 0x2986}, +} + +var neutral = table{ + {0x0000, 0x001F}, {0x007F, 0x00A0}, {0x00A9, 0x00A9}, + {0x00AB, 0x00AB}, {0x00B5, 0x00B5}, {0x00BB, 0x00BB}, + {0x00C0, 0x00C5}, {0x00C7, 0x00CF}, {0x00D1, 0x00D6}, + {0x00D9, 0x00DD}, {0x00E2, 0x00E5}, {0x00E7, 0x00E7}, + {0x00EB, 0x00EB}, {0x00EE, 0x00EF}, {0x00F1, 0x00F1}, + {0x00F4, 0x00F6}, {0x00FB, 0x00FB}, {0x00FD, 0x00FD}, + {0x00FF, 0x0100}, {0x0102, 0x0110}, {0x0112, 0x0112}, + {0x0114, 0x011A}, {0x011C, 0x0125}, {0x0128, 0x012A}, + {0x012C, 0x0130}, {0x0134, 0x0137}, {0x0139, 0x013E}, + {0x0143, 0x0143}, {0x0145, 0x0147}, {0x014C, 0x014C}, + {0x014E, 0x0151}, {0x0154, 0x0165}, {0x0168, 0x016A}, + {0x016C, 0x01CD}, {0x01CF, 0x01CF}, {0x01D1, 0x01D1}, + {0x01D3, 0x01D3}, {0x01D5, 0x01D5}, {0x01D7, 0x01D7}, + {0x01D9, 0x01D9}, {0x01DB, 0x01DB}, {0x01DD, 0x0250}, + {0x0252, 0x0260}, {0x0262, 0x02C3}, {0x02C5, 0x02C6}, + {0x02C8, 0x02C8}, {0x02CC, 0x02CC}, {0x02CE, 0x02CF}, + {0x02D1, 0x02D7}, {0x02DC, 0x02DC}, {0x02DE, 0x02DE}, + {0x02E0, 0x02FF}, {0x0370, 0x0377}, {0x037A, 0x037F}, + {0x0384, 0x038A}, {0x038C, 0x038C}, {0x038E, 0x0390}, + {0x03AA, 0x03B0}, {0x03C2, 0x03C2}, {0x03CA, 0x0400}, + {0x0402, 0x040F}, {0x0450, 0x0450}, {0x0452, 0x052F}, + {0x0531, 0x0556}, {0x0559, 0x058A}, {0x058D, 0x058F}, + {0x0591, 0x05C7}, {0x05D0, 0x05EA}, {0x05EF, 0x05F4}, + {0x0600, 0x061C}, {0x061E, 0x070D}, {0x070F, 0x074A}, + {0x074D, 0x07B1}, {0x07C0, 0x07FA}, {0x07FD, 0x082D}, + {0x0830, 0x083E}, {0x0840, 0x085B}, {0x085E, 0x085E}, + {0x0860, 0x086A}, {0x08A0, 0x08B4}, {0x08B6, 0x08C7}, + {0x08D3, 0x0983}, {0x0985, 0x098C}, {0x098F, 0x0990}, + {0x0993, 0x09A8}, {0x09AA, 0x09B0}, {0x09B2, 0x09B2}, + {0x09B6, 0x09B9}, {0x09BC, 0x09C4}, {0x09C7, 0x09C8}, + {0x09CB, 0x09CE}, {0x09D7, 0x09D7}, {0x09DC, 0x09DD}, + {0x09DF, 0x09E3}, {0x09E6, 0x09FE}, {0x0A01, 0x0A03}, + {0x0A05, 0x0A0A}, {0x0A0F, 0x0A10}, {0x0A13, 0x0A28}, + {0x0A2A, 0x0A30}, {0x0A32, 0x0A33}, {0x0A35, 0x0A36}, + {0x0A38, 0x0A39}, {0x0A3C, 0x0A3C}, {0x0A3E, 0x0A42}, + {0x0A47, 0x0A48}, {0x0A4B, 0x0A4D}, {0x0A51, 0x0A51}, + {0x0A59, 0x0A5C}, {0x0A5E, 0x0A5E}, {0x0A66, 0x0A76}, + {0x0A81, 0x0A83}, {0x0A85, 0x0A8D}, {0x0A8F, 0x0A91}, + {0x0A93, 0x0AA8}, {0x0AAA, 0x0AB0}, {0x0AB2, 0x0AB3}, + {0x0AB5, 0x0AB9}, {0x0ABC, 0x0AC5}, {0x0AC7, 0x0AC9}, + {0x0ACB, 0x0ACD}, {0x0AD0, 0x0AD0}, {0x0AE0, 0x0AE3}, + {0x0AE6, 0x0AF1}, {0x0AF9, 0x0AFF}, {0x0B01, 0x0B03}, + {0x0B05, 0x0B0C}, {0x0B0F, 0x0B10}, {0x0B13, 0x0B28}, + {0x0B2A, 0x0B30}, {0x0B32, 0x0B33}, {0x0B35, 0x0B39}, + {0x0B3C, 0x0B44}, {0x0B47, 0x0B48}, {0x0B4B, 0x0B4D}, + {0x0B55, 0x0B57}, {0x0B5C, 0x0B5D}, {0x0B5F, 0x0B63}, + {0x0B66, 0x0B77}, {0x0B82, 0x0B83}, {0x0B85, 0x0B8A}, + {0x0B8E, 0x0B90}, {0x0B92, 0x0B95}, {0x0B99, 0x0B9A}, + {0x0B9C, 0x0B9C}, {0x0B9E, 0x0B9F}, {0x0BA3, 0x0BA4}, + {0x0BA8, 0x0BAA}, {0x0BAE, 0x0BB9}, {0x0BBE, 0x0BC2}, + {0x0BC6, 0x0BC8}, {0x0BCA, 0x0BCD}, {0x0BD0, 0x0BD0}, + {0x0BD7, 0x0BD7}, {0x0BE6, 0x0BFA}, {0x0C00, 0x0C0C}, + {0x0C0E, 0x0C10}, {0x0C12, 0x0C28}, {0x0C2A, 0x0C39}, + {0x0C3D, 0x0C44}, {0x0C46, 0x0C48}, {0x0C4A, 0x0C4D}, + {0x0C55, 0x0C56}, {0x0C58, 0x0C5A}, {0x0C60, 0x0C63}, + {0x0C66, 0x0C6F}, {0x0C77, 0x0C8C}, {0x0C8E, 0x0C90}, + {0x0C92, 0x0CA8}, {0x0CAA, 0x0CB3}, {0x0CB5, 0x0CB9}, + {0x0CBC, 0x0CC4}, {0x0CC6, 0x0CC8}, {0x0CCA, 0x0CCD}, + {0x0CD5, 0x0CD6}, {0x0CDE, 0x0CDE}, {0x0CE0, 0x0CE3}, + {0x0CE6, 0x0CEF}, {0x0CF1, 0x0CF2}, {0x0D00, 0x0D0C}, + {0x0D0E, 0x0D10}, {0x0D12, 0x0D44}, {0x0D46, 0x0D48}, + {0x0D4A, 0x0D4F}, {0x0D54, 0x0D63}, {0x0D66, 0x0D7F}, + {0x0D81, 0x0D83}, {0x0D85, 0x0D96}, {0x0D9A, 0x0DB1}, + {0x0DB3, 0x0DBB}, {0x0DBD, 0x0DBD}, {0x0DC0, 0x0DC6}, + {0x0DCA, 0x0DCA}, {0x0DCF, 0x0DD4}, {0x0DD6, 0x0DD6}, + {0x0DD8, 0x0DDF}, {0x0DE6, 0x0DEF}, {0x0DF2, 0x0DF4}, + {0x0E01, 0x0E3A}, {0x0E3F, 0x0E5B}, {0x0E81, 0x0E82}, + {0x0E84, 0x0E84}, {0x0E86, 0x0E8A}, {0x0E8C, 0x0EA3}, + {0x0EA5, 0x0EA5}, {0x0EA7, 0x0EBD}, {0x0EC0, 0x0EC4}, + {0x0EC6, 0x0EC6}, {0x0EC8, 0x0ECD}, {0x0ED0, 0x0ED9}, + {0x0EDC, 0x0EDF}, {0x0F00, 0x0F47}, {0x0F49, 0x0F6C}, + {0x0F71, 0x0F97}, {0x0F99, 0x0FBC}, {0x0FBE, 0x0FCC}, + {0x0FCE, 0x0FDA}, {0x1000, 0x10C5}, {0x10C7, 0x10C7}, + {0x10CD, 0x10CD}, {0x10D0, 0x10FF}, {0x1160, 0x1248}, + {0x124A, 0x124D}, {0x1250, 0x1256}, {0x1258, 0x1258}, + {0x125A, 0x125D}, {0x1260, 0x1288}, {0x128A, 0x128D}, + {0x1290, 0x12B0}, {0x12B2, 0x12B5}, {0x12B8, 0x12BE}, + {0x12C0, 0x12C0}, {0x12C2, 0x12C5}, {0x12C8, 0x12D6}, + {0x12D8, 0x1310}, {0x1312, 0x1315}, {0x1318, 0x135A}, + {0x135D, 0x137C}, {0x1380, 0x1399}, {0x13A0, 0x13F5}, + {0x13F8, 0x13FD}, {0x1400, 0x169C}, {0x16A0, 0x16F8}, + {0x1700, 0x170C}, {0x170E, 0x1714}, {0x1720, 0x1736}, + {0x1740, 0x1753}, {0x1760, 0x176C}, {0x176E, 0x1770}, + {0x1772, 0x1773}, {0x1780, 0x17DD}, {0x17E0, 0x17E9}, + {0x17F0, 0x17F9}, {0x1800, 0x180E}, {0x1810, 0x1819}, + {0x1820, 0x1878}, {0x1880, 0x18AA}, {0x18B0, 0x18F5}, + {0x1900, 0x191E}, {0x1920, 0x192B}, {0x1930, 0x193B}, + {0x1940, 0x1940}, {0x1944, 0x196D}, {0x1970, 0x1974}, + {0x1980, 0x19AB}, {0x19B0, 0x19C9}, {0x19D0, 0x19DA}, + {0x19DE, 0x1A1B}, {0x1A1E, 0x1A5E}, {0x1A60, 0x1A7C}, + {0x1A7F, 0x1A89}, {0x1A90, 0x1A99}, {0x1AA0, 0x1AAD}, + {0x1AB0, 0x1AC0}, {0x1B00, 0x1B4B}, {0x1B50, 0x1B7C}, + {0x1B80, 0x1BF3}, {0x1BFC, 0x1C37}, {0x1C3B, 0x1C49}, + {0x1C4D, 0x1C88}, {0x1C90, 0x1CBA}, {0x1CBD, 0x1CC7}, + {0x1CD0, 0x1CFA}, {0x1D00, 0x1DF9}, {0x1DFB, 0x1F15}, + {0x1F18, 0x1F1D}, {0x1F20, 0x1F45}, {0x1F48, 0x1F4D}, + {0x1F50, 0x1F57}, {0x1F59, 0x1F59}, {0x1F5B, 0x1F5B}, + {0x1F5D, 0x1F5D}, {0x1F5F, 0x1F7D}, {0x1F80, 0x1FB4}, + {0x1FB6, 0x1FC4}, {0x1FC6, 0x1FD3}, {0x1FD6, 0x1FDB}, + {0x1FDD, 0x1FEF}, {0x1FF2, 0x1FF4}, {0x1FF6, 0x1FFE}, + {0x2000, 0x200F}, {0x2011, 0x2012}, {0x2017, 0x2017}, + {0x201A, 0x201B}, {0x201E, 0x201F}, {0x2023, 0x2023}, + {0x2028, 0x202F}, {0x2031, 0x2031}, {0x2034, 0x2034}, + {0x2036, 0x203A}, {0x203C, 0x203D}, {0x203F, 0x2064}, + {0x2066, 0x2071}, {0x2075, 0x207E}, {0x2080, 0x2080}, + {0x2085, 0x208E}, {0x2090, 0x209C}, {0x20A0, 0x20A8}, + {0x20AA, 0x20AB}, {0x20AD, 0x20BF}, {0x20D0, 0x20F0}, + {0x2100, 0x2102}, {0x2104, 0x2104}, {0x2106, 0x2108}, + {0x210A, 0x2112}, {0x2114, 0x2115}, {0x2117, 0x2120}, + {0x2123, 0x2125}, {0x2127, 0x212A}, {0x212C, 0x2152}, + {0x2155, 0x215A}, {0x215F, 0x215F}, {0x216C, 0x216F}, + {0x217A, 0x2188}, {0x218A, 0x218B}, {0x219A, 0x21B7}, + {0x21BA, 0x21D1}, {0x21D3, 0x21D3}, {0x21D5, 0x21E6}, + {0x21E8, 0x21FF}, {0x2201, 0x2201}, {0x2204, 0x2206}, + {0x2209, 0x220A}, {0x220C, 0x220E}, {0x2210, 0x2210}, + {0x2212, 0x2214}, {0x2216, 0x2219}, {0x221B, 0x221C}, + {0x2221, 0x2222}, {0x2224, 0x2224}, {0x2226, 0x2226}, + {0x222D, 0x222D}, {0x222F, 0x2233}, {0x2238, 0x223B}, + {0x223E, 0x2247}, {0x2249, 0x224B}, {0x224D, 0x2251}, + {0x2253, 0x225F}, {0x2262, 0x2263}, {0x2268, 0x2269}, + {0x226C, 0x226D}, {0x2270, 0x2281}, {0x2284, 0x2285}, + {0x2288, 0x2294}, {0x2296, 0x2298}, {0x229A, 0x22A4}, + {0x22A6, 0x22BE}, {0x22C0, 0x2311}, {0x2313, 0x2319}, + {0x231C, 0x2328}, {0x232B, 0x23E8}, {0x23ED, 0x23EF}, + {0x23F1, 0x23F2}, {0x23F4, 0x2426}, {0x2440, 0x244A}, + {0x24EA, 0x24EA}, {0x254C, 0x254F}, {0x2574, 0x257F}, + {0x2590, 0x2591}, {0x2596, 0x259F}, {0x25A2, 0x25A2}, + {0x25AA, 0x25B1}, {0x25B4, 0x25B5}, {0x25B8, 0x25BB}, + {0x25BE, 0x25BF}, {0x25C2, 0x25C5}, {0x25C9, 0x25CA}, + {0x25CC, 0x25CD}, {0x25D2, 0x25E1}, {0x25E6, 0x25EE}, + {0x25F0, 0x25FC}, {0x25FF, 0x2604}, {0x2607, 0x2608}, + {0x260A, 0x260D}, {0x2610, 0x2613}, {0x2616, 0x261B}, + {0x261D, 0x261D}, {0x261F, 0x263F}, {0x2641, 0x2641}, + {0x2643, 0x2647}, {0x2654, 0x265F}, {0x2662, 0x2662}, + {0x2666, 0x2666}, {0x266B, 0x266B}, {0x266E, 0x266E}, + {0x2670, 0x267E}, {0x2680, 0x2692}, {0x2694, 0x269D}, + {0x26A0, 0x26A0}, {0x26A2, 0x26A9}, {0x26AC, 0x26BC}, + {0x26C0, 0x26C3}, {0x26E2, 0x26E2}, {0x26E4, 0x26E7}, + {0x2700, 0x2704}, {0x2706, 0x2709}, {0x270C, 0x2727}, + {0x2729, 0x273C}, {0x273E, 0x274B}, {0x274D, 0x274D}, + {0x274F, 0x2752}, {0x2756, 0x2756}, {0x2758, 0x2775}, + {0x2780, 0x2794}, {0x2798, 0x27AF}, {0x27B1, 0x27BE}, + {0x27C0, 0x27E5}, {0x27EE, 0x2984}, {0x2987, 0x2B1A}, + {0x2B1D, 0x2B4F}, {0x2B51, 0x2B54}, {0x2B5A, 0x2B73}, + {0x2B76, 0x2B95}, {0x2B97, 0x2C2E}, {0x2C30, 0x2C5E}, + {0x2C60, 0x2CF3}, {0x2CF9, 0x2D25}, {0x2D27, 0x2D27}, + {0x2D2D, 0x2D2D}, {0x2D30, 0x2D67}, {0x2D6F, 0x2D70}, + {0x2D7F, 0x2D96}, {0x2DA0, 0x2DA6}, {0x2DA8, 0x2DAE}, + {0x2DB0, 0x2DB6}, {0x2DB8, 0x2DBE}, {0x2DC0, 0x2DC6}, + {0x2DC8, 0x2DCE}, {0x2DD0, 0x2DD6}, {0x2DD8, 0x2DDE}, + {0x2DE0, 0x2E52}, {0x303F, 0x303F}, {0x4DC0, 0x4DFF}, + {0xA4D0, 0xA62B}, {0xA640, 0xA6F7}, {0xA700, 0xA7BF}, + {0xA7C2, 0xA7CA}, {0xA7F5, 0xA82C}, {0xA830, 0xA839}, + {0xA840, 0xA877}, {0xA880, 0xA8C5}, {0xA8CE, 0xA8D9}, + {0xA8E0, 0xA953}, {0xA95F, 0xA95F}, {0xA980, 0xA9CD}, + {0xA9CF, 0xA9D9}, {0xA9DE, 0xA9FE}, {0xAA00, 0xAA36}, + {0xAA40, 0xAA4D}, {0xAA50, 0xAA59}, {0xAA5C, 0xAAC2}, + {0xAADB, 0xAAF6}, {0xAB01, 0xAB06}, {0xAB09, 0xAB0E}, + {0xAB11, 0xAB16}, {0xAB20, 0xAB26}, {0xAB28, 0xAB2E}, + {0xAB30, 0xAB6B}, {0xAB70, 0xABED}, {0xABF0, 0xABF9}, + {0xD7B0, 0xD7C6}, {0xD7CB, 0xD7FB}, {0xD800, 0xDFFF}, + {0xFB00, 0xFB06}, {0xFB13, 0xFB17}, {0xFB1D, 0xFB36}, + {0xFB38, 0xFB3C}, {0xFB3E, 0xFB3E}, {0xFB40, 0xFB41}, + {0xFB43, 0xFB44}, {0xFB46, 0xFBC1}, {0xFBD3, 0xFD3F}, + {0xFD50, 0xFD8F}, {0xFD92, 0xFDC7}, {0xFDF0, 0xFDFD}, + {0xFE20, 0xFE2F}, {0xFE70, 0xFE74}, {0xFE76, 0xFEFC}, + {0xFEFF, 0xFEFF}, {0xFFF9, 0xFFFC}, {0x10000, 0x1000B}, + {0x1000D, 0x10026}, {0x10028, 0x1003A}, {0x1003C, 0x1003D}, + {0x1003F, 0x1004D}, {0x10050, 0x1005D}, {0x10080, 0x100FA}, + {0x10100, 0x10102}, {0x10107, 0x10133}, {0x10137, 0x1018E}, + {0x10190, 0x1019C}, {0x101A0, 0x101A0}, {0x101D0, 0x101FD}, + {0x10280, 0x1029C}, {0x102A0, 0x102D0}, {0x102E0, 0x102FB}, + {0x10300, 0x10323}, {0x1032D, 0x1034A}, {0x10350, 0x1037A}, + {0x10380, 0x1039D}, {0x1039F, 0x103C3}, {0x103C8, 0x103D5}, + {0x10400, 0x1049D}, {0x104A0, 0x104A9}, {0x104B0, 0x104D3}, + {0x104D8, 0x104FB}, {0x10500, 0x10527}, {0x10530, 0x10563}, + {0x1056F, 0x1056F}, {0x10600, 0x10736}, {0x10740, 0x10755}, + {0x10760, 0x10767}, {0x10800, 0x10805}, {0x10808, 0x10808}, + {0x1080A, 0x10835}, {0x10837, 0x10838}, {0x1083C, 0x1083C}, + {0x1083F, 0x10855}, {0x10857, 0x1089E}, {0x108A7, 0x108AF}, + {0x108E0, 0x108F2}, {0x108F4, 0x108F5}, {0x108FB, 0x1091B}, + {0x1091F, 0x10939}, {0x1093F, 0x1093F}, {0x10980, 0x109B7}, + {0x109BC, 0x109CF}, {0x109D2, 0x10A03}, {0x10A05, 0x10A06}, + {0x10A0C, 0x10A13}, {0x10A15, 0x10A17}, {0x10A19, 0x10A35}, + {0x10A38, 0x10A3A}, {0x10A3F, 0x10A48}, {0x10A50, 0x10A58}, + {0x10A60, 0x10A9F}, {0x10AC0, 0x10AE6}, {0x10AEB, 0x10AF6}, + {0x10B00, 0x10B35}, {0x10B39, 0x10B55}, {0x10B58, 0x10B72}, + {0x10B78, 0x10B91}, {0x10B99, 0x10B9C}, {0x10BA9, 0x10BAF}, + {0x10C00, 0x10C48}, {0x10C80, 0x10CB2}, {0x10CC0, 0x10CF2}, + {0x10CFA, 0x10D27}, {0x10D30, 0x10D39}, {0x10E60, 0x10E7E}, + {0x10E80, 0x10EA9}, {0x10EAB, 0x10EAD}, {0x10EB0, 0x10EB1}, + {0x10F00, 0x10F27}, {0x10F30, 0x10F59}, {0x10FB0, 0x10FCB}, + {0x10FE0, 0x10FF6}, {0x11000, 0x1104D}, {0x11052, 0x1106F}, + {0x1107F, 0x110C1}, {0x110CD, 0x110CD}, {0x110D0, 0x110E8}, + {0x110F0, 0x110F9}, {0x11100, 0x11134}, {0x11136, 0x11147}, + {0x11150, 0x11176}, {0x11180, 0x111DF}, {0x111E1, 0x111F4}, + {0x11200, 0x11211}, {0x11213, 0x1123E}, {0x11280, 0x11286}, + {0x11288, 0x11288}, {0x1128A, 0x1128D}, {0x1128F, 0x1129D}, + {0x1129F, 0x112A9}, {0x112B0, 0x112EA}, {0x112F0, 0x112F9}, + {0x11300, 0x11303}, {0x11305, 0x1130C}, {0x1130F, 0x11310}, + {0x11313, 0x11328}, {0x1132A, 0x11330}, {0x11332, 0x11333}, + {0x11335, 0x11339}, {0x1133B, 0x11344}, {0x11347, 0x11348}, + {0x1134B, 0x1134D}, {0x11350, 0x11350}, {0x11357, 0x11357}, + {0x1135D, 0x11363}, {0x11366, 0x1136C}, {0x11370, 0x11374}, + {0x11400, 0x1145B}, {0x1145D, 0x11461}, {0x11480, 0x114C7}, + {0x114D0, 0x114D9}, {0x11580, 0x115B5}, {0x115B8, 0x115DD}, + {0x11600, 0x11644}, {0x11650, 0x11659}, {0x11660, 0x1166C}, + {0x11680, 0x116B8}, {0x116C0, 0x116C9}, {0x11700, 0x1171A}, + {0x1171D, 0x1172B}, {0x11730, 0x1173F}, {0x11800, 0x1183B}, + {0x118A0, 0x118F2}, {0x118FF, 0x11906}, {0x11909, 0x11909}, + {0x1190C, 0x11913}, {0x11915, 0x11916}, {0x11918, 0x11935}, + {0x11937, 0x11938}, {0x1193B, 0x11946}, {0x11950, 0x11959}, + {0x119A0, 0x119A7}, {0x119AA, 0x119D7}, {0x119DA, 0x119E4}, + {0x11A00, 0x11A47}, {0x11A50, 0x11AA2}, {0x11AC0, 0x11AF8}, + {0x11C00, 0x11C08}, {0x11C0A, 0x11C36}, {0x11C38, 0x11C45}, + {0x11C50, 0x11C6C}, {0x11C70, 0x11C8F}, {0x11C92, 0x11CA7}, + {0x11CA9, 0x11CB6}, {0x11D00, 0x11D06}, {0x11D08, 0x11D09}, + {0x11D0B, 0x11D36}, {0x11D3A, 0x11D3A}, {0x11D3C, 0x11D3D}, + {0x11D3F, 0x11D47}, {0x11D50, 0x11D59}, {0x11D60, 0x11D65}, + {0x11D67, 0x11D68}, {0x11D6A, 0x11D8E}, {0x11D90, 0x11D91}, + {0x11D93, 0x11D98}, {0x11DA0, 0x11DA9}, {0x11EE0, 0x11EF8}, + {0x11FB0, 0x11FB0}, {0x11FC0, 0x11FF1}, {0x11FFF, 0x12399}, + {0x12400, 0x1246E}, {0x12470, 0x12474}, {0x12480, 0x12543}, + {0x13000, 0x1342E}, {0x13430, 0x13438}, {0x14400, 0x14646}, + {0x16800, 0x16A38}, {0x16A40, 0x16A5E}, {0x16A60, 0x16A69}, + {0x16A6E, 0x16A6F}, {0x16AD0, 0x16AED}, {0x16AF0, 0x16AF5}, + {0x16B00, 0x16B45}, {0x16B50, 0x16B59}, {0x16B5B, 0x16B61}, + {0x16B63, 0x16B77}, {0x16B7D, 0x16B8F}, {0x16E40, 0x16E9A}, + {0x16F00, 0x16F4A}, {0x16F4F, 0x16F87}, {0x16F8F, 0x16F9F}, + {0x1BC00, 0x1BC6A}, {0x1BC70, 0x1BC7C}, {0x1BC80, 0x1BC88}, + {0x1BC90, 0x1BC99}, {0x1BC9C, 0x1BCA3}, {0x1D000, 0x1D0F5}, + {0x1D100, 0x1D126}, {0x1D129, 0x1D1E8}, {0x1D200, 0x1D245}, + {0x1D2E0, 0x1D2F3}, {0x1D300, 0x1D356}, {0x1D360, 0x1D378}, + {0x1D400, 0x1D454}, {0x1D456, 0x1D49C}, {0x1D49E, 0x1D49F}, + {0x1D4A2, 0x1D4A2}, {0x1D4A5, 0x1D4A6}, {0x1D4A9, 0x1D4AC}, + {0x1D4AE, 0x1D4B9}, {0x1D4BB, 0x1D4BB}, {0x1D4BD, 0x1D4C3}, + {0x1D4C5, 0x1D505}, {0x1D507, 0x1D50A}, {0x1D50D, 0x1D514}, + {0x1D516, 0x1D51C}, {0x1D51E, 0x1D539}, {0x1D53B, 0x1D53E}, + {0x1D540, 0x1D544}, {0x1D546, 0x1D546}, {0x1D54A, 0x1D550}, + {0x1D552, 0x1D6A5}, {0x1D6A8, 0x1D7CB}, {0x1D7CE, 0x1DA8B}, + {0x1DA9B, 0x1DA9F}, {0x1DAA1, 0x1DAAF}, {0x1E000, 0x1E006}, + {0x1E008, 0x1E018}, {0x1E01B, 0x1E021}, {0x1E023, 0x1E024}, + {0x1E026, 0x1E02A}, {0x1E100, 0x1E12C}, {0x1E130, 0x1E13D}, + {0x1E140, 0x1E149}, {0x1E14E, 0x1E14F}, {0x1E2C0, 0x1E2F9}, + {0x1E2FF, 0x1E2FF}, {0x1E800, 0x1E8C4}, {0x1E8C7, 0x1E8D6}, + {0x1E900, 0x1E94B}, {0x1E950, 0x1E959}, {0x1E95E, 0x1E95F}, + {0x1EC71, 0x1ECB4}, {0x1ED01, 0x1ED3D}, {0x1EE00, 0x1EE03}, + {0x1EE05, 0x1EE1F}, {0x1EE21, 0x1EE22}, {0x1EE24, 0x1EE24}, + {0x1EE27, 0x1EE27}, {0x1EE29, 0x1EE32}, {0x1EE34, 0x1EE37}, + {0x1EE39, 0x1EE39}, {0x1EE3B, 0x1EE3B}, {0x1EE42, 0x1EE42}, + {0x1EE47, 0x1EE47}, {0x1EE49, 0x1EE49}, {0x1EE4B, 0x1EE4B}, + {0x1EE4D, 0x1EE4F}, {0x1EE51, 0x1EE52}, {0x1EE54, 0x1EE54}, + {0x1EE57, 0x1EE57}, {0x1EE59, 0x1EE59}, {0x1EE5B, 0x1EE5B}, + {0x1EE5D, 0x1EE5D}, {0x1EE5F, 0x1EE5F}, {0x1EE61, 0x1EE62}, + {0x1EE64, 0x1EE64}, {0x1EE67, 0x1EE6A}, {0x1EE6C, 0x1EE72}, + {0x1EE74, 0x1EE77}, {0x1EE79, 0x1EE7C}, {0x1EE7E, 0x1EE7E}, + {0x1EE80, 0x1EE89}, {0x1EE8B, 0x1EE9B}, {0x1EEA1, 0x1EEA3}, + {0x1EEA5, 0x1EEA9}, {0x1EEAB, 0x1EEBB}, {0x1EEF0, 0x1EEF1}, + {0x1F000, 0x1F003}, {0x1F005, 0x1F02B}, {0x1F030, 0x1F093}, + {0x1F0A0, 0x1F0AE}, {0x1F0B1, 0x1F0BF}, {0x1F0C1, 0x1F0CE}, + {0x1F0D1, 0x1F0F5}, {0x1F10B, 0x1F10F}, {0x1F12E, 0x1F12F}, + {0x1F16A, 0x1F16F}, {0x1F1AD, 0x1F1AD}, {0x1F1E6, 0x1F1FF}, + {0x1F321, 0x1F32C}, {0x1F336, 0x1F336}, {0x1F37D, 0x1F37D}, + {0x1F394, 0x1F39F}, {0x1F3CB, 0x1F3CE}, {0x1F3D4, 0x1F3DF}, + {0x1F3F1, 0x1F3F3}, {0x1F3F5, 0x1F3F7}, {0x1F43F, 0x1F43F}, + {0x1F441, 0x1F441}, {0x1F4FD, 0x1F4FE}, {0x1F53E, 0x1F54A}, + {0x1F54F, 0x1F54F}, {0x1F568, 0x1F579}, {0x1F57B, 0x1F594}, + {0x1F597, 0x1F5A3}, {0x1F5A5, 0x1F5FA}, {0x1F650, 0x1F67F}, + {0x1F6C6, 0x1F6CB}, {0x1F6CD, 0x1F6CF}, {0x1F6D3, 0x1F6D4}, + {0x1F6E0, 0x1F6EA}, {0x1F6F0, 0x1F6F3}, {0x1F700, 0x1F773}, + {0x1F780, 0x1F7D8}, {0x1F800, 0x1F80B}, {0x1F810, 0x1F847}, + {0x1F850, 0x1F859}, {0x1F860, 0x1F887}, {0x1F890, 0x1F8AD}, + {0x1F8B0, 0x1F8B1}, {0x1F900, 0x1F90B}, {0x1F93B, 0x1F93B}, + {0x1F946, 0x1F946}, {0x1FA00, 0x1FA53}, {0x1FA60, 0x1FA6D}, + {0x1FB00, 0x1FB92}, {0x1FB94, 0x1FBCA}, {0x1FBF0, 0x1FBF9}, + {0xE0001, 0xE0001}, {0xE0020, 0xE007F}, +} + +var emoji = table{ + {0x203C, 0x203C}, {0x2049, 0x2049}, {0x2122, 0x2122}, + {0x2139, 0x2139}, {0x2194, 0x2199}, {0x21A9, 0x21AA}, + {0x231A, 0x231B}, {0x2328, 0x2328}, {0x2388, 0x2388}, + {0x23CF, 0x23CF}, {0x23E9, 0x23F3}, {0x23F8, 0x23FA}, + {0x24C2, 0x24C2}, {0x25AA, 0x25AB}, {0x25B6, 0x25B6}, + {0x25C0, 0x25C0}, {0x25FB, 0x25FE}, {0x2600, 0x2605}, + {0x2607, 0x2612}, {0x2614, 0x2685}, {0x2690, 0x2705}, + {0x2708, 0x2712}, {0x2714, 0x2714}, {0x2716, 0x2716}, + {0x271D, 0x271D}, {0x2721, 0x2721}, {0x2728, 0x2728}, + {0x2733, 0x2734}, {0x2744, 0x2744}, {0x2747, 0x2747}, + {0x274C, 0x274C}, {0x274E, 0x274E}, {0x2753, 0x2755}, + {0x2757, 0x2757}, {0x2763, 0x2767}, {0x2795, 0x2797}, + {0x27A1, 0x27A1}, {0x27B0, 0x27B0}, {0x27BF, 0x27BF}, + {0x2934, 0x2935}, {0x2B05, 0x2B07}, {0x2B1B, 0x2B1C}, + {0x2B50, 0x2B50}, {0x2B55, 0x2B55}, {0x3030, 0x3030}, + {0x303D, 0x303D}, {0x3297, 0x3297}, {0x3299, 0x3299}, + {0x1F000, 0x1F0FF}, {0x1F10D, 0x1F10F}, {0x1F12F, 0x1F12F}, + {0x1F16C, 0x1F171}, {0x1F17E, 0x1F17F}, {0x1F18E, 0x1F18E}, + {0x1F191, 0x1F19A}, {0x1F1AD, 0x1F1E5}, {0x1F201, 0x1F20F}, + {0x1F21A, 0x1F21A}, {0x1F22F, 0x1F22F}, {0x1F232, 0x1F23A}, + {0x1F23C, 0x1F23F}, {0x1F249, 0x1F3FA}, {0x1F400, 0x1F53D}, + {0x1F546, 0x1F64F}, {0x1F680, 0x1F6FF}, {0x1F774, 0x1F77F}, + {0x1F7D5, 0x1F7FF}, {0x1F80C, 0x1F80F}, {0x1F848, 0x1F84F}, + {0x1F85A, 0x1F85F}, {0x1F888, 0x1F88F}, {0x1F8AE, 0x1F8FF}, + {0x1F90C, 0x1F93A}, {0x1F93C, 0x1F945}, {0x1F947, 0x1FAFF}, + {0x1FC00, 0x1FFFD}, +} diff --git a/vendor/github.com/mattn/go-runewidth/runewidth_windows.go b/vendor/github.com/mattn/go-runewidth/runewidth_windows.go new file mode 100644 index 000000000..d6a61777d --- /dev/null +++ b/vendor/github.com/mattn/go-runewidth/runewidth_windows.go @@ -0,0 +1,28 @@ +// +build windows +// +build !appengine + +package runewidth + +import ( + "syscall" +) + +var ( + kernel32 = syscall.NewLazyDLL("kernel32") + procGetConsoleOutputCP = kernel32.NewProc("GetConsoleOutputCP") +) + +// IsEastAsian return true if the current locale is CJK +func IsEastAsian() bool { + r1, _, _ := procGetConsoleOutputCP.Call() + if r1 == 0 { + return false + } + + switch int(r1) { + case 932, 51932, 936, 949, 950: + return true + } + + return false +} diff --git a/vendor/github.com/vbauerster/mpb/v5/bar.go b/vendor/github.com/vbauerster/mpb/v5/bar.go index 13bda2247..9c28a07a8 100644 --- a/vendor/github.com/vbauerster/mpb/v5/bar.go +++ b/vendor/github.com/vbauerster/mpb/v5/bar.go @@ -6,28 +6,15 @@ import ( "fmt" "io" "log" + "runtime/debug" "strings" "time" - "unicode/utf8" + "github.com/acarl005/stripansi" + "github.com/mattn/go-runewidth" "github.com/vbauerster/mpb/v5/decor" ) -// BarFiller interface. -// Bar renders itself by calling BarFiller's Fill method. You can -// literally have any bar kind, by implementing this interface and -// passing it to the *Progress.Add(...) *Bar method. -type BarFiller interface { - Fill(w io.Writer, width int, stat *decor.Statistics) -} - -// BarFillerFunc is function type adapter to convert function into Filler. -type BarFillerFunc func(w io.Writer, width int, stat *decor.Statistics) - -func (f BarFillerFunc) Fill(w io.Writer, width int, stat *decor.Statistics) { - f(w, width, stat) -} - // Bar represents a progress Bar. type Bar struct { priority int // used by heap @@ -55,21 +42,22 @@ type Bar struct { recoveredPanic interface{} } -type extFunc func(in io.Reader, tw int, st *decor.Statistics) (out io.Reader, lines int) +type extFunc func(in io.Reader, reqWidth int, st decor.Statistics) (out io.Reader, lines int) type bState struct { - baseF BarFiller - filler BarFiller id int - width int + priority int + reqWidth int total int64 current int64 + refill int64 lastN int64 iterated bool trimSpace bool toComplete bool completeFlushed bool ignoreComplete bool + dropOnComplete bool noPop bool aDecorators []decor.Decorator pDecorators []decor.Decorator @@ -77,12 +65,10 @@ type bState struct { ewmaDecorators []decor.EwmaDecorator shutdownListeners []decor.ShutdownListener bufP, bufB, bufA *bytes.Buffer + filler BarFiller + middleware func(BarFiller) BarFiller extender extFunc - // priority overrides *Bar's priority, if set - priority int - // dropOnComplete propagates to *Bar - dropOnComplete bool // runningBar is a key for *pState.parkedBars runningBar *Bar @@ -146,13 +132,8 @@ func (b *Bar) Current() int64 { // Given default bar style is "[=>-]<+", refill rune is '+'. // To set bar style use mpb.BarStyle(string) BarOption. func (b *Bar) SetRefill(amount int64) { - type refiller interface { - SetRefill(int64) - } b.operateState <- func(s *bState) { - if f, ok := s.baseF.(refiller); ok { - f.SetRefill(amount) - } + s.refill = amount } } @@ -318,44 +299,40 @@ func (b *Bar) serve(ctx context.Context, s *bState) { } func (b *Bar) render(tw int) { - if b.recoveredPanic != nil { - b.toShutdown = false - b.frameCh <- b.panicToFrame(tw) - return - } select { case b.operateState <- func(s *bState) { + stat := newStatistics(tw, s) defer func() { // recovering if user defined decorator panics for example if p := recover(); p != nil { - b.dlogger.Println(p) + s.extender = makePanicExtender(p) + frame, lines := s.extender(nil, s.reqWidth, stat) + b.extendedLines = lines + b.toShutdown = !b.toShutdown b.recoveredPanic = p - b.toShutdown = !s.completeFlushed - b.frameCh <- b.panicToFrame(tw) + b.frameCh <- frame + b.dlogger.Println(p) } + s.completeFlushed = s.toComplete }() - - st := newStatistics(s) - frame := s.draw(tw, st) - frame, b.extendedLines = s.extender(frame, tw, st) - + frame, lines := s.extender(s.draw(stat), s.reqWidth, stat) + b.extendedLines = lines b.toShutdown = s.toComplete && !s.completeFlushed - s.completeFlushed = s.toComplete b.frameCh <- frame }: case <-b.done: s := b.cacheState - st := newStatistics(s) - frame := s.draw(tw, st) - frame, b.extendedLines = s.extender(frame, tw, st) + stat := newStatistics(tw, s) + var r io.Reader + if b.recoveredPanic == nil { + r = s.draw(stat) + } + frame, lines := s.extender(r, s.reqWidth, stat) + b.extendedLines = lines b.frameCh <- frame } } -func (b *Bar) panicToFrame(termWidth int) io.Reader { - return strings.NewReader(fmt.Sprintf(fmt.Sprintf("%%.%dv\n", termWidth), b.recoveredPanic)) -} - func (b *Bar) subscribeDecorators() { var averageDecorators []decor.AverageDecorator var ewmaDecorators []decor.EwmaDecorator @@ -398,34 +375,41 @@ func (b *Bar) wSyncTable() [][]chan int { } } -func (s *bState) draw(termWidth int, stat *decor.Statistics) io.Reader { +func (s *bState) draw(stat decor.Statistics) io.Reader { + if !s.trimSpace { + stat.AvailableWidth -= 2 + s.bufB.WriteByte(' ') + defer s.bufB.WriteByte(' ') + } + + nlr := strings.NewReader("\n") + tw := stat.AvailableWidth for _, d := range s.pDecorators { - s.bufP.WriteString(d.Decor(stat)) + str := d.Decor(stat) + stat.AvailableWidth -= runewidth.StringWidth(stripansi.Strip(str)) + s.bufP.WriteString(str) + } + if stat.AvailableWidth <= 0 { + trunc := strings.NewReader(runewidth.Truncate(stripansi.Strip(s.bufP.String()), tw, "…")) + s.bufP.Reset() + return io.MultiReader(trunc, s.bufB, nlr) } + tw = stat.AvailableWidth for _, d := range s.aDecorators { - s.bufA.WriteString(d.Decor(stat)) + str := d.Decor(stat) + stat.AvailableWidth -= runewidth.StringWidth(stripansi.Strip(str)) + s.bufA.WriteString(str) } - - s.bufA.WriteByte('\n') - - prependCount := utf8.RuneCount(s.bufP.Bytes()) - appendCount := utf8.RuneCount(s.bufA.Bytes()) - 1 - - if fitWidth := s.width; termWidth > 1 { - if !s.trimSpace { - // reserve space for edge spaces - termWidth -= 2 - s.bufB.WriteByte(' ') - defer s.bufB.WriteByte(' ') - } - if prependCount+s.width+appendCount > termWidth { - fitWidth = termWidth - prependCount - appendCount - } - s.filler.Fill(s.bufB, fitWidth, stat) + if stat.AvailableWidth <= 0 { + trunc := strings.NewReader(runewidth.Truncate(stripansi.Strip(s.bufA.String()), tw, "…")) + s.bufA.Reset() + return io.MultiReader(s.bufP, s.bufB, trunc, nlr) } - return io.MultiReader(s.bufP, s.bufB, s.bufA) + s.filler.Fill(s.bufB, s.reqWidth, stat) + + return io.MultiReader(s.bufP, s.bufB, s.bufA, nlr) } func (s *bState) wSyncTable() [][]chan int { @@ -450,12 +434,14 @@ func (s *bState) wSyncTable() [][]chan int { return table } -func newStatistics(s *bState) *decor.Statistics { - return &decor.Statistics{ - ID: s.id, - Completed: s.completeFlushed, - Total: s.total, - Current: s.current, +func newStatistics(tw int, s *bState) decor.Statistics { + return decor.Statistics{ + ID: s.id, + AvailableWidth: tw, + Total: s.total, + Current: s.current, + Refill: s.refill, + Completed: s.completeFlushed, } } @@ -476,3 +462,17 @@ func ewmaIterationUpdate(done bool, s *bState, dur time.Duration) { d.EwmaUpdate(s.lastN, dur) } } + +func makePanicExtender(p interface{}) extFunc { + pstr := fmt.Sprint(p) + stack := debug.Stack() + stackLines := bytes.Count(stack, []byte("\n")) + return func(_ io.Reader, _ int, st decor.Statistics) (io.Reader, int) { + mr := io.MultiReader( + strings.NewReader(runewidth.Truncate(pstr, st.AvailableWidth, "…")), + strings.NewReader(fmt.Sprintf("\n%#v\n", st)), + bytes.NewReader(stack), + ) + return mr, stackLines + 1 + } +} diff --git a/vendor/github.com/vbauerster/mpb/v5/bar_filler.go b/vendor/github.com/vbauerster/mpb/v5/bar_filler.go index 33dbf191d..07148bffb 100644 --- a/vendor/github.com/vbauerster/mpb/v5/bar_filler.go +++ b/vendor/github.com/vbauerster/mpb/v5/bar_filler.go @@ -2,137 +2,29 @@ package mpb import ( "io" - "unicode/utf8" "github.com/vbauerster/mpb/v5/decor" - "github.com/vbauerster/mpb/v5/internal" ) -const ( - rLeft = iota - rFill - rTip - rEmpty - rRight - rRevTip - rRefill -) - -// DefaultBarStyle is a string containing 7 runes. -// Each rune is a building block of a progress bar. -// -// '1st rune' stands for left boundary rune -// -// '2nd rune' stands for fill rune +// BarFiller interface. +// Bar (without decorators) renders itself by calling BarFiller's Fill method. // -// '3rd rune' stands for tip rune +// `reqWidth` is requested width, which is set via: +// func WithWidth(width int) ContainerOption +// func BarWidth(width int) BarOption // -// '4th rune' stands for empty rune +// Default implementations can be obtained via: // -// '5th rune' stands for right boundary rune +// func NewBarFiller(style string, reverse bool) BarFiller +// func NewSpinnerFiller(style []string, alignment SpinnerAlignment) BarFiller // -// '6th rune' stands for reverse tip rune -// -// '7th rune' stands for refill rune -// -const DefaultBarStyle string = "[=>-]<+" - -type barFiller struct { - format [][]byte - tip []byte - refill int64 - reverse bool - flush func(w io.Writer, bb [][]byte) -} - -// NewBarFiller constucts mpb.BarFiller, to be used with *Progress.Add(...) *Bar method. -func NewBarFiller(style string, reverse bool) BarFiller { - if style == "" { - style = DefaultBarStyle - } - bf := &barFiller{ - format: make([][]byte, utf8.RuneCountInString(style)), - reverse: reverse, - } - bf.SetStyle(style) - return bf -} - -func (s *barFiller) SetStyle(style string) { - if !utf8.ValidString(style) { - return - } - src := make([][]byte, 0, utf8.RuneCountInString(style)) - for _, r := range style { - src = append(src, []byte(string(r))) - } - copy(s.format, src) - s.SetReverse(s.reverse) -} - -func (s *barFiller) SetReverse(reverse bool) { - if reverse { - s.tip = s.format[rRevTip] - s.flush = reverseFlush - } else { - s.tip = s.format[rTip] - s.flush = regularFlush - } - s.reverse = reverse +type BarFiller interface { + Fill(w io.Writer, reqWidth int, stat decor.Statistics) } -func (s *barFiller) SetRefill(amount int64) { - s.refill = amount -} - -func (s *barFiller) Fill(w io.Writer, width int, stat *decor.Statistics) { - // don't count rLeft and rRight as progress - width -= 2 - if width < 2 { - return - } - w.Write(s.format[rLeft]) - defer w.Write(s.format[rRight]) - - bb := make([][]byte, width) - - cwidth := int(internal.PercentageRound(stat.Total, stat.Current, width)) - - for i := 0; i < cwidth; i++ { - bb[i] = s.format[rFill] - } - - if s.refill > 0 { - var rwidth int - if s.refill > stat.Current { - rwidth = cwidth - } else { - rwidth = int(internal.PercentageRound(stat.Total, int64(s.refill), width)) - } - for i := 0; i < rwidth; i++ { - bb[i] = s.format[rRefill] - } - } - - if cwidth > 0 && cwidth < width { - bb[cwidth-1] = s.tip - } - - for i := cwidth; i < width; i++ { - bb[i] = s.format[rEmpty] - } - - s.flush(w, bb) -} - -func regularFlush(w io.Writer, bb [][]byte) { - for i := 0; i < len(bb); i++ { - w.Write(bb[i]) - } -} +// BarFillerFunc is function type adapter to convert function into BarFiller. +type BarFillerFunc func(w io.Writer, reqWidth int, stat decor.Statistics) -func reverseFlush(w io.Writer, bb [][]byte) { - for i := len(bb) - 1; i >= 0; i-- { - w.Write(bb[i]) - } +func (f BarFillerFunc) Fill(w io.Writer, reqWidth int, stat decor.Statistics) { + f(w, reqWidth, stat) } diff --git a/vendor/github.com/vbauerster/mpb/v5/bar_filler_bar.go b/vendor/github.com/vbauerster/mpb/v5/bar_filler_bar.go new file mode 100644 index 000000000..637bd88ca --- /dev/null +++ b/vendor/github.com/vbauerster/mpb/v5/bar_filler_bar.go @@ -0,0 +1,173 @@ +package mpb + +import ( + "bytes" + "io" + "unicode/utf8" + + "github.com/mattn/go-runewidth" + "github.com/vbauerster/mpb/v5/decor" + "github.com/vbauerster/mpb/v5/internal" +) + +const ( + rLeft = iota + rFill + rTip + rSpace + rRight + rRevTip + rRefill +) + +// DefaultBarStyle is a string containing 7 runes. +// Each rune is a building block of a progress bar. +// +// '1st rune' stands for left boundary rune +// +// '2nd rune' stands for fill rune +// +// '3rd rune' stands for tip rune +// +// '4th rune' stands for space rune +// +// '5th rune' stands for right boundary rune +// +// '6th rune' stands for reverse tip rune +// +// '7th rune' stands for refill rune +// +const DefaultBarStyle string = "[=>-]<+" + +type barFiller struct { + format [][]byte + rwidth []int + tip []byte + refill int64 + reverse bool + flush func(io.Writer, *space, [][]byte) +} + +type space struct { + space []byte + rwidth int + count int +} + +// NewBarFiller constucts mpb.BarFiller, to be used with *Progress.Add(...) *Bar method. +func NewBarFiller(style string, reverse bool) BarFiller { + bf := &barFiller{ + format: make([][]byte, len(DefaultBarStyle)), + rwidth: make([]int, len(DefaultBarStyle)), + reverse: reverse, + } + bf.SetStyle(style) + return bf +} + +func (s *barFiller) SetStyle(style string) { + if !utf8.ValidString(style) { + panic("invalid bar style") + } + if style == "" { + style = DefaultBarStyle + } + src := make([][]byte, utf8.RuneCountInString(style)) + i := 0 + for _, r := range style { + s.rwidth[i] = runewidth.RuneWidth(r) + src[i] = []byte(string(r)) + i++ + } + copy(s.format, src) + s.SetReverse(s.reverse) +} + +func (s *barFiller) SetReverse(reverse bool) { + if reverse { + s.tip = s.format[rRevTip] + s.flush = reverseFlush + } else { + s.tip = s.format[rTip] + s.flush = regularFlush + } + s.reverse = reverse +} + +func (s *barFiller) Fill(w io.Writer, reqWidth int, stat decor.Statistics) { + width := internal.WidthForBarFiller(reqWidth, stat.AvailableWidth) + + if brackets := s.rwidth[rLeft] + s.rwidth[rRight]; width < brackets { + return + } else { + // don't count brackets as progress + width -= brackets + } + w.Write(s.format[rLeft]) + defer w.Write(s.format[rRight]) + + cwidth := int(internal.PercentageRound(stat.Total, stat.Current, width)) + space := &space{ + space: s.format[rSpace], + rwidth: s.rwidth[rSpace], + count: width - cwidth, + } + + index, refill := 0, 0 + bb := make([][]byte, cwidth) + + if cwidth > 0 && cwidth != width { + bb[index] = s.tip + cwidth -= s.rwidth[rTip] + index++ + } + + if stat.Refill > 0 { + refill = int(internal.PercentageRound(stat.Total, int64(stat.Refill), width)) + if refill > cwidth { + refill = cwidth + } + cwidth -= refill + } + + for cwidth > 0 { + bb[index] = s.format[rFill] + cwidth -= s.rwidth[rFill] + index++ + } + + for refill > 0 { + bb[index] = s.format[rRefill] + refill -= s.rwidth[rRefill] + index++ + } + + if cwidth+refill < 0 || space.rwidth > 1 { + buf := new(bytes.Buffer) + s.flush(buf, space, bb[:index]) + io.WriteString(w, runewidth.Truncate(buf.String(), width, "…")) + return + } + + s.flush(w, space, bb) +} + +func regularFlush(w io.Writer, space *space, bb [][]byte) { + for i := len(bb) - 1; i >= 0; i-- { + w.Write(bb[i]) + } + for space.count > 0 { + w.Write(space.space) + space.count -= space.rwidth + } +} + +func reverseFlush(w io.Writer, space *space, bb [][]byte) { + for space.count > 0 { + w.Write(space.space) + space.count -= space.rwidth + } + for i := 0; i < len(bb); i++ { + w.Write(bb[i]) + } +} diff --git a/vendor/github.com/vbauerster/mpb/v5/spinner_filler.go b/vendor/github.com/vbauerster/mpb/v5/bar_filler_spinner.go index 517725fbf..d2cb2b726 100644 --- a/vendor/github.com/vbauerster/mpb/v5/spinner_filler.go +++ b/vendor/github.com/vbauerster/mpb/v5/bar_filler_spinner.go @@ -6,6 +6,7 @@ import ( "unicode/utf8" "github.com/vbauerster/mpb/v5/decor" + "github.com/vbauerster/mpb/v5/internal" ) // SpinnerAlignment enum. @@ -39,7 +40,8 @@ func NewSpinnerFiller(style []string, alignment SpinnerAlignment) BarFiller { return filler } -func (s *spinnerFiller) Fill(w io.Writer, width int, stat *decor.Statistics) { +func (s *spinnerFiller) Fill(w io.Writer, reqWidth int, stat decor.Statistics) { + width := internal.WidthForBarFiller(reqWidth, stat.AvailableWidth) frame := s.frames[s.count%uint(len(s.frames))] frameWidth := utf8.RuneCountInString(frame) diff --git a/vendor/github.com/vbauerster/mpb/v5/bar_option.go b/vendor/github.com/vbauerster/mpb/v5/bar_option.go index 76f2050f1..31b7939b0 100644 --- a/vendor/github.com/vbauerster/mpb/v5/bar_option.go +++ b/vendor/github.com/vbauerster/mpb/v5/bar_option.go @@ -46,7 +46,7 @@ func BarID(id int) BarOption { // BarWidth sets bar width independent of the container. func BarWidth(width int) BarOption { return func(s *bState) { - s.width = width + s.reqWidth = width } } @@ -77,19 +77,22 @@ func BarFillerClearOnComplete() BarOption { // BarFillerOnComplete replaces bar's filler with message, on complete event. func BarFillerOnComplete(message string) BarOption { - return func(s *bState) { - s.filler = makeBarFillerOnComplete(s.baseF, message) - } + return BarFillerMiddleware(func(base BarFiller) BarFiller { + return BarFillerFunc(func(w io.Writer, reqWidth int, st decor.Statistics) { + if st.Completed { + io.WriteString(w, message) + } else { + base.Fill(w, reqWidth, st) + } + }) + }) } -func makeBarFillerOnComplete(filler BarFiller, message string) BarFiller { - return BarFillerFunc(func(w io.Writer, width int, st *decor.Statistics) { - if st.Completed { - io.WriteString(w, message) - } else { - filler.Fill(w, width, st) - } - }) +// BarFillerMiddleware provides a way to augment default BarFiller. +func BarFillerMiddleware(middle func(BarFiller) BarFiller) BarOption { + return func(s *bState) { + s.middleware = middle + } } // BarPriority sets bar's priority. Zero is highest priority, i.e. bar @@ -103,21 +106,20 @@ func BarPriority(priority int) BarOption { // BarExtender is an option to extend bar to the next new line, with // arbitrary output. -func BarExtender(extender BarFiller) BarOption { - if extender == nil { +func BarExtender(filler BarFiller) BarOption { + if filler == nil { return nil } return func(s *bState) { - s.extender = makeExtFunc(extender) + s.extender = makeExtFunc(filler) } } -func makeExtFunc(extender BarFiller) extFunc { +func makeExtFunc(filler BarFiller) extFunc { buf := new(bytes.Buffer) - nl := []byte("\n") - return func(r io.Reader, tw int, st *decor.Statistics) (io.Reader, int) { - extender.Fill(buf, tw, st) - return io.MultiReader(r, buf), bytes.Count(buf.Bytes(), nl) + return func(r io.Reader, reqWidth int, st decor.Statistics) (io.Reader, int) { + filler.Fill(buf, reqWidth, st) + return io.MultiReader(r, buf), bytes.Count(buf.Bytes(), []byte("\n")) } } @@ -139,7 +141,7 @@ func BarStyle(style string) BarOption { SetStyle(string) } return func(s *bState) { - if t, ok := s.baseF.(styleSetter); ok { + if t, ok := s.filler.(styleSetter); ok { t.SetStyle(style) } } @@ -159,7 +161,7 @@ func BarReverse() BarOption { SetReverse(bool) } return func(s *bState) { - if t, ok := s.baseF.(revSetter); ok { + if t, ok := s.filler.(revSetter); ok { t.SetReverse(true) } } @@ -189,7 +191,7 @@ func MakeFillerTypeSpecificBarOption( cb func(interface{}), ) BarOption { return func(s *bState) { - if t, ok := typeChecker(s.baseF); ok { + if t, ok := typeChecker(s.filler); ok { cb(t) } } diff --git a/vendor/github.com/vbauerster/mpb/v5/options.go b/vendor/github.com/vbauerster/mpb/v5/container_option.go index 048870284..fac59e436 100644 --- a/vendor/github.com/vbauerster/mpb/v5/options.go +++ b/vendor/github.com/vbauerster/mpb/v5/container_option.go @@ -21,14 +21,11 @@ func WithWaitGroup(wg *sync.WaitGroup) ContainerOption { } } -// WithWidth sets container width. Default is 80. Bars inherit this -// width, as long as no BarWidth is applied. -func WithWidth(w int) ContainerOption { +// WithWidth sets container width. If not set underlying bars will +// occupy whole term width. +func WithWidth(width int) ContainerOption { return func(s *pState) { - if w < 0 { - return - } - s.width = w + s.reqWidth = width } } diff --git a/vendor/github.com/vbauerster/mpb/v5/cwriter/writer.go b/vendor/github.com/vbauerster/mpb/v5/cwriter/writer.go index 9ec1ec66b..bb503360d 100644 --- a/vendor/github.com/vbauerster/mpb/v5/cwriter/writer.go +++ b/vendor/github.com/vbauerster/mpb/v5/cwriter/writer.go @@ -7,7 +7,7 @@ import ( "io" "os" - "golang.org/x/crypto/ssh/terminal" + "github.com/mattn/go-isatty" ) // NotATTY not a TeleTYpewriter error. @@ -30,13 +30,14 @@ func New(out io.Writer) *Writer { w := &Writer{out: out} if f, ok := out.(*os.File); ok { w.fd = f.Fd() - w.isTerminal = terminal.IsTerminal(int(w.fd)) + w.isTerminal = isatty.IsTerminal(w.fd) } return w } // Flush flushes the underlying buffer. func (w *Writer) Flush(lineCount int) (err error) { + // some terminals interpret clear 0 lines as clear 1 if w.lineCount > 0 { w.clearLines() } @@ -63,9 +64,9 @@ func (w *Writer) ReadFrom(r io.Reader) (n int64, err error) { // GetWidth returns width of underlying terminal. func (w *Writer) GetWidth() (int, error) { - if w.isTerminal { - tw, _, err := terminal.GetSize(int(w.fd)) - return tw, err + if !w.isTerminal { + return -1, NotATTY } - return -1, NotATTY + tw, _, err := GetSize(w.fd) + return tw, err } diff --git a/vendor/github.com/vbauerster/mpb/v5/cwriter/writer_posix.go b/vendor/github.com/vbauerster/mpb/v5/cwriter/writer_posix.go index 3fb8b7d75..e836cec3a 100644 --- a/vendor/github.com/vbauerster/mpb/v5/cwriter/writer_posix.go +++ b/vendor/github.com/vbauerster/mpb/v5/cwriter/writer_posix.go @@ -2,8 +2,21 @@ package cwriter -import "fmt" +import ( + "fmt" + + "golang.org/x/sys/unix" +) func (w *Writer) clearLines() { fmt.Fprintf(w.out, cuuAndEd, w.lineCount) } + +// GetSize returns the dimensions of the given terminal. +func GetSize(fd uintptr) (width, height int, err error) { + ws, err := unix.IoctlGetWinsize(int(fd), unix.TIOCGWINSZ) + if err != nil { + return -1, -1, err + } + return int(ws.Col), int(ws.Row), nil +} diff --git a/vendor/github.com/vbauerster/mpb/v5/cwriter/writer_windows.go b/vendor/github.com/vbauerster/mpb/v5/cwriter/writer_windows.go index 712528900..7a3ed5bcc 100644 --- a/vendor/github.com/vbauerster/mpb/v5/cwriter/writer_windows.go +++ b/vendor/github.com/vbauerster/mpb/v5/cwriter/writer_windows.go @@ -14,7 +14,6 @@ var ( procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo") procSetConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition") procFillConsoleOutputCharacter = kernel32.NewProc("FillConsoleOutputCharacterW") - procFillConsoleOutputAttribute = kernel32.NewProc("FillConsoleOutputAttribute") ) type coord struct { @@ -41,8 +40,9 @@ func (w *Writer) clearLines() { if !w.isTerminal { fmt.Fprintf(w.out, cuuAndEd, w.lineCount) } - var info consoleScreenBufferInfo - procGetConsoleScreenBufferInfo.Call(w.fd, uintptr(unsafe.Pointer(&info))) + + info := new(consoleScreenBufferInfo) + procGetConsoleScreenBufferInfo.Call(w.fd, uintptr(unsafe.Pointer(info))) info.cursorPosition.y -= int16(w.lineCount) if info.cursorPosition.y < 0 { @@ -51,10 +51,19 @@ func (w *Writer) clearLines() { procSetConsoleCursorPosition.Call(w.fd, uintptr(uint32(uint16(info.cursorPosition.y))<<16|uint32(uint16(info.cursorPosition.x)))) // clear the lines - cursor := coord{ + cursor := &coord{ x: info.window.left, y: info.cursorPosition.y, } count := uint32(info.size.x) * uint32(w.lineCount) - procFillConsoleOutputCharacter.Call(w.fd, uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(new(uint32)))) + procFillConsoleOutputCharacter.Call(w.fd, uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(cursor)), uintptr(unsafe.Pointer(new(uint32)))) +} + +// GetSize returns the visible dimensions of the given terminal. +// +// These dimensions don't include any scrollback buffer height. +func GetSize(fd uintptr) (width, height int, err error) { + info := new(consoleScreenBufferInfo) + procGetConsoleScreenBufferInfo.Call(fd, uintptr(unsafe.Pointer(info))) + return int(info.window.right - info.window.left), int(info.window.bottom - info.window.top), nil } diff --git a/vendor/github.com/vbauerster/mpb/v5/decor/any.go b/vendor/github.com/vbauerster/mpb/v5/decor/any.go index bf9cf51a5..39518f594 100644 --- a/vendor/github.com/vbauerster/mpb/v5/decor/any.go +++ b/vendor/github.com/vbauerster/mpb/v5/decor/any.go @@ -1,21 +1,21 @@ package decor // Any decorator displays text, that can be changed during decorator's -// lifetime via provided func call back. +// lifetime via provided DecorFunc. // -// `f` call back which provides string to display +// `fn` DecorFunc callback // // `wcc` optional WC config // -func Any(f func(*Statistics) string, wcc ...WC) Decorator { - return &any{initWC(wcc...), f} +func Any(fn DecorFunc, wcc ...WC) Decorator { + return &any{initWC(wcc...), fn} } type any struct { WC - f func(*Statistics) string + fn DecorFunc } -func (d *any) Decor(s *Statistics) string { - return d.FormatMsg(d.f(s)) +func (d *any) Decor(s Statistics) string { + return d.FormatMsg(d.fn(s)) } diff --git a/vendor/github.com/vbauerster/mpb/v5/decor/counters.go b/vendor/github.com/vbauerster/mpb/v5/decor/counters.go index 297bf937b..010ec371a 100644 --- a/vendor/github.com/vbauerster/mpb/v5/decor/counters.go +++ b/vendor/github.com/vbauerster/mpb/v5/decor/counters.go @@ -46,21 +46,21 @@ func Counters(unit int, pairFmt string, wcc ...WC) Decorator { return Any(chooseSizeProducer(unit, pairFmt), wcc...) } -func chooseSizeProducer(unit int, format string) func(*Statistics) string { +func chooseSizeProducer(unit int, format string) DecorFunc { if format == "" { format = "%d / %d" } switch unit { case UnitKiB: - return func(s *Statistics) string { + return func(s Statistics) string { return fmt.Sprintf(format, SizeB1024(s.Current), SizeB1024(s.Total)) } case UnitKB: - return func(s *Statistics) string { + return func(s Statistics) string { return fmt.Sprintf(format, SizeB1000(s.Current), SizeB1000(s.Total)) } default: - return func(s *Statistics) string { + return func(s Statistics) string { return fmt.Sprintf(format, s.Current, s.Total) } } diff --git a/vendor/github.com/vbauerster/mpb/v5/decor/decorator.go b/vendor/github.com/vbauerster/mpb/v5/decor/decorator.go index 5bca63d52..e81fae367 100644 --- a/vendor/github.com/vbauerster/mpb/v5/decor/decorator.go +++ b/vendor/github.com/vbauerster/mpb/v5/decor/decorator.go @@ -3,9 +3,9 @@ package decor import ( "fmt" "time" - "unicode/utf8" "github.com/acarl005/stripansi" + "github.com/mattn/go-runewidth" ) const ( @@ -47,22 +47,32 @@ const ( // Statistics consists of progress related statistics, that Decorator // may need. type Statistics struct { - ID int - Completed bool - Total int64 - Current int64 + ID int + AvailableWidth int + Total int64 + Current int64 + Refill int64 + Completed bool } // Decorator interface. -// Implementors should embed WC type, that way only single method -// Decor(*Statistics) needs to be implemented, the rest will be handled -// by WC type. +// Most of the time there is no need to implement this interface +// manually, as decor package already provides a wide range of decorators +// which implement this interface. If however built-in decorators don't +// meet your needs, you're free to implement your own one by implementing +// this particular interface. The easy way to go is to convert a +// `DecorFunc` into a `Decorator` interface by using provided +// `func Any(DecorFunc, ...WC) Decorator`. type Decorator interface { Configurator Synchronizer - Decor(*Statistics) string + Decor(Statistics) string } +// DecorFunc func type. +// To be used with `func Any`(DecorFunc, ...WC) Decorator`. +type DecorFunc func(Statistics) string + // Synchronizer interface. // All decorators implement this interface implicitly. Its Sync // method exposes width sync channel, if DSyncWidth bit is set. @@ -117,38 +127,35 @@ var ( // W represents width and C represents bit set of width related config. // A decorator should embed WC, to enable width synchronization. type WC struct { - W int - C int - dynFormat string - wsync chan int + W int + C int + fill func(s string, w int) string + wsync chan int } // FormatMsg formats final message according to WC.W and WC.C. // Should be called by any Decorator implementation. func (wc *WC) FormatMsg(msg string) string { - var format string - runeCount := utf8.RuneCountInString(stripansi.Strip(msg)) - ansiCount := utf8.RuneCountInString(msg) - runeCount + pureWidth := runewidth.StringWidth(msg) + stripWidth := runewidth.StringWidth(stripansi.Strip(msg)) + maxCell := wc.W if (wc.C & DSyncWidth) != 0 { + cellCount := stripWidth if (wc.C & DextraSpace) != 0 { - runeCount++ + cellCount++ } - wc.wsync <- runeCount - max := <-wc.wsync - format = fmt.Sprintf(wc.dynFormat, ansiCount+max) - } else { - format = fmt.Sprintf(wc.dynFormat, ansiCount+wc.W) + wc.wsync <- cellCount + maxCell = <-wc.wsync } - return fmt.Sprintf(format, msg) + return wc.fill(msg, maxCell+(pureWidth-stripWidth)) } // Init initializes width related config. func (wc *WC) Init() WC { - wc.dynFormat = "%%" + wc.fill = runewidth.FillLeft if (wc.C & DidentRight) != 0 { - wc.dynFormat += "-" + wc.fill = runewidth.FillRight } - wc.dynFormat += "%ds" if (wc.C & DSyncWidth) != 0 { // it's deliberate choice to override wsync on each Init() call, // this way globals like WCSyncSpace can be reused diff --git a/vendor/github.com/vbauerster/mpb/v5/decor/elapsed.go b/vendor/github.com/vbauerster/mpb/v5/decor/elapsed.go index c9999a3b5..e389f1581 100644 --- a/vendor/github.com/vbauerster/mpb/v5/decor/elapsed.go +++ b/vendor/github.com/vbauerster/mpb/v5/decor/elapsed.go @@ -25,11 +25,11 @@ func Elapsed(style TimeStyle, wcc ...WC) Decorator { func NewElapsed(style TimeStyle, startTime time.Time, wcc ...WC) Decorator { var msg string producer := chooseTimeProducer(style) - f := func(s *Statistics) string { + fn := func(s Statistics) string { if !s.Completed { msg = producer(time.Since(startTime)) } return msg } - return Any(f, wcc...) + return Any(fn, wcc...) } diff --git a/vendor/github.com/vbauerster/mpb/v5/decor/eta.go b/vendor/github.com/vbauerster/mpb/v5/decor/eta.go index 6cb27a247..d03caa735 100644 --- a/vendor/github.com/vbauerster/mpb/v5/decor/eta.go +++ b/vendor/github.com/vbauerster/mpb/v5/decor/eta.go @@ -63,7 +63,7 @@ type movingAverageETA struct { producer func(time.Duration) string } -func (d *movingAverageETA) Decor(s *Statistics) string { +func (d *movingAverageETA) Decor(s Statistics) string { v := math.Round(d.average.Value()) remaining := time.Duration((s.Total - s.Current) * int64(v)) if d.normalizer != nil { @@ -117,7 +117,7 @@ type averageETA struct { producer func(time.Duration) string } -func (d *averageETA) Decor(s *Statistics) string { +func (d *averageETA) Decor(s Statistics) string { var remaining time.Duration if s.Current != 0 { durPerItem := float64(time.Since(d.startTime)) / float64(s.Current) diff --git a/vendor/github.com/vbauerster/mpb/v5/decor/merge.go b/vendor/github.com/vbauerster/mpb/v5/decor/merge.go index 520f13a7f..e41406a64 100644 --- a/vendor/github.com/vbauerster/mpb/v5/decor/merge.go +++ b/vendor/github.com/vbauerster/mpb/v5/decor/merge.go @@ -1,9 +1,10 @@ package decor import ( - "fmt" "strings" - "unicode/utf8" + + "github.com/acarl005/stripansi" + "github.com/mattn/go-runewidth" ) // Merge wraps its decorator argument with intention to sync width @@ -64,18 +65,18 @@ func (d *mergeDecorator) Base() Decorator { return d.Decorator } -func (d *mergeDecorator) Decor(s *Statistics) string { +func (d *mergeDecorator) Decor(s Statistics) string { msg := d.Decorator.Decor(s) - msgLen := utf8.RuneCountInString(msg) + pureWidth := runewidth.StringWidth(msg) + stripWidth := runewidth.StringWidth(stripansi.Strip(msg)) + cellCount := stripWidth if (d.wc.C & DextraSpace) != 0 { - msgLen++ + cellCount++ } - var total int - max := utf8.RuneCountInString(d.placeHolders[0].FormatMsg("")) - total += max - pw := (msgLen - max) / len(d.placeHolders) - rem := (msgLen - max) % len(d.placeHolders) + total := runewidth.StringWidth(d.placeHolders[0].FormatMsg("")) + pw := (cellCount - total) / len(d.placeHolders) + rem := (cellCount - total) % len(d.placeHolders) var diff int for i := 1; i < len(d.placeHolders); i++ { @@ -87,20 +88,20 @@ func (d *mergeDecorator) Decor(s *Statistics) string { width = 0 } } - max = utf8.RuneCountInString(ph.FormatMsg(strings.Repeat(" ", width))) + max := runewidth.StringWidth(ph.FormatMsg(strings.Repeat(" ", width))) total += max diff = max - pw } d.wc.wsync <- pw + rem - max = <-d.wc.wsync - return fmt.Sprintf(fmt.Sprintf(d.wc.dynFormat, max+total), msg) + max := <-d.wc.wsync + return d.wc.fill(msg, max+total+(pureWidth-stripWidth)) } type placeHolderDecorator struct { WC } -func (d *placeHolderDecorator) Decor(*Statistics) string { +func (d *placeHolderDecorator) Decor(Statistics) string { return "" } diff --git a/vendor/github.com/vbauerster/mpb/v5/decor/name.go b/vendor/github.com/vbauerster/mpb/v5/decor/name.go index a7d477e07..3af311254 100644 --- a/vendor/github.com/vbauerster/mpb/v5/decor/name.go +++ b/vendor/github.com/vbauerster/mpb/v5/decor/name.go @@ -8,5 +8,5 @@ package decor // `wcc` optional WC config // func Name(str string, wcc ...WC) Decorator { - return Any(func(*Statistics) string { return str }, wcc...) + return Any(func(Statistics) string { return str }, wcc...) } diff --git a/vendor/github.com/vbauerster/mpb/v5/decor/on_complete.go b/vendor/github.com/vbauerster/mpb/v5/decor/on_complete.go index 0a1526bf5..f46b19aba 100644 --- a/vendor/github.com/vbauerster/mpb/v5/decor/on_complete.go +++ b/vendor/github.com/vbauerster/mpb/v5/decor/on_complete.go @@ -24,7 +24,7 @@ type onCompleteWrapper struct { msg string } -func (d *onCompleteWrapper) Decor(s *Statistics) string { +func (d *onCompleteWrapper) Decor(s Statistics) string { if s.Completed { wc := d.GetConf() return wc.FormatMsg(d.msg) diff --git a/vendor/github.com/vbauerster/mpb/v5/decor/percentage.go b/vendor/github.com/vbauerster/mpb/v5/decor/percentage.go index 65ca7d318..d6314a619 100644 --- a/vendor/github.com/vbauerster/mpb/v5/decor/percentage.go +++ b/vendor/github.com/vbauerster/mpb/v5/decor/percentage.go @@ -50,7 +50,7 @@ func NewPercentage(format string, wcc ...WC) Decorator { if format == "" { format = "% d" } - f := func(s *Statistics) string { + f := func(s Statistics) string { p := internal.Percentage(s.Total, s.Current, 100) return fmt.Sprintf(format, percentageType(p)) } diff --git a/vendor/github.com/vbauerster/mpb/v5/decor/speed.go b/vendor/github.com/vbauerster/mpb/v5/decor/speed.go index 8a48e3f52..634edabfd 100644 --- a/vendor/github.com/vbauerster/mpb/v5/decor/speed.go +++ b/vendor/github.com/vbauerster/mpb/v5/decor/speed.go @@ -78,7 +78,7 @@ type movingAverageSpeed struct { msg string } -func (d *movingAverageSpeed) Decor(s *Statistics) string { +func (d *movingAverageSpeed) Decor(s Statistics) string { if !s.Completed { var speed float64 if v := d.average.Value(); v > 0 { @@ -140,7 +140,7 @@ type averageSpeed struct { msg string } -func (d *averageSpeed) Decor(s *Statistics) string { +func (d *averageSpeed) Decor(s Statistics) string { if !s.Completed { speed := float64(s.Current) / float64(time.Since(d.startTime)) d.msg = d.producer(speed * 1e9) diff --git a/vendor/github.com/vbauerster/mpb/v5/decor/spinner.go b/vendor/github.com/vbauerster/mpb/v5/decor/spinner.go index abfb2f76c..6871639db 100644 --- a/vendor/github.com/vbauerster/mpb/v5/decor/spinner.go +++ b/vendor/github.com/vbauerster/mpb/v5/decor/spinner.go @@ -12,7 +12,7 @@ func Spinner(frames []string, wcc ...WC) Decorator { frames = defaultSpinnerStyle } var count uint - f := func(s *Statistics) string { + f := func(s Statistics) string { frame := frames[count%uint(len(frames))] count++ return frame diff --git a/vendor/github.com/vbauerster/mpb/v5/go.mod b/vendor/github.com/vbauerster/mpb/v5/go.mod index 1d8d52934..389a19d54 100644 --- a/vendor/github.com/vbauerster/mpb/v5/go.mod +++ b/vendor/github.com/vbauerster/mpb/v5/go.mod @@ -3,8 +3,9 @@ module github.com/vbauerster/mpb/v5 require ( github.com/VividCortex/ewma v1.1.1 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d - golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5 - golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f // indirect + github.com/mattn/go-isatty v0.0.12 + github.com/mattn/go-runewidth v0.0.9 + golang.org/x/sys v0.0.0-20200519105757-fe76b779f299 ) go 1.14 diff --git a/vendor/github.com/vbauerster/mpb/v5/go.sum b/vendor/github.com/vbauerster/mpb/v5/go.sum index 99ca1bf67..dcaa8c553 100644 --- a/vendor/github.com/vbauerster/mpb/v5/go.sum +++ b/vendor/github.com/vbauerster/mpb/v5/go.sum @@ -2,12 +2,10 @@ github.com/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdc github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5 h1:Q7tZBpemrlsc2I7IyODzhtallWRSm4Q0d09pL6XbQtU= -golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f h1:gWF768j/LaZugp8dyS4UwsslYCYz9XgFxvlgsn0n9H8= -golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200519105757-fe76b779f299 h1:DYfZAGf2WMFjMxbgTjaC+2HC7NkNAQs+6Q8b9WEB/F4= +golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/vendor/github.com/vbauerster/mpb/v5/internal/width.go b/vendor/github.com/vbauerster/mpb/v5/internal/width.go new file mode 100644 index 000000000..35d528983 --- /dev/null +++ b/vendor/github.com/vbauerster/mpb/v5/internal/width.go @@ -0,0 +1,8 @@ +package internal + +func WidthForBarFiller(reqWidth, available int) int { + if reqWidth <= 0 || reqWidth >= available { + return available + } + return reqWidth +} diff --git a/vendor/github.com/vbauerster/mpb/v5/progress.go b/vendor/github.com/vbauerster/mpb/v5/progress.go index a366b9295..ac1ce50ab 100644 --- a/vendor/github.com/vbauerster/mpb/v5/progress.go +++ b/vendor/github.com/vbauerster/mpb/v5/progress.go @@ -19,8 +19,6 @@ import ( const ( // default RefreshRate prr = 120 * time.Millisecond - // default width - pwidth = 80 ) // Progress represents the container that renders Progress bars @@ -46,7 +44,7 @@ type pState struct { // following are provided/overrided by user idCount int - width int + reqWidth int popCompleted bool rr time.Duration uwg *sync.WaitGroup @@ -70,7 +68,6 @@ func New(options ...ContainerOption) *Progress { func NewWithContext(ctx context.Context, options ...ContainerOption) *Progress { s := &pState{ bHeap: priorityQueue{}, - width: pwidth, rr: prr, parkedBars: make(map[*Bar]*Bar), output: os.Stdout, @@ -113,7 +110,7 @@ func (p *Progress) AddSpinner(total int64, alignment SpinnerAlignment, options . // Panics if *Progress instance is done, i.e. called after *Progress.Wait(). func (p *Progress) Add(total int64, filler BarFiller, options ...BarOption) *Bar { if filler == nil { - filler = NewBarFiller(DefaultBarStyle, false) + filler = BarFillerFunc(func(io.Writer, int, decor.Statistics) {}) } p.bwg.Add(1) result := make(chan *Bar) @@ -215,14 +212,46 @@ func (p *Progress) serve(s *pState, cw *cwriter.Writer) { op(s) case <-p.refreshCh: if err := s.render(cw); err != nil { - go p.dlogger.Println(err) + p.dlogger.Println(err) } case <-s.shutdownNotifier: + if s.heapUpdated { + if err := s.render(cw); err != nil { + p.dlogger.Println(err) + } + } return } } } +func (s *pState) newTicker(done <-chan struct{}) chan time.Time { + ch := make(chan time.Time) + if s.shutdownNotifier == nil { + s.shutdownNotifier = make(chan struct{}) + } + go func() { + if s.renderDelay != nil { + <-s.renderDelay + } + if s.refreshSrc == nil { + ticker := time.NewTicker(s.rr) + defer ticker.Stop() + s.refreshSrc = ticker.C + } + for { + select { + case tick := <-s.refreshSrc: + ch <- tick + case <-done: + close(s.shutdownNotifier) + return + } + } + }() + return ch +} + func (s *pState) render(cw *cwriter.Writer) error { if s.heapUpdated { s.updateSyncMatrix() @@ -233,7 +262,7 @@ func (s *pState) render(cw *cwriter.Writer) error { tw, err := cw.GetWidth() if err != nil { - tw = s.width + tw = s.reqWidth } for i := 0; i < s.bHeap.Len(); i++ { bar := s.bHeap[i] @@ -250,11 +279,16 @@ func (s *pState) flush(cw *cwriter.Writer) error { b := heap.Pop(&s.bHeap).(*Bar) cw.ReadFrom(<-b.frameCh) if b.toShutdown { - // shutdown at next flush - // this ensures no bar ends up with less than 100% rendered - defer func() { + if b.recoveredPanic != nil { s.barShutdownQueue = append(s.barShutdownQueue, b) - }() + b.toShutdown = false + } else { + // shutdown at next flush + // this ensures no bar ends up with less than 100% rendered + defer func() { + s.barShutdownQueue = append(s.barShutdownQueue, b) + }() + } } lineCount += b.extendedLines + 1 bm[b] = struct{}{} @@ -295,33 +329,6 @@ func (s *pState) flush(cw *cwriter.Writer) error { return cw.Flush(lineCount) } -func (s *pState) newTicker(done <-chan struct{}) chan time.Time { - ch := make(chan time.Time) - if s.shutdownNotifier == nil { - s.shutdownNotifier = make(chan struct{}) - } - go func() { - if s.renderDelay != nil { - <-s.renderDelay - } - if s.refreshSrc == nil { - ticker := time.NewTicker(s.rr) - defer ticker.Stop() - s.refreshSrc = ticker.C - } - for { - select { - case tick := <-s.refreshSrc: - ch <- tick - case <-done: - close(s.shutdownNotifier) - return - } - } - }() - return ch -} - func (s *pState) updateSyncMatrix() { s.pMatrix = make(map[int][]chan int) s.aMatrix = make(map[int][]chan int) @@ -342,16 +349,13 @@ func (s *pState) updateSyncMatrix() { func (s *pState) makeBarState(total int64, filler BarFiller, options ...BarOption) *bState { bs := &bState{ + id: s.idCount, + priority: s.idCount, + reqWidth: s.reqWidth, total: total, - baseF: extractBaseFiller(filler), filler: filler, - priority: s.idCount, - id: s.idCount, - width: s.width, + extender: func(r io.Reader, _ int, _ decor.Statistics) (io.Reader, int) { return r, 0 }, debugOut: s.debugOut, - extender: func(r io.Reader, _ int, _ *decor.Statistics) (io.Reader, int) { - return r, 0 - }, } for _, opt := range options { @@ -360,13 +364,18 @@ func (s *pState) makeBarState(total int64, filler BarFiller, options ...BarOptio } } + if bs.middleware != nil { + bs.filler = bs.middleware(filler) + bs.middleware = nil + } + if s.popCompleted && !bs.noPop { bs.priority = -1 } - bs.bufP = bytes.NewBuffer(make([]byte, 0, bs.width)) - bs.bufB = bytes.NewBuffer(make([]byte, 0, bs.width)) - bs.bufA = bytes.NewBuffer(make([]byte, 0, bs.width)) + bs.bufP = bytes.NewBuffer(make([]byte, 0, 128)) + bs.bufB = bytes.NewBuffer(make([]byte, 0, 256)) + bs.bufA = bytes.NewBuffer(make([]byte, 0, 128)) return bs } @@ -387,13 +396,3 @@ func syncWidth(matrix map[int][]chan int) { }() } } - -func extractBaseFiller(f BarFiller) BarFiller { - type wrapper interface { - Base() BarFiller - } - if f, ok := f.(wrapper); ok { - return extractBaseFiller(f.Base()) - } - return f -} diff --git a/vendor/modules.txt b/vendor/modules.txt index 9794dc526..a42743c33 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -93,7 +93,7 @@ github.com/containers/common/pkg/config github.com/containers/common/pkg/sysinfo # github.com/containers/conmon v2.0.16+incompatible github.com/containers/conmon/runner/config -# github.com/containers/image/v5 v5.4.4 +# github.com/containers/image/v5 v5.4.5-0.20200529084758-46b2ee6aebb0 github.com/containers/image/v5/copy github.com/containers/image/v5/directory github.com/containers/image/v5/directory/explicitfilepath @@ -324,7 +324,7 @@ github.com/inconshreveable/mousetrap github.com/ishidawataru/sctp # github.com/json-iterator/go v1.1.9 github.com/json-iterator/go -# github.com/klauspost/compress v1.10.5 +# github.com/klauspost/compress v1.10.6 github.com/klauspost/compress/flate github.com/klauspost/compress/fse github.com/klauspost/compress/huff0 @@ -335,6 +335,10 @@ github.com/klauspost/compress/zstd/internal/xxhash github.com/klauspost/pgzip # github.com/konsorten/go-windows-terminal-sequences v1.0.3 github.com/konsorten/go-windows-terminal-sequences +# github.com/mattn/go-isatty v0.0.12 +github.com/mattn/go-isatty +# github.com/mattn/go-runewidth v0.0.9 +github.com/mattn/go-runewidth # github.com/mattn/go-shellwords v1.0.10 github.com/mattn/go-shellwords # github.com/matttproud/golang_protobuf_extensions v1.0.1 @@ -527,7 +531,7 @@ github.com/varlink/go/varlink/idl github.com/vbatts/tar-split/archive/tar github.com/vbatts/tar-split/tar/asm github.com/vbatts/tar-split/tar/storage -# github.com/vbauerster/mpb/v5 v5.0.4 +# github.com/vbauerster/mpb/v5 v5.2.2 github.com/vbauerster/mpb/v5 github.com/vbauerster/mpb/v5/cwriter github.com/vbauerster/mpb/v5/decor |