Update the vendor of containers/common

Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
This commit is contained in:
Daniel J Walsh
2022-01-20 13:29:54 -05:00
parent 43726bbc27
commit 923c58a8ee
53 changed files with 1015 additions and 632 deletions

View File

@@ -70,13 +70,10 @@ func Login(ctx context.Context, systemContext *types.SystemContext, opts *LoginO
systemContext = systemContextWithOptions(systemContext, opts.AuthFile, opts.CertDir)
var (
authConfig types.DockerAuthConfig
key, registry string
ref reference.Named
err error
)
l := len(args)
switch l {
switch len(args) {
case 0:
if !opts.AcceptUnspecifiedRegistry {
return errors.New("please provide a registry to login to")
@@ -88,26 +85,18 @@ func Login(ctx context.Context, systemContext *types.SystemContext, opts *LoginO
logrus.Debugf("registry not specified, default to the first registry %q from registries.conf", key)
case 1:
key, registry, ref, err = parseRegistryArgument(args[0], opts.AcceptRepositories)
key, registry, err = parseCredentialsKey(args[0], opts.AcceptRepositories)
if err != nil {
return err
}
default:
return errors.New("login accepts only one registry to login to")
}
if ref != nil {
authConfig, err = config.GetCredentialsForRef(systemContext, ref)
if err != nil {
return errors.Wrap(err, "get credentials for repository")
}
} else {
authConfig, err = config.GetCredentials(systemContext, registry)
if err != nil {
return errors.Wrap(err, "get credentials")
}
authConfig, err := config.GetCredentials(systemContext, key)
if err != nil {
return errors.Wrap(err, "get credentials")
}
if opts.GetLoginSet {
@@ -173,39 +162,44 @@ func Login(ctx context.Context, systemContext *types.SystemContext, opts *LoginO
return errors.Wrapf(err, "authenticating creds for %q", key)
}
// parseRegistryArgument verifies the provided arg depending if we accept
// repositories or not.
func parseRegistryArgument(arg string, acceptRepositories bool) (key, registry string, maybeRef reference.Named, err error) {
// parseCredentialsKey turns the provided argument into a valid credential key
// and computes the registry part.
func parseCredentialsKey(arg string, acceptRepositories bool) (key, registry string, err error) {
if !acceptRepositories {
registry = getRegistryName(arg)
key = registry
return key, registry, maybeRef, nil
return key, registry, nil
}
key = trimScheme(arg)
if key != arg {
return key, registry, nil, errors.New("credentials key has https[s]:// prefix")
return "", "", errors.New("credentials key has https[s]:// prefix")
}
registry = getRegistryName(key)
if registry == key {
// We cannot parse a reference from a registry, so we stop here
return key, registry, nil, nil
// The key is not namespaced
return key, registry, nil
}
ref, parseErr := reference.ParseNamed(key)
if parseErr != nil {
return key, registry, nil, errors.Wrapf(parseErr, "parse reference from %q", key)
// Sanity-check that the key looks reasonable (e.g. doesn't use invalid characters),
// and does not contain a tag or digest.
// WARNING: ref.Named() MUST NOT be used to compute key, because
// reference.ParseNormalizedNamed() turns docker.io/vendor to docker.io/library/vendor
// Ideally c/image should provide dedicated validation functionality.
ref, err := reference.ParseNormalizedNamed(key)
if err != nil {
return "", "", errors.Wrapf(err, "parse reference from %q", key)
}
if !reference.IsNameOnly(ref) {
return key, registry, nil, errors.Errorf("reference %q contains tag or digest", ref.String())
return "", "", errors.Errorf("reference %q contains tag or digest", ref.String())
}
refRegistry := reference.Domain(ref)
if refRegistry != registry { // This should never happen, check just to make sure
return "", "", fmt.Errorf("internal error: key %q registry mismatch, %q vs. %q", key, ref, refRegistry)
}
maybeRef = ref
registry = reference.Domain(ref)
return key, registry, maybeRef, nil
return key, registry, nil
}
// getRegistryName scrubs and parses the input to get the server name
@@ -271,15 +265,23 @@ func Logout(systemContext *types.SystemContext, opts *LogoutOptions, args []stri
}
systemContext = systemContextWithOptions(systemContext, opts.AuthFile, "")
if opts.All {
if len(args) != 0 {
return errors.New("--all takes no arguments")
}
if err := config.RemoveAllAuthentication(systemContext); err != nil {
return err
}
fmt.Fprintln(opts.Stdout, "Removed login credentials for all registries")
return nil
}
var (
key, registry string
ref reference.Named
err error
)
if len(args) > 1 {
return errors.New("logout accepts only one registry to logout from")
}
if len(args) == 0 && !opts.All {
switch len(args) {
case 0:
if !opts.AcceptUnspecifiedRegistry {
return errors.New("please provide a registry to logout from")
}
@@ -288,23 +290,15 @@ func Logout(systemContext *types.SystemContext, opts *LogoutOptions, args []stri
}
registry = key
logrus.Debugf("registry not specified, default to the first registry %q from registries.conf", key)
}
if len(args) != 0 {
if opts.All {
return errors.New("--all takes no arguments")
}
key, registry, ref, err = parseRegistryArgument(args[0], opts.AcceptRepositories)
case 1:
key, registry, err = parseCredentialsKey(args[0], opts.AcceptRepositories)
if err != nil {
return err
}
}
if opts.All {
if err := config.RemoveAllAuthentication(systemContext); err != nil {
return err
}
fmt.Fprintln(opts.Stdout, "Removed login credentials for all registries")
return nil
default:
return errors.New("logout accepts only one registry to logout from")
}
err = config.RemoveAuthentication(systemContext, key)
@@ -313,17 +307,9 @@ func Logout(systemContext *types.SystemContext, opts *LogoutOptions, args []stri
fmt.Fprintf(opts.Stdout, "Removed login credentials for %s\n", key)
return nil
case config.ErrNotLoggedIn:
var authConfig types.DockerAuthConfig
if ref != nil {
authConfig, err = config.GetCredentialsForRef(systemContext, ref)
if err != nil {
return errors.Wrap(err, "get credentials for repository")
}
} else {
authConfig, err = config.GetCredentials(systemContext, registry)
if err != nil {
return errors.Wrap(err, "get credentials")
}
authConfig, err := config.GetCredentials(systemContext, key)
if err != nil {
return errors.Wrap(err, "get credentials")
}
authInvalid := docker.CheckAuth(context.Background(), systemContext, authConfig.Username, authConfig.Password, registry)

View File

@@ -3,34 +3,44 @@ Package report provides helper structs/methods/funcs for formatting output
To format output for an array of structs:
w := report.NewWriterDefault(os.Stdout)
defer w.Flush()
ExamplePodman:
headers := report.Headers(struct {
ID string
}{}, nil)
t, _ := report.NewTemplate("command name").Parse("{{range .}}{{.ID}}{{end}}")
t.Execute(t, headers)
t.Execute(t, map[string]string{
f := report.New(os.Stdout, "Command Name")
f, _ := f.Parse(report.OriginPodman, "{{range .}}{{.ID}}{{end}}")
defer f.Flush()
if f.RenderHeaders {
f.Execute(headers)
}
f.Execute( map[string]string{
"ID":"fa85da03b40141899f3af3de6d27852b",
})
// t.IsTable() == false
or
w := report.NewWriterDefault(os.Stdout)
defer w.Flush()
// Output:
// ID
// fa85da03b40141899f3af3de6d27852b
ExampleUser:
headers := report.Headers(struct {
CID string
}{}, map[string]string{
"CID":"ID"})
t, _ := report.NewTemplate("command name").Parse("table {{.CID}}")
t.Execute(t, headers)
}{}, map[string]string{"CID":"ID"})
f, _ := report.New(os.Stdout, "Command Name").Parse(report.OriginUser, "table {{.CID}}")
defer f.Flush()
if f.RenderHeaders {
t.Execute(t, headers)
}
t.Execute(t,map[string]string{
"CID":"fa85da03b40141899f3af3de6d27852b",
})
// t.IsTable() == true
// Output:
// ID
// fa85da03b40141899f3af3de6d27852b
Helpers:
@@ -38,13 +48,20 @@ Helpers:
... process JSON and output
}
if report.HasTable(cmd.Flag("format").Value.String()) {
... "table" keyword prefix in format text
}
Template Functions:
The following template functions are added to the template when parsed:
- join strings.Join, {{join .Field separator}}
- json encode field as JSON {{ json .Field }}
- lower strings.ToLower {{ .Field | lower }}
- pad add spaces as prefix and suffix {{ pad . 2 2 }}
- split strings.Split {{ .Field | split }}
- title strings.Title {{ .Field | title }}
- truncate limit field length {{ truncate . 10 }}
- upper strings.ToUpper {{ .Field | upper }}
report.Funcs() may be used to add additional template functions.

View File

@@ -0,0 +1,151 @@
package report
import (
"io"
"strings"
"text/tabwriter"
"text/template"
)
// Flusher is the interface that wraps the Flush method.
type Flusher interface {
Flush() error
}
// NopFlusher represents a type which flush operation is nop.
type NopFlusher struct{}
// Flush is a nop operation.
func (f *NopFlusher) Flush() (err error) { return }
type Origin int
const (
OriginUnknown Origin = iota
OriginPodman
OriginUser
)
func (o Origin) String() string {
switch o {
case OriginPodman:
return "OriginPodman"
case OriginUser:
return "OriginUser"
default:
return "OriginUnknown"
}
}
// Formatter holds the configured Writer and parsed Template, additional state fields are
// maintained to assist in the podman command report writing.
type Formatter struct {
Origin Origin // Source of go template. OriginUser or OriginPodman
RenderHeaders bool // Hint, default behavior for given template is to include headers
RenderTable bool // Does template have "table" keyword
flusher Flusher // Flush any buffered formatted output
template *template.Template // Go text/template for formatting output
text string // value of canonical template after processing
writer io.Writer // Destination for formatted output
}
// Parse parses golang template returning a formatter
//
// - OriginPodman implies text is a template from podman code. Output will
// be filtered through a tabwriter.
//
// - OriginUser implies text is a template from a user. If template includes
// keyword "table" output will be filtered through a tabwriter.
func (f *Formatter) Parse(origin Origin, text string) (*Formatter, error) {
f.Origin = origin
switch {
case strings.HasPrefix(text, "table "):
f.RenderTable = true
text = "{{range .}}" + NormalizeFormat(text) + "{{end -}}"
case OriginUser == origin:
text = EnforceRange(NormalizeFormat(text))
default:
text = NormalizeFormat(text)
}
f.text = text
if f.RenderTable || origin == OriginPodman {
tw := tabwriter.NewWriter(f.writer, 12, 2, 2, ' ', tabwriter.StripEscape)
f.writer = tw
f.flusher = tw
f.RenderHeaders = true
}
tmpl, err := f.template.Funcs(template.FuncMap(DefaultFuncs)).Parse(text)
if err != nil {
return f, err
}
f.template = tmpl
return f, nil
}
// Funcs adds the elements of the argument map to the template's function map.
// A default template function will be replaced if there is a key collision.
func (f *Formatter) Funcs(funcMap template.FuncMap) *Formatter {
m := make(template.FuncMap, len(DefaultFuncs)+len(funcMap))
for k, v := range DefaultFuncs {
m[k] = v
}
for k, v := range funcMap {
m[k] = v
}
f.template = f.template.Funcs(funcMap)
return f
}
// Init either resets the given tabwriter with new values or wraps w in tabwriter with given values
func (f *Formatter) Init(w io.Writer, minwidth, tabwidth, padding int, padchar byte, flags uint) *Formatter {
flags |= tabwriter.StripEscape
if tw, ok := f.writer.(*tabwriter.Writer); ok {
tw = tw.Init(w, minwidth, tabwidth, padding, padchar, flags)
f.writer = tw
f.flusher = tw
} else {
tw = tabwriter.NewWriter(w, minwidth, tabwidth, padding, padchar, flags)
f.writer = tw
f.flusher = tw
}
return f
}
// Execute applies a parsed template to the specified data object,
// and writes the output to Formatter.Writer.
func (f *Formatter) Execute(data interface{}) error {
return f.template.Execute(f.writer, data)
}
// Flush should be called after the last call to Write to ensure
// that any data buffered in the Formatter is written to output. Any
// incomplete escape sequence at the end is considered
// complete for formatting purposes.
func (f *Formatter) Flush() error {
// Indirection is required here to prevent caller from having to know when
// value of Flusher may be changed.
return f.flusher.Flush()
}
// Writer returns the embedded io.Writer from Formatter
func (f *Formatter) Writer() io.Writer {
return f.writer
}
// New allocates a new, undefined Formatter with the given name and Writer
func New(output io.Writer, name string) *Formatter {
f := new(Formatter)
f.flusher = new(NopFlusher)
if flusher, ok := output.(Flusher); ok {
f.flusher = flusher
}
f.template = template.New(name)
f.writer = output
return f
}

View File

@@ -25,17 +25,19 @@ var tableReplacer = strings.NewReplacer(
"table ", "",
`\t`, "\t",
" ", "\t",
`\n`, "\n",
)
// escapedReplacer will clean up escaped characters from CLI
var escapedReplacer = strings.NewReplacer(
`\t`, "\t",
`\n`, "\n",
)
var DefaultFuncs = FuncMap{
"join": strings.Join,
"json": func(v interface{}) string {
buf := &bytes.Buffer{}
buf := new(bytes.Buffer)
enc := json.NewEncoder(buf)
enc.SetEscapeHTML(false)
enc.Encode(v)
@@ -157,7 +159,7 @@ func (t *Template) IsTable() bool {
return t.isTable
}
var rangeRegex = regexp.MustCompile(`{{\s*range\s*\.\s*}}.*{{\s*end\s*-?\s*}}`)
var rangeRegex = regexp.MustCompile(`(?s){{\s*range\s*\.\s*}}.*{{\s*end\s*-?\s*}}`)
// EnforceRange ensures that the format string contains a range
func EnforceRange(format string) string {

View File

@@ -45,8 +45,12 @@ func RetryIfNecessary(ctx context.Context, operation func() error, retryOptions
func isRetryable(err error) bool {
err = errors.Cause(err)
if err == context.Canceled || err == context.DeadlineExceeded {
switch err {
case nil:
return false
case context.Canceled, context.DeadlineExceeded:
return false
default: // continue
}
type unwrapper interface {
@@ -57,7 +61,8 @@ func isRetryable(err error) bool {
case errcode.Error:
switch e.Code {
case errcode.ErrorCodeUnauthorized, errcodev2.ErrorCodeNameUnknown, errcodev2.ErrorCodeManifestUnknown:
case errcode.ErrorCodeUnauthorized, errcode.ErrorCodeDenied,
errcodev2.ErrorCodeNameUnknown, errcodev2.ErrorCodeManifestUnknown:
return false
}
return true
@@ -86,7 +91,7 @@ func isRetryable(err error) bool {
}
}
return true
case unwrapper:
case unwrapper: // Test this last, because various error types might implement .Unwrap()
err = e.Unwrap()
return isRetryable(err)
}

View File

@@ -1058,7 +1058,8 @@ func (ic *imageCopier) copyUpdatedConfigAndManifest(ctx context.Context, instanc
instanceDigest = &manifestDigest
}
if err := ic.c.dest.PutManifest(ctx, man, instanceDigest); err != nil {
return nil, "", errors.Wrapf(err, "writing manifest %q", string(man))
logrus.Debugf("Error %v while writing manifest %q", err, string(man))
return nil, "", errors.Wrapf(err, "writing manifest")
}
return man, manifestDigest, nil
}
@@ -1072,20 +1073,15 @@ func (c *copier) newProgressPool() *mpb.Progress {
return mpb.New(mpb.WithWidth(40), mpb.WithOutput(c.progressOutput))
}
// customPartialBlobCounter provides a decorator function for the partial blobs retrieval progress bar
func customPartialBlobCounter(filler interface{}, wcc ...decor.WC) decor.Decorator {
producer := func(filler interface{}) decor.DecorFunc {
return func(s decor.Statistics) string {
if s.Total == 0 {
pairFmt := "%.1f / %.1f (skipped: %.1f)"
return fmt.Sprintf(pairFmt, decor.SizeB1024(s.Current), decor.SizeB1024(s.Total), decor.SizeB1024(s.Refill))
}
pairFmt := "%.1f / %.1f (skipped: %.1f = %.2f%%)"
percentage := 100.0 * float64(s.Refill) / float64(s.Total)
return fmt.Sprintf(pairFmt, decor.SizeB1024(s.Current), decor.SizeB1024(s.Total), decor.SizeB1024(s.Refill), percentage)
}
// customPartialBlobDecorFunc implements mpb.DecorFunc for the partial blobs retrieval progress bar
func customPartialBlobDecorFunc(s decor.Statistics) string {
if s.Total == 0 {
pairFmt := "%.1f / %.1f (skipped: %.1f)"
return fmt.Sprintf(pairFmt, decor.SizeB1024(s.Current), decor.SizeB1024(s.Total), decor.SizeB1024(s.Refill))
}
return decor.Any(producer(filler), wcc...)
pairFmt := "%.1f / %.1f (skipped: %.1f = %.2f%%)"
percentage := 100.0 * float64(s.Refill) / float64(s.Total)
return fmt.Sprintf(pairFmt, decor.SizeB1024(s.Current), decor.SizeB1024(s.Total), decor.SizeB1024(s.Refill), percentage)
}
// createProgressBar creates a mpb.Bar in pool. Note that if the copier's reportWriter
@@ -1110,7 +1106,6 @@ func (c *copier) createProgressBar(pool *mpb.Progress, partial bool, info types.
// Use a normal progress bar when we know the size (i.e., size > 0).
// Otherwise, use a spinner to indicate that something's happening.
var bar *mpb.Bar
sstyle := mpb.SpinnerStyle(".", "..", "...", "....", "").PositionLeft()
if info.Size > 0 {
if partial {
bar = pool.AddBar(info.Size,
@@ -1119,7 +1114,7 @@ func (c *copier) createProgressBar(pool *mpb.Progress, partial bool, info types.
decor.OnComplete(decor.Name(prefix), onComplete),
),
mpb.AppendDecorators(
customPartialBlobCounter(sstyle.Build()),
decor.Any(customPartialBlobDecorFunc),
),
)
} else {
@@ -1134,8 +1129,8 @@ func (c *copier) createProgressBar(pool *mpb.Progress, partial bool, info types.
)
}
} else {
bar = pool.Add(0,
sstyle.Build(),
bar = pool.New(0,
mpb.SpinnerStyle(".", "..", "...", "....", "").PositionLeft(),
mpb.BarFillerClearOnComplete(),
mpb.PrependDecorators(
decor.OnComplete(decor.Name(prefix), onComplete),

View File

@@ -711,7 +711,7 @@ func (c *dockerClient) getBearerToken(ctx context.Context, challenge challenge,
return nil, err
}
defer res.Body.Close()
if err := httpResponseToError(res, "Requesting bear token"); err != nil {
if err := httpResponseToError(res, "Requesting bearer token"); err != nil {
return nil, err
}
tokenBlob, err := iolimits.ReadAtMost(res.Body, iolimits.MaxAuthTokenBodySize)

View File

@@ -464,6 +464,18 @@ func (d *dockerImageDestination) PutManifest(ctx context.Context, m []byte, inst
}
return err
}
// A HTTP server may not be a registry at all, and just return 200 OK to everything
// (in particular that can fairly easily happen after tearing down a website and
// replacing it with a global 302 redirect to a new website, completely ignoring the
// path in the request); in that case we could “succeed” uploading a whole image.
// With docker/distribution we could rely on a Docker-Content-Digest header being present
// (because docker/distribution/registry/client has been failing uploads if it was missing),
// but that has been defined as explicitly optional by
// https://github.com/opencontainers/distribution-spec/blob/ec90a2af85fe4d612cf801e1815b95bfa40ae72b/spec.md#legacy-docker-support-http-headers
// So, just note the missing header in a debug log.
if v := res.Header.Values("Docker-Content-Digest"); len(v) == 0 {
logrus.Debugf("Manifest upload response didnt contain a Docker-Content-Digest header, it might not be a container registry")
}
return nil
}

View File

@@ -110,7 +110,8 @@ func GuessMIMEType(manifest []byte) string {
}
switch meta.MediaType {
case DockerV2Schema2MediaType, DockerV2ListMediaType: // A recognized type.
case DockerV2Schema2MediaType, DockerV2ListMediaType,
imgspecv1.MediaTypeImageManifest, imgspecv1.MediaTypeImageIndex: // A recognized type.
return meta.MediaType
}
// this is the only way the function can return DockerV2Schema1MediaType, and recognizing that is essential for stripping the JWS signatures = computing the correct manifest digest.
@@ -121,9 +122,9 @@ func GuessMIMEType(manifest []byte) string {
}
return DockerV2Schema1MediaType
case 2:
// best effort to understand if this is an OCI image since mediaType
// isn't in the manifest for OCI anymore
// for docker v2s2 meta.MediaType should have been set. But given the data, this is our best guess.
// Best effort to understand if this is an OCI image since mediaType
// wasn't in the manifest for OCI image-spec < 1.0.2.
// For docker v2s2 meta.MediaType should have been set. But given the data, this is our best guess.
ociMan := struct {
Config struct {
MediaType string `json:"mediaType"`

View File

@@ -66,6 +66,7 @@ func OCI1FromComponents(config imgspecv1.Descriptor, layers []imgspecv1.Descript
return &OCI1{
imgspecv1.Manifest{
Versioned: specs.Versioned{SchemaVersion: 2},
MediaType: imgspecv1.MediaTypeImageManifest,
Config: config,
Layers: layers,
},

View File

@@ -119,6 +119,7 @@ func OCI1IndexFromComponents(components []imgspecv1.Descriptor, annotations map[
index := OCI1Index{
imgspecv1.Index{
Versioned: imgspec.Versioned{SchemaVersion: 2},
MediaType: imgspecv1.MediaTypeImageIndex,
Manifests: make([]imgspecv1.Descriptor, len(components)),
Annotations: dupStringStringMap(annotations),
},
@@ -195,6 +196,7 @@ func OCI1IndexFromManifest(manifest []byte) (*OCI1Index, error) {
index := OCI1Index{
Index: imgspecv1.Index{
Versioned: imgspec.Versioned{SchemaVersion: 2},
MediaType: imgspecv1.MediaTypeImageIndex,
Manifests: []imgspecv1.Descriptor{},
Annotations: make(map[string]string),
},

View File

@@ -54,8 +54,8 @@ var (
// SetCredentials stores the username and password in a location
// appropriate for sys and the users configuration.
// A valid key can be either a registry hostname or additionally a namespace if
// the AuthenticationFileHelper is being unsed.
// A valid key is a repository, a namespace within a registry, or a registry hostname;
// using forms other than just a registry may fail depending on configuration.
// Returns a human-redable description of the location that was updated.
// NOTE: The return value is only intended to be read by humans; its form is not an API,
// it may change (or new forms can be added) any time.
@@ -128,11 +128,15 @@ func GetAllCredentials(sys *types.SystemContext) (map[string]types.DockerAuthCon
// possible sources, and then call `GetCredentials` on them. That
// prevents us from having to reverse engineer the logic in
// `GetCredentials`.
allRegistries := make(map[string]bool)
addRegistry := func(s string) {
allRegistries[s] = true
allKeys := make(map[string]bool)
addKey := func(s string) {
allKeys[s] = true
}
// To use GetCredentials, we must at least convert the URL forms into host names.
// While we're at it, well also canonicalize docker.io to the standard format.
normalizedDockerIORegistry := normalizeRegistry("docker.io")
helpers, err := sysregistriesv2.CredentialHelpers(sys)
if err != nil {
return nil, err
@@ -151,10 +155,14 @@ func GetAllCredentials(sys *types.SystemContext) (map[string]types.DockerAuthCon
// direct mapping to a registry, so we can just
// walk the map.
for registry := range auths.CredHelpers {
addRegistry(registry)
addKey(registry)
}
for registry := range auths.AuthConfigs {
addRegistry(registry)
for key := range auths.AuthConfigs {
key := normalizeAuthFileKey(key, path.legacyFormat)
if key == normalizedDockerIORegistry {
key = "docker.io"
}
addKey(key)
}
}
// External helpers.
@@ -166,7 +174,7 @@ func GetAllCredentials(sys *types.SystemContext) (map[string]types.DockerAuthCon
switch errors.Cause(err) {
case nil:
for registry := range creds {
addRegistry(registry)
addKey(registry)
}
case exec.ErrNotFound:
// It's okay if the helper doesn't exist.
@@ -179,17 +187,15 @@ func GetAllCredentials(sys *types.SystemContext) (map[string]types.DockerAuthCon
// Now use `GetCredentials` to the specific auth configs for each
// previously listed registry.
authConfigs := make(map[string]types.DockerAuthConfig)
for registry := range allRegistries {
authConf, err := GetCredentials(sys, registry)
for key := range allKeys {
authConf, err := GetCredentials(sys, key)
if err != nil {
if credentials.IsErrCredentialsNotFoundMessage(err.Error()) {
// Ignore if the credentials could not be found (anymore).
continue
}
// Note: we rely on the logging in `GetCredentials`.
return nil, err
}
authConfigs[registry] = authConf
if authConf != (types.DockerAuthConfig{}) {
authConfigs[key] = authConf
}
}
return authConfigs, nil
@@ -230,16 +236,14 @@ func getAuthFilePaths(sys *types.SystemContext, homeDir string) []authPath {
return paths
}
// GetCredentials returns the registry credentials stored in the
// registry-specific credential helpers or in the default global credentials
// helpers with falling back to using either auth.json
// file or .docker/config.json, including support for OAuth2 and IdentityToken.
// GetCredentials returns the registry credentials matching key, appropriate for
// sys and the users configuration.
// If an entry is not found, an empty struct is returned.
// A valid key is a repository, a namespace within a registry, or a registry hostname.
//
// GetCredentialsForRef should almost always be used in favor of this API to
// allow different credentials for different repositories on the same registry.
func GetCredentials(sys *types.SystemContext, registry string) (types.DockerAuthConfig, error) {
return getCredentialsWithHomeDir(sys, nil, registry, homedir.Get())
// GetCredentialsForRef should almost always be used in favor of this API.
func GetCredentials(sys *types.SystemContext, key string) (types.DockerAuthConfig, error) {
return getCredentialsWithHomeDir(sys, key, homedir.Get())
}
// GetCredentialsForRef returns the registry credentials necessary for
@@ -247,35 +251,39 @@ func GetCredentials(sys *types.SystemContext, registry string) (types.DockerAuth
// appropriate for sys and the users configuration.
// If an entry is not found, an empty struct is returned.
func GetCredentialsForRef(sys *types.SystemContext, ref reference.Named) (types.DockerAuthConfig, error) {
return getCredentialsWithHomeDir(sys, ref, reference.Domain(ref), homedir.Get())
return getCredentialsWithHomeDir(sys, ref.Name(), homedir.Get())
}
// getCredentialsWithHomeDir is an internal implementation detail of
// GetCredentialsForRef and GetCredentials. It exists only to allow testing it
// with an artificial home directory.
func getCredentialsWithHomeDir(sys *types.SystemContext, ref reference.Named, registry, homeDir string) (types.DockerAuthConfig, error) {
// consistency check of the ref and registry arguments
if ref != nil && reference.Domain(ref) != registry {
return types.DockerAuthConfig{}, errors.Errorf(
"internal error: provided reference domain %q name does not match registry %q",
reference.Domain(ref), registry,
)
func getCredentialsWithHomeDir(sys *types.SystemContext, key, homeDir string) (types.DockerAuthConfig, error) {
_, err := validateKey(key)
if err != nil {
return types.DockerAuthConfig{}, err
}
if sys != nil && sys.DockerAuthConfig != nil {
logrus.Debugf("Returning credentials for %s from DockerAuthConfig", registry)
logrus.Debugf("Returning credentials for %s from DockerAuthConfig", key)
return *sys.DockerAuthConfig, nil
}
var registry string // We compute this once because it is used in several places.
if firstSlash := strings.IndexRune(key, '/'); firstSlash != -1 {
registry = key[:firstSlash]
} else {
registry = key
}
// Anonymous function to query credentials from auth files.
getCredentialsFromAuthFiles := func() (types.DockerAuthConfig, string, error) {
for _, path := range getAuthFilePaths(sys, homeDir) {
authConfig, err := findAuthentication(ref, registry, path.path, path.legacyFormat)
authConfig, err := findCredentialsInFile(key, registry, path.path, path.legacyFormat)
if err != nil {
return types.DockerAuthConfig{}, "", err
}
if (authConfig.Username != "" && authConfig.Password != "") || authConfig.IdentityToken != "" {
if authConfig != (types.DockerAuthConfig{}) {
return authConfig, path.path, nil
}
}
@@ -291,57 +299,61 @@ func getCredentialsWithHomeDir(sys *types.SystemContext, ref reference.Named, re
for _, helper := range helpers {
var (
creds types.DockerAuthConfig
helperKey string
credHelperPath string
err error
)
switch helper {
// Special-case the built-in helper for auth files.
case sysregistriesv2.AuthenticationFileHelper:
helperKey = key
creds, credHelperPath, err = getCredentialsFromAuthFiles()
// External helpers.
default:
// This intentionally uses "registry", not "key"; we don't support namespaced
// credentials in helpers, but a "registry" is a valid parent of "key".
helperKey = registry
creds, err = getAuthFromCredHelper(helper, registry)
}
if err != nil {
logrus.Debugf("Error looking up credentials for %s in credential helper %s: %v", registry, helper, err)
logrus.Debugf("Error looking up credentials for %s in credential helper %s: %v", helperKey, helper, err)
multiErr = multierror.Append(multiErr, err)
continue
}
if len(creds.Username)+len(creds.Password)+len(creds.IdentityToken) == 0 {
continue
if creds != (types.DockerAuthConfig{}) {
msg := fmt.Sprintf("Found credentials for %s in credential helper %s", helperKey, helper)
if credHelperPath != "" {
msg = fmt.Sprintf("%s in file %s", msg, credHelperPath)
}
logrus.Debug(msg)
return creds, nil
}
msg := fmt.Sprintf("Found credentials for %s in credential helper %s", registry, helper)
if credHelperPath != "" {
msg = fmt.Sprintf("%s in file %s", msg, credHelperPath)
}
logrus.Debug(msg)
return creds, nil
}
if multiErr != nil {
return types.DockerAuthConfig{}, multiErr
}
logrus.Debugf("No credentials for %s found", registry)
logrus.Debugf("No credentials for %s found", key)
return types.DockerAuthConfig{}, nil
}
// GetAuthentication returns the registry credentials stored in the
// registry-specific credential helpers or in the default global credentials
// helpers with falling back to using either auth.json file or
// .docker/config.json
// GetAuthentication returns the registry credentials matching key, appropriate for
// sys and the users configuration.
// If an entry is not found, an empty struct is returned.
// A valid key is a repository, a namespace within a registry, or a registry hostname.
//
// Deprecated: This API only has support for username and password. To get the
// support for oauth2 in container registry authentication, we added the new
// GetCredentials API. The new API should be used and this API is kept to
// GetCredentialsForRef and GetCredentials API. The new API should be used and this API is kept to
// maintain backward compatibility.
func GetAuthentication(sys *types.SystemContext, registry string) (string, string, error) {
return getAuthenticationWithHomeDir(sys, registry, homedir.Get())
func GetAuthentication(sys *types.SystemContext, key string) (string, string, error) {
return getAuthenticationWithHomeDir(sys, key, homedir.Get())
}
// getAuthenticationWithHomeDir is an internal implementation detail of GetAuthentication,
// it exists only to allow testing it with an artificial home directory.
func getAuthenticationWithHomeDir(sys *types.SystemContext, registry, homeDir string) (string, string, error) {
auth, err := getCredentialsWithHomeDir(sys, nil, registry, homeDir)
func getAuthenticationWithHomeDir(sys *types.SystemContext, key, homeDir string) (string, string, error) {
auth, err := getCredentialsWithHomeDir(sys, key, homeDir)
if err != nil {
return "", "", err
}
@@ -353,8 +365,8 @@ func getAuthenticationWithHomeDir(sys *types.SystemContext, registry, homeDir st
// RemoveAuthentication removes credentials for `key` from all possible
// sources such as credential helpers and auth files.
// A valid key can be either a registry hostname or additionally a namespace if
// the AuthenticationFileHelper is being unsed.
// A valid key is a repository, a namespace within a registry, or a registry hostname;
// using forms other than just a registry may fail depending on configuration.
func RemoveAuthentication(sys *types.SystemContext, key string) error {
isNamespaced, err := validateKey(key)
if err != nil {
@@ -606,6 +618,10 @@ func getAuthFromCredHelper(credHelper, registry string) (types.DockerAuthConfig,
p := helperclient.NewShellProgramFunc(helperName)
creds, err := helperclient.Get(p, registry)
if err != nil {
if credentials.IsErrCredentialsNotFoundMessage(err.Error()) {
logrus.Debugf("Not logged in to %s with credential helper %s", registry, credHelper)
err = nil
}
return types.DockerAuthConfig{}, err
}
@@ -639,26 +655,27 @@ func deleteAuthFromCredHelper(credHelper, registry string) error {
return helperclient.Erase(p, registry)
}
// findAuthentication looks for auth of registry in path. If ref is
// not nil, then it will be taken into account when looking up the
// authentication credentials.
func findAuthentication(ref reference.Named, registry, path string, legacyFormat bool) (types.DockerAuthConfig, error) {
// findCredentialsInFile looks for credentials matching "key"
// (which is "registry" or a namespace in "registry") in "path".
func findCredentialsInFile(key, registry, path string, legacyFormat bool) (types.DockerAuthConfig, error) {
auths, err := readJSONFile(path, legacyFormat)
if err != nil {
return types.DockerAuthConfig{}, errors.Wrapf(err, "reading JSON file %q", path)
}
// First try cred helpers. They should always be normalized.
// This intentionally uses "registry", not "key"; we don't support namespaced
// credentials in helpers.
if ch, exists := auths.CredHelpers[registry]; exists {
return getAuthFromCredHelper(ch, registry)
}
// Support for different paths in auth.
// Support sub-registry namespaces in auth.
// (This is not a feature of ~/.docker/config.json; we support it even for
// those files as an extension.)
var keys []string
if !legacyFormat && ref != nil {
keys = authKeysForRef(ref)
if !legacyFormat {
keys = authKeysForKey(key)
} else {
keys = []string{registry}
}
@@ -689,23 +706,22 @@ func findAuthentication(ref reference.Named, registry, path string, legacyFormat
return types.DockerAuthConfig{}, nil
}
// authKeysForRef returns the valid paths for a provided reference. For example,
// when given a reference "quay.io/repo/ns/image:tag", then it would return
// authKeysForKey returns the keys matching a provided auth file key, in order
// from the best match to worst. For example,
// when given a repository key "quay.io/repo/ns/image", it returns
// - quay.io/repo/ns/image
// - quay.io/repo/ns
// - quay.io/repo
// - quay.io
func authKeysForRef(ref reference.Named) (res []string) {
name := ref.Name()
func authKeysForKey(key string) (res []string) {
for {
res = append(res, name)
res = append(res, key)
lastSlash := strings.LastIndex(name, "/")
lastSlash := strings.LastIndex(key, "/")
if lastSlash == -1 {
break
}
name = name[:lastSlash]
key = key[:lastSlash]
}
return res
@@ -759,11 +775,24 @@ func normalizeRegistry(registry string) string {
// validateKey verifies that the input key does not have a prefix that is not
// allowed and returns an indicator if the key is namespaced.
func validateKey(key string) (isNamespaced bool, err error) {
func validateKey(key string) (bool, error) {
if strings.HasPrefix(key, "http://") || strings.HasPrefix(key, "https://") {
return isNamespaced, errors.Errorf("key %s contains http[s]:// prefix", key)
return false, errors.Errorf("key %s contains http[s]:// prefix", key)
}
// Ideally this should only accept explicitly valid keys, compare
// validateIdentityRemappingPrefix. For now, just reject values that look
// like tagged or digested values.
if strings.ContainsRune(key, '@') {
return false, fmt.Errorf(`key %s contains a '@' character`, key)
}
firstSlash := strings.IndexRune(key, '/')
isNamespaced := firstSlash != -1
// Reject host/repo:tag, but allow localhost:5000 and localhost:5000/foo.
if isNamespaced && strings.ContainsRune(key[firstSlash+1:], ':') {
return false, fmt.Errorf(`key %s contains a ':' character after host[:port]`, key)
}
// check if the provided key contains one or more subpaths.
return strings.ContainsRune(key, '/'), nil
return isNamespaced, nil
}

View File

@@ -9,7 +9,11 @@ import (
"io/ioutil"
"strings"
"golang.org/x/crypto/openpgp"
// This code is used only to parse the data in an explicitly-untrusted
// code path, where cryptography is not relevant. For now, continue to
// use this frozen deprecated implementation. When mechanism_openpgp.go
// migrates to another implementation, this should migrate as well.
"golang.org/x/crypto/openpgp" //nolint:staticcheck
)
// SigningMechanism abstracts a way to sign binary blobs and verify their signatures.

View File

@@ -14,7 +14,13 @@ import (
"time"
"github.com/containers/storage/pkg/homedir"
"golang.org/x/crypto/openpgp"
// This is a fallback code; the primary recommendation is to use the gpgme mechanism
// implementation, which is out-of-process and more appropriate for handling long-term private key material
// than any Go implementation.
// For this verify-only fallback, we haven't reviewed any of the
// existing alternatives to choose; so, for now, continue to
// use this frozen deprecated implementation.
"golang.org/x/crypto/openpgp" //nolint:staticcheck
)
// A GPG/OpenPGP signing mechanism, implemented using x/crypto/openpgp.

View File

@@ -6,12 +6,12 @@ const (
// VersionMajor is for an API incompatible changes
VersionMajor = 5
// VersionMinor is for functionality in a backwards-compatible manner
VersionMinor = 17
VersionMinor = 18
// VersionPatch is for backwards-compatible bug fixes
VersionPatch = 1
VersionPatch = 0
// VersionDev indicates development branch. Releases will be empty string.
VersionDev = "-dev"
VersionDev = ""
)
// Version is the specification version that the package types support.

View File

@@ -1,11 +0,0 @@
language: go
arch:
- amd64
- ppc64le
go:
- 1.14.x
script:
- go test -race ./...
- for i in _examples/*/; do go build $i/*.go || exit 1; done

View File

@@ -1,8 +1,7 @@
# Multi Progress Bar
[![GoDoc](https://pkg.go.dev/badge/github.com/vbauerster/mpb)](https://pkg.go.dev/github.com/vbauerster/mpb/v7)
[![Build Status](https://travis-ci.org/vbauerster/mpb.svg?branch=master)](https://travis-ci.org/vbauerster/mpb)
[![Go Report Card](https://goreportcard.com/badge/github.com/vbauerster/mpb)](https://goreportcard.com/report/github.com/vbauerster/mpb)
[![Test status](https://github.com/vbauerster/mpb/actions/workflows/test.yml/badge.svg)](https://github.com/vbauerster/mpb/actions/workflows/test.yml)
[![Donate with PayPal](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.me/vbauerster)
**mpb** is a Go lib for rendering progress bars in terminal applications.
@@ -37,10 +36,10 @@ func main() {
total := 100
name := "Single Bar:"
// adding a single bar, which will inherit container's width
bar := p.Add(int64(total),
// progress bar filler with customized style
mpb.NewBarFiller(mpb.BarStyle().Lbound("╢").Filler("▌").Tip("▌").Padding("░").Rbound("╟")),
// create a single bar, which will inherit container's width
bar := p.New(int64(total),
// BarFillerBuilder with custom style
mpb.BarStyle().Lbound("╢").Filler("▌").Tip("▌").Padding("░").Rbound("╟"),
mpb.PrependDecorators(
// display our name with one space on the right
decor.Name(name, decor.WC{W: len(name) + 1, C: decor.DidentRight}),
@@ -66,7 +65,7 @@ func main() {
```go
var wg sync.WaitGroup
// passed &wg will be accounted at p.Wait() call
// passed wg will be accounted at p.Wait() call
p := mpb.New(mpb.WithWaitGroup(&wg))
total, numBars := 100, 3
wg.Add(numBars)
@@ -104,7 +103,7 @@ func main() {
}
}()
}
// Waiting for passed &wg and for all bars to complete and flush
// wait for passed wg and for all bars to complete and flush
p.Wait()
```

View File

@@ -8,6 +8,7 @@ import (
"log"
"runtime/debug"
"strings"
"sync"
"time"
"github.com/acarl005/stripansi"
@@ -50,11 +51,11 @@ type bState struct {
total int64
current int64
refill int64
lastN int64
iterated bool
lastIncrement int64
trimSpace bool
completed bool
completeFlushed bool
aborted bool
triggerComplete bool
dropOnComplete bool
noPop bool
@@ -164,12 +165,12 @@ func (b *Bar) TraverseDecorators(cb func(decor.Decorator)) {
}
// SetTotal sets total dynamically.
// If total is less than or equal to zero it takes progress' current value.
// If total is negative it takes progress' current value.
func (b *Bar) SetTotal(total int64, triggerComplete bool) {
select {
case b.operateState <- func(s *bState) {
s.triggerComplete = triggerComplete
if total <= 0 {
if total < 0 {
s.total = s.current
} else {
s.total = total
@@ -189,8 +190,7 @@ func (b *Bar) SetTotal(total int64, triggerComplete bool) {
func (b *Bar) SetCurrent(current int64) {
select {
case b.operateState <- func(s *bState) {
s.iterated = true
s.lastN = current - s.current
s.lastIncrement = current - s.current
s.current = current
if s.triggerComplete && s.current >= s.total {
s.current = s.total
@@ -214,10 +214,12 @@ func (b *Bar) IncrBy(n int) {
// IncrInt64 increments progress by amount of n.
func (b *Bar) IncrInt64(n int64) {
if n <= 0 {
return
}
select {
case b.operateState <- func(s *bState) {
s.iterated = true
s.lastN = n
s.lastIncrement = n
s.current += n
if s.triggerComplete && s.current >= s.total {
s.current = s.total
@@ -236,10 +238,18 @@ func (b *Bar) IncrInt64(n int64) {
func (b *Bar) DecoratorEwmaUpdate(dur time.Duration) {
select {
case b.operateState <- func(s *bState) {
ewmaIterationUpdate(false, s, dur)
if s.lastIncrement > 0 {
s.decoratorEwmaUpdate(dur)
s.lastIncrement = 0
} else {
panic("increment required before ewma iteration update")
}
}:
case <-b.done:
ewmaIterationUpdate(true, b.cacheState, dur)
if b.cacheState.lastIncrement > 0 {
b.cacheState.decoratorEwmaUpdate(dur)
b.cacheState.lastIncrement = 0
}
}
}
@@ -249,9 +259,7 @@ func (b *Bar) DecoratorEwmaUpdate(dur time.Duration) {
func (b *Bar) DecoratorAverageAdjust(start time.Time) {
select {
case b.operateState <- func(s *bState) {
for _, d := range s.averageDecorators {
d.AverageAdjust(start)
}
s.decoratorAverageAdjust(start)
}:
case <-b.done:
}
@@ -275,6 +283,8 @@ func (b *Bar) Abort(drop bool) {
close(done)
return
}
s.aborted = true
b.cancel()
// container must be run during lifetime of this inner goroutine
// we control this by done channel declared above
go func() {
@@ -295,7 +305,6 @@ func (b *Bar) Abort(drop bool) {
}
close(done) // release hold of Abort
}()
b.cancel()
}:
// guarantee: container is alive during lifetime of this hold
<-done
@@ -321,10 +330,7 @@ func (b *Bar) serve(ctx context.Context, s *bState) {
case op := <-b.operateState:
op(s)
case <-ctx.Done():
// Notifying decorators about shutdown event
for _, sl := range s.shutdownListeners {
sl.Shutdown()
}
s.decoratorShutdownNotify()
b.cacheState = s
close(b.done)
return
@@ -481,6 +487,45 @@ func (s *bState) wSyncTable() [][]chan int {
return table
}
func (s bState) decoratorEwmaUpdate(dur time.Duration) {
wg := new(sync.WaitGroup)
wg.Add(len(s.ewmaDecorators))
for _, d := range s.ewmaDecorators {
d := d
go func() {
d.EwmaUpdate(s.lastIncrement, dur)
wg.Done()
}()
}
wg.Wait()
}
func (s bState) decoratorAverageAdjust(start time.Time) {
wg := new(sync.WaitGroup)
wg.Add(len(s.averageDecorators))
for _, d := range s.averageDecorators {
d := d
go func() {
d.AverageAdjust(start)
wg.Done()
}()
}
wg.Wait()
}
func (s bState) decoratorShutdownNotify() {
wg := new(sync.WaitGroup)
wg.Add(len(s.shutdownListeners))
for _, d := range s.shutdownListeners {
d := d
go func() {
d.Shutdown()
wg.Done()
}()
}
wg.Wait()
}
func newStatistics(tw int, s *bState) decor.Statistics {
return decor.Statistics{
ID: s.id,
@@ -489,6 +534,7 @@ func newStatistics(tw int, s *bState) decor.Statistics {
Current: s.current,
Refill: s.refill,
Completed: s.completeFlushed,
Aborted: s.aborted,
}
}
@@ -499,17 +545,6 @@ func extractBaseDecorator(d decor.Decorator) decor.Decorator {
return d
}
func ewmaIterationUpdate(done bool, s *bState, dur time.Duration) {
if !done && !s.iterated {
panic("increment required before ewma iteration update")
} else {
s.iterated = false
}
for _, d := range s.ewmaDecorators {
d.EwmaUpdate(s.lastN, dur)
}
}
func makePanicExtender(p interface{}) extenderFunc {
pstr := fmt.Sprint(p)
stack := debug.Stack()

View File

@@ -9,31 +9,42 @@ import (
// BarFiller interface.
// Bar (without decorators) renders itself by calling BarFiller's Fill method.
//
// reqWidth is requested width, set by `func WithWidth(int) ContainerOption`.
// reqWidth is requested width set by `func WithWidth(int) ContainerOption`.
// If not set, it defaults to terminal width.
//
// Default implementations can be obtained via:
//
// func NewBarFiller(BarStyle()) BarFiller
// func NewBarFiller(SpinnerStyle()) BarFiller
//
type BarFiller interface {
Fill(w io.Writer, reqWidth int, stat decor.Statistics)
}
// BarFillerBuilder interface.
// Default implementations are:
//
// BarStyle()
// SpinnerStyle()
// NopStyle()
//
type BarFillerBuilder interface {
Build() BarFiller
}
// BarFillerFunc is function type adapter to convert function into BarFiller.
// BarFillerFunc is function type adapter to convert compatible function
// into BarFiller interface.
type BarFillerFunc func(w io.Writer, reqWidth int, stat decor.Statistics)
func (f BarFillerFunc) Fill(w io.Writer, reqWidth int, stat decor.Statistics) {
f(w, reqWidth, stat)
}
// BarFillerBuilderFunc is function type adapter to convert compatible
// function into BarFillerBuilder interface.
type BarFillerBuilderFunc func() BarFiller
func (f BarFillerBuilderFunc) Build() BarFiller {
return f()
}
// NewBarFiller constructs a BarFiller from provided BarFillerBuilder.
// Deprecated. Prefer using `*Progress.New(...)` directly.
func NewBarFiller(b BarFillerBuilder) BarFiller {
return b.Build()
}

14
vendor/github.com/vbauerster/mpb/v7/bar_filler_nop.go generated vendored Normal file
View File

@@ -0,0 +1,14 @@
package mpb
import (
"io"
"github.com/vbauerster/mpb/v7/decor"
)
// NopStyle provides BarFillerBuilder which builds NOP BarFiller.
func NopStyle() BarFillerBuilder {
return BarFillerBuilderFunc(func() BarFiller {
return BarFillerFunc(func(io.Writer, int, decor.Statistics) {})
})
}

View File

@@ -5,12 +5,20 @@ import (
"io"
"github.com/vbauerster/mpb/v7/decor"
"github.com/vbauerster/mpb/v7/internal"
)
// BarOption is a func option to alter default behavior of a bar.
type BarOption func(*bState)
func skipNil(decorators []decor.Decorator) (filtered []decor.Decorator) {
for _, d := range decorators {
if d != nil {
filtered = append(filtered, d)
}
}
return
}
func (s *bState) addDecorators(dest *[]decor.Decorator, decorators ...decor.Decorator) {
type mergeWrapper interface {
MergeUnwrap() []decor.Decorator
@@ -26,14 +34,14 @@ func (s *bState) addDecorators(dest *[]decor.Decorator, decorators ...decor.Deco
// AppendDecorators let you inject decorators to the bar's right side.
func AppendDecorators(decorators ...decor.Decorator) BarOption {
return func(s *bState) {
s.addDecorators(&s.aDecorators, decorators...)
s.addDecorators(&s.aDecorators, skipNil(decorators)...)
}
}
// PrependDecorators let you inject decorators to the bar's left side.
func PrependDecorators(decorators ...decor.Decorator) BarOption {
return func(s *bState) {
s.addDecorators(&s.pDecorators, decorators...)
s.addDecorators(&s.pDecorators, skipNil(decorators)...)
}
}
@@ -138,9 +146,12 @@ func BarNoPop() BarOption {
}
}
// BarOptional will invoke provided option only when pick is true.
func BarOptional(option BarOption, pick bool) BarOption {
return BarOptOn(option, internal.Predicate(pick))
// BarOptional will invoke provided option only when cond is true.
func BarOptional(option BarOption, cond bool) BarOption {
if cond {
return option
}
return nil
}
// BarOptOn will invoke provided option only when higher order predicate

View File

@@ -5,8 +5,6 @@ import (
"io/ioutil"
"sync"
"time"
"github.com/vbauerster/mpb/v7/internal"
)
// ContainerOption is a func option to alter default behavior of a bar
@@ -101,9 +99,12 @@ func PopCompletedMode() ContainerOption {
}
}
// ContainerOptional will invoke provided option only when pick is true.
func ContainerOptional(option ContainerOption, pick bool) ContainerOption {
return ContainerOptOn(option, internal.Predicate(pick))
// ContainerOptional will invoke provided option only when cond is true.
func ContainerOptional(option ContainerOption, cond bool) ContainerOption {
if cond {
return option
}
return nil
}
// ContainerOptOn will invoke provided option only when higher order

View File

@@ -53,6 +53,7 @@ type Statistics struct {
Current int64
Refill int64
Completed bool
Aborted bool
}
// Decorator interface.

View File

@@ -17,6 +17,9 @@ import (
// +----+--------+---------+--------+
//
func Merge(decorator Decorator, placeholders ...WC) Decorator {
if decorator == nil {
return nil
}
if _, ok := decorator.Sync(); !ok || len(placeholders) == 0 {
return decorator
}

41
vendor/github.com/vbauerster/mpb/v7/decor/on_abort.go generated vendored Normal file
View File

@@ -0,0 +1,41 @@
package decor
// OnAbort returns decorator, which wraps provided decorator with sole
// purpose to display provided message on abort event. It has no effect
// if bar.Abort(drop bool) is called with true argument.
//
// `decorator` Decorator to wrap
//
// `message` message to display on abort event
//
func OnAbort(decorator Decorator, message string) Decorator {
if decorator == nil {
return nil
}
d := &onAbortWrapper{
Decorator: decorator,
msg: message,
}
if md, ok := decorator.(*mergeDecorator); ok {
d.Decorator, md.Decorator = md.Decorator, d
return md
}
return d
}
type onAbortWrapper struct {
Decorator
msg string
}
func (d *onAbortWrapper) Decor(s Statistics) string {
if s.Aborted {
wc := d.GetConf()
return wc.FormatMsg(d.msg)
}
return d.Decorator.Decor(s)
}
func (d *onAbortWrapper) Base() Decorator {
return d.Decorator
}

View File

@@ -1,6 +1,6 @@
package decor
// OnComplete returns decorator, which wraps provided decorator, with
// OnComplete returns decorator, which wraps provided decorator with
// sole purpose to display provided message on complete event.
//
// `decorator` Decorator to wrap
@@ -8,6 +8,9 @@ package decor
// `message` message to display on complete event
//
func OnComplete(decorator Decorator, message string) Decorator {
if decorator == nil {
return nil
}
d := &onCompleteWrapper{
Decorator: decorator,
msg: message,

View File

@@ -0,0 +1,27 @@
package decor
// OnPredicate returns decorator if predicate evaluates to true.
//
// `decorator` Decorator
//
// `predicate` func() bool
//
func OnPredicate(decorator Decorator, predicate func() bool) Decorator {
if predicate() {
return decorator
}
return nil
}
// OnCondition returns decorator if condition is true.
//
// `decorator` Decorator
//
// `cond` bool
//
func OnCondition(decorator Decorator, cond bool) Decorator {
if cond {
return decorator
}
return nil
}

View File

@@ -4,7 +4,7 @@ require (
github.com/VividCortex/ewma v1.2.0
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d
github.com/mattn/go-runewidth v0.0.13
golang.org/x/sys v0.0.0-20210910150752-751e447fb3d0
golang.org/x/sys v0.0.0-20211214234402-4825e8c3871d
)
go 1.14

View File

@@ -6,5 +6,5 @@ github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
golang.org/x/sys v0.0.0-20210910150752-751e447fb3d0 h1:xrCZDmdtoloIiooiA9q0OQb9r8HejIHYoHGhGCe1pGg=
golang.org/x/sys v0.0.0-20210910150752-751e447fb3d0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211214234402-4825e8c3871d h1:1oIt9o40TWWI9FUaveVpUvBe13FNqBNVXy3ue2fcfkw=
golang.org/x/sys v0.0.0-20211214234402-4825e8c3871d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=

View File

@@ -1,6 +0,0 @@
package internal
// Predicate helper for internal use.
func Predicate(pick bool) func() bool {
return func() bool { return pick }
}

View File

@@ -99,17 +99,19 @@ func NewWithContext(ctx context.Context, options ...ContainerOption) *Progress {
return p
}
// AddBar creates a bar with default bar filler. Different filler can
// be chosen and applied via `*Progress.Add(...) *Bar` method.
// AddBar creates a bar with default bar filler.
func (p *Progress) AddBar(total int64, options ...BarOption) *Bar {
return p.Add(total, NewBarFiller(BarStyle()), options...)
return p.New(total, BarStyle(), options...)
}
// AddSpinner creates a bar with default spinner filler. Different
// filler can be chosen and applied via `*Progress.Add(...) *Bar`
// method.
// AddSpinner creates a bar with default spinner filler.
func (p *Progress) AddSpinner(total int64, options ...BarOption) *Bar {
return p.Add(total, NewBarFiller(SpinnerStyle()), options...)
return p.New(total, SpinnerStyle(), options...)
}
// New creates a bar with provided BarFillerBuilder.
func (p *Progress) New(total int64, builder BarFillerBuilder, options ...BarOption) *Bar {
return p.Add(total, builder.Build(), options...)
}
// Add creates a bar which renders itself by provided filler.
@@ -117,7 +119,7 @@ func (p *Progress) AddSpinner(total int64, options ...BarOption) *Bar {
// 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 = BarFillerFunc(func(io.Writer, int, decor.Statistics) {})
filler = NopStyle().Build()
}
p.bwg.Add(1)
result := make(chan *Bar)

View File

@@ -15,7 +15,7 @@ func (x *proxyReader) Read(p []byte) (int, error) {
n, err := x.ReadCloser.Read(p)
x.bar.IncrBy(n)
if err == io.EOF {
go x.bar.SetTotal(0, true)
go x.bar.SetTotal(-1, true)
}
return n, err
}
@@ -30,7 +30,7 @@ func (x *proxyWriterTo) WriteTo(w io.Writer) (int64, error) {
n, err := x.wt.WriteTo(w)
x.bar.IncrInt64(n)
if err == io.EOF {
go x.bar.SetTotal(0, true)
go x.bar.SetTotal(-1, true)
}
return n, err
}
@@ -38,14 +38,13 @@ func (x *proxyWriterTo) WriteTo(w io.Writer) (int64, error) {
type ewmaProxyReader struct {
io.ReadCloser // *proxyReader
bar *Bar
iT time.Time
}
func (x *ewmaProxyReader) Read(p []byte) (int, error) {
start := time.Now()
n, err := x.ReadCloser.Read(p)
if n > 0 {
x.bar.DecoratorEwmaUpdate(time.Since(x.iT))
x.iT = time.Now()
x.bar.DecoratorEwmaUpdate(time.Since(start))
}
return n, err
}
@@ -54,14 +53,13 @@ type ewmaProxyWriterTo struct {
io.ReadCloser // *ewmaProxyReader
wt io.WriterTo // *proxyWriterTo
bar *Bar
iT time.Time
}
func (x *ewmaProxyWriterTo) WriteTo(w io.Writer) (int64, error) {
start := time.Now()
n, err := x.wt.WriteTo(w)
if n > 0 {
x.bar.DecoratorEwmaUpdate(time.Since(x.iT))
x.iT = time.Now()
x.bar.DecoratorEwmaUpdate(time.Since(start))
}
return n, err
}
@@ -71,10 +69,9 @@ func newProxyReader(r io.Reader, bar *Bar) io.ReadCloser {
rc = &proxyReader{rc, bar}
if wt, isWriterTo := r.(io.WriterTo); bar.hasEwmaDecorators {
now := time.Now()
rc = &ewmaProxyReader{rc, bar, now}
rc = &ewmaProxyReader{rc, bar}
if isWriterTo {
rc = &ewmaProxyWriterTo{rc, wt, bar, now}
rc = &ewmaProxyWriterTo{rc, wt, bar}
}
} else if isWriterTo {
rc = &proxyWriterTo{rc, wt, bar}