vendor github.com/containers/image@v2.0.1
* progress bar: use spinners for unknown blob sizes * use 'containers_image_ostree' as build tag * ostree: default is no OStree support * Add "Env" to ImageInspectInfo * config.go: improve debug message * config.go: log where credentials come from * Fix typo in docs/containers-registries.conf.5.md * docker: delete: support all MIME types * Try harder in storageImageDestination.TryReusingBlob * docker: allow deleting OCI images * ostree: improve error message Signed-off-by: Valentin Rothberg <rothberg@redhat.com> Closes: #1730 Approved by: TomSweeneyRedHat
This commit is contained in:
		
							parent
							
								
									4ccb343cef
								
							
						
					
					
						commit
						5bab9b0651
					
				
							
								
								
									
										4
									
								
								go.mod
								
								
								
								
							
							
						
						
									
										4
									
								
								go.mod
								
								
								
								
							|  | @ -7,7 +7,7 @@ require ( | ||||||
| 	github.com/blang/semver v3.5.0+incompatible // indirect | 	github.com/blang/semver v3.5.0+incompatible // indirect | ||||||
| 	github.com/containerd/continuity v0.0.0-20181203112020-004b46473808 // indirect | 	github.com/containerd/continuity v0.0.0-20181203112020-004b46473808 // indirect | ||||||
| 	github.com/containernetworking/cni v0.7.0-rc2 | 	github.com/containernetworking/cni v0.7.0-rc2 | ||||||
| 	github.com/containers/image v2.0.0+incompatible | 	github.com/containers/image v2.0.1+incompatible | ||||||
| 	github.com/containers/storage v1.12.15 | 	github.com/containers/storage v1.12.15 | ||||||
| 	github.com/cyphar/filepath-securejoin v0.2.1 | 	github.com/cyphar/filepath-securejoin v0.2.1 | ||||||
| 	github.com/docker/distribution v0.0.0-20170817175659-5f6282db7d65 | 	github.com/docker/distribution v0.0.0-20170817175659-5f6282db7d65 | ||||||
|  | @ -44,7 +44,7 @@ require ( | ||||||
| 	github.com/spf13/pflag v1.0.3 | 	github.com/spf13/pflag v1.0.3 | ||||||
| 	github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2 | 	github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2 | ||||||
| 	github.com/ulikunitz/xz v0.5.5 // indirect | 	github.com/ulikunitz/xz v0.5.5 // indirect | ||||||
| 	github.com/vbauerster/mpb v3.3.4+incompatible // indirect | 	github.com/vbauerster/mpb v3.4.0+incompatible // indirect | ||||||
| 	github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect | 	github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect | ||||||
| 	github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect | 	github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect | ||||||
| 	github.com/xeipuuv/gojsonschema v1.1.0 // indirect | 	github.com/xeipuuv/gojsonschema v1.1.0 // indirect | ||||||
|  |  | ||||||
							
								
								
									
										4
									
								
								go.sum
								
								
								
								
							
							
						
						
									
										4
									
								
								go.sum
								
								
								
								
							|  | @ -27,6 +27,8 @@ github.com/containernetworking/cni v0.7.0-rc2 h1:2GGDhbwdWPY53iT7LXy+LBP76Ch2D/h | ||||||
| github.com/containernetworking/cni v0.7.0-rc2/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= | github.com/containernetworking/cni v0.7.0-rc2/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= | ||||||
| github.com/containers/image v2.0.0+incompatible h1:FTr6Br7jlIKNCKMjSOMbAxKp2keQ0//jzJaYNTVhauk= | github.com/containers/image v2.0.0+incompatible h1:FTr6Br7jlIKNCKMjSOMbAxKp2keQ0//jzJaYNTVhauk= | ||||||
| github.com/containers/image v2.0.0+incompatible/go.mod h1:8Vtij257IWSanUQKe1tAeNOm2sRVkSqQTVQ1IlwI3+M= | github.com/containers/image v2.0.0+incompatible/go.mod h1:8Vtij257IWSanUQKe1tAeNOm2sRVkSqQTVQ1IlwI3+M= | ||||||
|  | github.com/containers/image v2.0.1+incompatible h1:w39mlElA/aSFZ6moFa5N+A4MWu9c8hgdMiMMYnH94Hs= | ||||||
|  | github.com/containers/image v2.0.1+incompatible/go.mod h1:8Vtij257IWSanUQKe1tAeNOm2sRVkSqQTVQ1IlwI3+M= | ||||||
| github.com/containers/storage v1.12.10-0.20190725063046-8038df61d6f6 h1:c7Fq9bbRl0Ua6swRHAH8rkrK2fSt6K+ZBrXHD50kDR4= | github.com/containers/storage v1.12.10-0.20190725063046-8038df61d6f6 h1:c7Fq9bbRl0Ua6swRHAH8rkrK2fSt6K+ZBrXHD50kDR4= | ||||||
| github.com/containers/storage v1.12.10-0.20190725063046-8038df61d6f6/go.mod h1:QsZp4XMJjyPNNbQHZeyNW3OmhwsWviI+7S6iOcu6a4c= | github.com/containers/storage v1.12.10-0.20190725063046-8038df61d6f6/go.mod h1:QsZp4XMJjyPNNbQHZeyNW3OmhwsWviI+7S6iOcu6a4c= | ||||||
| github.com/containers/storage v1.12.13 h1:GtaLCY8p1Drlk1Oew581jGvB137UaO+kpz0HII67T0A= | github.com/containers/storage v1.12.13 h1:GtaLCY8p1Drlk1Oew581jGvB137UaO+kpz0HII67T0A= | ||||||
|  | @ -169,6 +171,8 @@ github.com/vbatts/tar-split v0.11.1 h1:0Odu65rhcZ3JZaPHxl7tCI3V/C/Q9Zf82UFravl02 | ||||||
| github.com/vbatts/tar-split v0.11.1/go.mod h1:LEuURwDEiWjRjwu46yU3KVGuUdVv/dcnpcEPSzR8z6g= | github.com/vbatts/tar-split v0.11.1/go.mod h1:LEuURwDEiWjRjwu46yU3KVGuUdVv/dcnpcEPSzR8z6g= | ||||||
| github.com/vbauerster/mpb v3.3.4+incompatible h1:DDIhnwmgTQIDZo+SWlEr5d6mJBxkOLBwCXPzunhEfJ4= | github.com/vbauerster/mpb v3.3.4+incompatible h1:DDIhnwmgTQIDZo+SWlEr5d6mJBxkOLBwCXPzunhEfJ4= | ||||||
| github.com/vbauerster/mpb v3.3.4+incompatible/go.mod h1:zAHG26FUhVKETRu+MWqYXcI70POlC6N8up9p1dID7SU= | github.com/vbauerster/mpb v3.3.4+incompatible/go.mod h1:zAHG26FUhVKETRu+MWqYXcI70POlC6N8up9p1dID7SU= | ||||||
|  | github.com/vbauerster/mpb v3.4.0+incompatible h1:mfiiYw87ARaeRW6x5gWwYRUawxaW1tLAD8IceomUCNw= | ||||||
|  | github.com/vbauerster/mpb v3.4.0+incompatible/go.mod h1:zAHG26FUhVKETRu+MWqYXcI70POlC6N8up9p1dID7SU= | ||||||
| github.com/vishvananda/netlink v1.0.0/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= | github.com/vishvananda/netlink v1.0.0/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= | ||||||
| github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= | github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= | ||||||
| github.com/vrothberg/storage v0.0.0-20190724065215-a1e42fd78930 h1:/LeIxi2kj5UYTJR9W35t5Pq2gqz03ZNoTURchTH3vc0= | github.com/vrothberg/storage v0.0.0-20190724065215-a1e42fd78930 h1:/LeIxi2kj5UYTJR9W35t5Pq2gqz03ZNoTURchTH3vc0= | ||||||
|  |  | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| #!/bin/bash | #!/bin/bash | ||||||
| if pkg-config ostree-1 2> /dev/null ; then | if pkg-config ostree-1 2> /dev/null ; then | ||||||
| 	echo constainers_image_ostree | 	echo containers_image_ostree | ||||||
| else | else | ||||||
| 	echo containers_image_ostree_stub | 	echo containers_image_ostree_stub | ||||||
| fi | fi | ||||||
|  |  | ||||||
|  | @ -597,15 +597,32 @@ func (c *copier) createProgressBar(pool *mpb.Progress, info types.BlobInfo, kind | ||||||
| 		prefix = prefix[:maxPrefixLen] | 		prefix = prefix[:maxPrefixLen] | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	bar := pool.AddBar(info.Size, | 	// Use a normal progress bar when we know the size (i.e., size > 0).
 | ||||||
| 		mpb.BarClearOnComplete(), | 	// Otherwise, use a spinner to indicate that something's happening.
 | ||||||
| 		mpb.PrependDecorators( | 	var bar *mpb.Bar | ||||||
| 			decor.Name(prefix), | 	if info.Size > 0 { | ||||||
| 		), | 		bar = pool.AddBar(info.Size, | ||||||
| 		mpb.AppendDecorators( | 			mpb.BarClearOnComplete(), | ||||||
| 			decor.OnComplete(decor.CountersKibiByte("%.1f / %.1f"), " "+onComplete), | 			mpb.PrependDecorators( | ||||||
| 		), | 				decor.Name(prefix), | ||||||
| 	) | 			), | ||||||
|  | 			mpb.AppendDecorators( | ||||||
|  | 				decor.OnComplete(decor.CountersKibiByte("%.1f / %.1f"), " "+onComplete), | ||||||
|  | 			), | ||||||
|  | 		) | ||||||
|  | 	} else { | ||||||
|  | 		bar = pool.AddSpinner(info.Size, | ||||||
|  | 			mpb.SpinnerOnLeft, | ||||||
|  | 			mpb.BarClearOnComplete(), | ||||||
|  | 			mpb.SpinnerStyle([]string{".", "..", "...", "....", ""}), | ||||||
|  | 			mpb.PrependDecorators( | ||||||
|  | 				decor.Name(prefix), | ||||||
|  | 			), | ||||||
|  | 			mpb.AppendDecorators( | ||||||
|  | 				decor.OnComplete(decor.Name(""), " "+onComplete), | ||||||
|  | 			), | ||||||
|  | 		) | ||||||
|  | 	} | ||||||
| 	if c.progressOutput == ioutil.Discard { | 	if c.progressOutput == ioutil.Discard { | ||||||
| 		c.Printf("Copying %s %s\n", kind, info.Digest) | 		c.Printf("Copying %s %s\n", kind, info.Digest) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -138,8 +138,9 @@ func (s *dockerImageSource) GetManifest(ctx context.Context, instanceDigest *dig | ||||||
| 
 | 
 | ||||||
| func (s *dockerImageSource) fetchManifest(ctx context.Context, tagOrDigest string) ([]byte, string, error) { | func (s *dockerImageSource) fetchManifest(ctx context.Context, tagOrDigest string) ([]byte, string, error) { | ||||||
| 	path := fmt.Sprintf(manifestPath, reference.Path(s.ref.ref), tagOrDigest) | 	path := fmt.Sprintf(manifestPath, reference.Path(s.ref.ref), tagOrDigest) | ||||||
| 	headers := make(map[string][]string) | 	headers := map[string][]string{ | ||||||
| 	headers["Accept"] = manifest.DefaultRequestedManifestMIMETypes | 		"Accept": manifest.DefaultRequestedManifestMIMETypes, | ||||||
|  | 	} | ||||||
| 	res, err := s.c.makeRequest(ctx, "GET", path, headers, nil, v2Auth, nil) | 	res, err := s.c.makeRequest(ctx, "GET", path, headers, nil, v2Auth, nil) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, "", err | 		return nil, "", err | ||||||
|  | @ -381,11 +382,9 @@ func deleteImage(ctx context.Context, sys *types.SystemContext, ref dockerRefere | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// When retrieving the digest from a registry >= 2.3 use the following header:
 | 	headers := map[string][]string{ | ||||||
| 	//   "Accept": "application/vnd.docker.distribution.manifest.v2+json"
 | 		"Accept": manifest.DefaultRequestedManifestMIMETypes, | ||||||
| 	headers := make(map[string][]string) | 	} | ||||||
| 	headers["Accept"] = []string{manifest.DockerV2Schema2MediaType} |  | ||||||
| 
 |  | ||||||
| 	refTail, err := ref.tagOrDigest() | 	refTail, err := ref.tagOrDigest() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
|  |  | ||||||
|  | @ -15,24 +15,24 @@ import ( | ||||||
| 	"github.com/containers/image/manifest" | 	"github.com/containers/image/manifest" | ||||||
| 	"github.com/containers/image/pkg/compression" | 	"github.com/containers/image/pkg/compression" | ||||||
| 	"github.com/containers/image/types" | 	"github.com/containers/image/types" | ||||||
| 	"github.com/opencontainers/go-digest" | 	digest "github.com/opencontainers/go-digest" | ||||||
| 	"github.com/pkg/errors" | 	"github.com/pkg/errors" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // Source is a partial implementation of types.ImageSource for reading from tarPath.
 | // Source is a partial implementation of types.ImageSource for reading from tarPath.
 | ||||||
| type Source struct { | type Source struct { | ||||||
| 	tarPath              string | 	tarPath              string | ||||||
| 	removeTarPathOnClose bool      // Remove temp file on close if true
 | 	removeTarPathOnClose bool // Remove temp file on close if true
 | ||||||
| 	cacheDataLock        sync.Once // Atomic way to ensure that ensureCachedDataIsPresent is only invoked once
 |  | ||||||
| 	// The following data is only available after ensureCachedDataIsPresent() succeeds
 | 	// The following data is only available after ensureCachedDataIsPresent() succeeds
 | ||||||
| 	cacheDataResult   error         // The return value of ensureCachedDataIsPresent, since it should be as safe to cache as the side effects
 |  | ||||||
| 	tarManifest       *ManifestItem // nil if not available yet.
 | 	tarManifest       *ManifestItem // nil if not available yet.
 | ||||||
| 	configBytes       []byte | 	configBytes       []byte | ||||||
| 	configDigest      digest.Digest | 	configDigest      digest.Digest | ||||||
| 	orderedDiffIDList []digest.Digest | 	orderedDiffIDList []digest.Digest | ||||||
| 	knownLayers       map[digest.Digest]*layerInfo | 	knownLayers       map[digest.Digest]*layerInfo | ||||||
| 	// Other state
 | 	// Other state
 | ||||||
| 	generatedManifest []byte // Private cache for GetManifest(), nil if not set yet.
 | 	generatedManifest []byte    // Private cache for GetManifest(), nil if not set yet.
 | ||||||
|  | 	cacheDataLock     sync.Once // Private state for ensureCachedDataIsPresent to make it concurrency-safe
 | ||||||
|  | 	cacheDataResult   error     // Private state for ensureCachedDataIsPresent
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type layerInfo struct { | type layerInfo struct { | ||||||
|  | @ -201,49 +201,52 @@ func (s *Source) readTarComponent(path string) ([]byte, error) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // ensureCachedDataIsPresent loads data necessary for any of the public accessors.
 | // ensureCachedDataIsPresent loads data necessary for any of the public accessors.
 | ||||||
|  | // It is safe to call this from multi-threaded code.
 | ||||||
| func (s *Source) ensureCachedDataIsPresent() error { | func (s *Source) ensureCachedDataIsPresent() error { | ||||||
| 	s.cacheDataLock.Do(func() { | 	s.cacheDataLock.Do(func() { | ||||||
| 		// Read and parse manifest.json
 | 		s.cacheDataResult = s.ensureCachedDataIsPresentPrivate() | ||||||
| 		tarManifest, err := s.loadTarManifest() |  | ||||||
| 		if err != nil { |  | ||||||
| 			s.cacheDataResult = err |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// Check to make sure length is 1
 |  | ||||||
| 		if len(tarManifest) != 1 { |  | ||||||
| 			s.cacheDataResult = errors.Errorf("Unexpected tar manifest.json: expected 1 item, got %d", len(tarManifest)) |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// Read and parse config.
 |  | ||||||
| 		configBytes, err := s.readTarComponent(tarManifest[0].Config) |  | ||||||
| 		if err != nil { |  | ||||||
| 			s.cacheDataResult = err |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
| 		var parsedConfig manifest.Schema2Image // There's a lot of info there, but we only really care about layer DiffIDs.
 |  | ||||||
| 		if err := json.Unmarshal(configBytes, &parsedConfig); err != nil { |  | ||||||
| 			s.cacheDataResult = errors.Wrapf(err, "Error decoding tar config %s", tarManifest[0].Config) |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		knownLayers, err := s.prepareLayerData(&tarManifest[0], &parsedConfig) |  | ||||||
| 		if err != nil { |  | ||||||
| 			s.cacheDataResult = err |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// Success; commit.
 |  | ||||||
| 		s.tarManifest = &tarManifest[0] |  | ||||||
| 		s.configBytes = configBytes |  | ||||||
| 		s.configDigest = digest.FromBytes(configBytes) |  | ||||||
| 		s.orderedDiffIDList = parsedConfig.RootFS.DiffIDs |  | ||||||
| 		s.knownLayers = knownLayers |  | ||||||
| 	}) | 	}) | ||||||
| 	return s.cacheDataResult | 	return s.cacheDataResult | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // ensureCachedDataIsPresentPrivate is a private implementation detail of ensureCachedDataIsPresent.
 | ||||||
|  | // Call ensureCachedDataIsPresent instead.
 | ||||||
|  | func (s *Source) ensureCachedDataIsPresentPrivate() error { | ||||||
|  | 	// Read and parse manifest.json
 | ||||||
|  | 	tarManifest, err := s.loadTarManifest() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Check to make sure length is 1
 | ||||||
|  | 	if len(tarManifest) != 1 { | ||||||
|  | 		return errors.Errorf("Unexpected tar manifest.json: expected 1 item, got %d", len(tarManifest)) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Read and parse config.
 | ||||||
|  | 	configBytes, err := s.readTarComponent(tarManifest[0].Config) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	var parsedConfig manifest.Schema2Image // There's a lot of info there, but we only really care about layer DiffIDs.
 | ||||||
|  | 	if err := json.Unmarshal(configBytes, &parsedConfig); err != nil { | ||||||
|  | 		return errors.Wrapf(err, "Error decoding tar config %s", tarManifest[0].Config) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	knownLayers, err := s.prepareLayerData(&tarManifest[0], &parsedConfig) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Success; commit.
 | ||||||
|  | 	s.tarManifest = &tarManifest[0] | ||||||
|  | 	s.configBytes = configBytes | ||||||
|  | 	s.configDigest = digest.FromBytes(configBytes) | ||||||
|  | 	s.orderedDiffIDList = parsedConfig.RootFS.DiffIDs | ||||||
|  | 	s.knownLayers = knownLayers | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // loadTarManifest loads and decodes the manifest.json.
 | // loadTarManifest loads and decodes the manifest.json.
 | ||||||
| func (s *Source) loadTarManifest() ([]ManifestItem, error) { | func (s *Source) loadTarManifest() ([]ManifestItem, error) { | ||||||
| 	// FIXME? Do we need to deal with the legacy format?
 | 	// FIXME? Do we need to deal with the legacy format?
 | ||||||
|  |  | ||||||
|  | @ -226,6 +226,7 @@ func (m *Schema1) Inspect(_ func(types.BlobInfo) ([]byte, error)) (*types.ImageI | ||||||
| 	} | 	} | ||||||
| 	if s1.Config != nil { | 	if s1.Config != nil { | ||||||
| 		i.Labels = s1.Config.Labels | 		i.Labels = s1.Config.Labels | ||||||
|  | 		i.Env = s1.Config.Env | ||||||
| 	} | 	} | ||||||
| 	return i, nil | 	return i, nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -241,6 +241,7 @@ func (m *Schema2) Inspect(configGetter func(types.BlobInfo) ([]byte, error)) (*t | ||||||
| 	} | 	} | ||||||
| 	if s2.Config != nil { | 	if s2.Config != nil { | ||||||
| 		i.Labels = s2.Config.Labels | 		i.Labels = s2.Config.Labels | ||||||
|  | 		i.Env = s2.Config.Env | ||||||
| 	} | 	} | ||||||
| 	return i, nil | 	return i, nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -116,6 +116,7 @@ func (m *OCI1) Inspect(configGetter func(types.BlobInfo) ([]byte, error)) (*type | ||||||
| 		Architecture:  v1.Architecture, | 		Architecture:  v1.Architecture, | ||||||
| 		Os:            v1.OS, | 		Os:            v1.OS, | ||||||
| 		Layers:        layerInfosToStrings(m.LayerInfos()), | 		Layers:        layerInfosToStrings(m.LayerInfos()), | ||||||
|  | 		Env:           d1.Config.Env, | ||||||
| 	} | 	} | ||||||
| 	return i, nil | 	return i, nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,4 +1,4 @@ | ||||||
| // +build !containers_image_ostree_stub
 | // +build containers_image_ostree
 | ||||||
| 
 | 
 | ||||||
| package ostree | package ostree | ||||||
| 
 | 
 | ||||||
|  | @ -218,7 +218,7 @@ func fixFiles(selinuxHnd *C.struct_selabel_handle, root string, dir string, user | ||||||
| 				defer C.free(unsafe.Pointer(fullpathC)) | 				defer C.free(unsafe.Pointer(fullpathC)) | ||||||
| 				res, err = C.lsetfilecon_raw(fullpathC, context) | 				res, err = C.lsetfilecon_raw(fullpathC, context) | ||||||
| 				if int(res) < 0 { | 				if int(res) < 0 { | ||||||
| 					return errors.Wrapf(err, "cannot setfilecon_raw %s", fullpath) | 					return errors.Wrapf(err, "cannot setfilecon_raw %s to %s", fullpath, C.GoString(context)) | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | @ -1,4 +1,4 @@ | ||||||
| // +build !containers_image_ostree_stub
 | // +build containers_image_ostree
 | ||||||
| 
 | 
 | ||||||
| package ostree | package ostree | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,4 +1,4 @@ | ||||||
| // +build !containers_image_ostree_stub
 | // +build containers_image_ostree
 | ||||||
| 
 | 
 | ||||||
| package ostree | package ostree | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -56,6 +56,7 @@ func SetAuthentication(sys *types.SystemContext, registry, username, password st | ||||||
| // If an entry is not found empty strings are returned for the username and password
 | // If an entry is not found empty strings are returned for the username and password
 | ||||||
| func GetAuthentication(sys *types.SystemContext, registry string) (string, string, error) { | func GetAuthentication(sys *types.SystemContext, registry string) (string, string, error) { | ||||||
| 	if sys != nil && sys.DockerAuthConfig != nil { | 	if sys != nil && sys.DockerAuthConfig != nil { | ||||||
|  | 		logrus.Debug("Returning credentials from DockerAuthConfig") | ||||||
| 		return sys.DockerAuthConfig.Username, sys.DockerAuthConfig.Password, nil | 		return sys.DockerAuthConfig.Username, sys.DockerAuthConfig.Password, nil | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -76,12 +77,15 @@ func GetAuthentication(sys *types.SystemContext, registry string) (string, strin | ||||||
| 		legacyFormat := path == dockerLegacyPath | 		legacyFormat := path == dockerLegacyPath | ||||||
| 		username, password, err := findAuthentication(registry, path, legacyFormat) | 		username, password, err := findAuthentication(registry, path, legacyFormat) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
|  | 			logrus.Debugf("Credentials not found") | ||||||
| 			return "", "", err | 			return "", "", err | ||||||
| 		} | 		} | ||||||
| 		if username != "" && password != "" { | 		if username != "" && password != "" { | ||||||
|  | 			logrus.Debugf("Returning credentials from %s", path) | ||||||
| 			return username, password, nil | 			return username, password, nil | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  | 	logrus.Debugf("Credentials not found") | ||||||
| 	return "", "", nil | 	return "", "", nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										24
									
								
								vendor/github.com/containers/image/pkg/sysregistriesv2/system_registries_v2.go
								
								
									generated
								
								
									vendored
								
								
							
							
						
						
									
										24
									
								
								vendor/github.com/containers/image/pkg/sysregistriesv2/system_registries_v2.go
								
								
									generated
								
								
									vendored
								
								
							|  | @ -30,10 +30,10 @@ const builtinRegistriesConfPath = "/etc/containers/registries.conf" | ||||||
| // Endpoint describes a remote location of a registry.
 | // Endpoint describes a remote location of a registry.
 | ||||||
| type Endpoint struct { | type Endpoint struct { | ||||||
| 	// The endpoint's remote location.
 | 	// The endpoint's remote location.
 | ||||||
| 	Location string `toml:"location"` | 	Location string `toml:"location,omitempty"` | ||||||
| 	// If true, certs verification will be skipped and HTTP (non-TLS)
 | 	// If true, certs verification will be skipped and HTTP (non-TLS)
 | ||||||
| 	// connections will be allowed.
 | 	// connections will be allowed.
 | ||||||
| 	Insecure bool `toml:"insecure"` | 	Insecure bool `toml:"insecure,omitempty"` | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // rewriteReference will substitute the provided reference `prefix` to the
 | // rewriteReference will substitute the provided reference `prefix` to the
 | ||||||
|  | @ -56,22 +56,22 @@ func (e *Endpoint) rewriteReference(ref reference.Named, prefix string) (referen | ||||||
| 
 | 
 | ||||||
| // Registry represents a registry.
 | // Registry represents a registry.
 | ||||||
| type Registry struct { | type Registry struct { | ||||||
| 	// A registry is an Endpoint too
 |  | ||||||
| 	Endpoint |  | ||||||
| 	// The registry's mirrors.
 |  | ||||||
| 	Mirrors []Endpoint `toml:"mirror"` |  | ||||||
| 	// If true, pulling from the registry will be blocked.
 |  | ||||||
| 	Blocked bool `toml:"blocked"` |  | ||||||
| 	// If true, mirrors will only be used for digest pulls. Pulling images by
 |  | ||||||
| 	// tag can potentially yield different images, depending on which endpoint
 |  | ||||||
| 	// we pull from.  Forcing digest-pulls for mirrors avoids that issue.
 |  | ||||||
| 	MirrorByDigestOnly bool `toml:"mirror-by-digest-only"` |  | ||||||
| 	// Prefix is used for matching images, and to translate one namespace to
 | 	// Prefix is used for matching images, and to translate one namespace to
 | ||||||
| 	// another.  If `Prefix="example.com/bar"`, `location="example.com/foo/bar"`
 | 	// another.  If `Prefix="example.com/bar"`, `location="example.com/foo/bar"`
 | ||||||
| 	// and we pull from "example.com/bar/myimage:latest", the image will
 | 	// and we pull from "example.com/bar/myimage:latest", the image will
 | ||||||
| 	// effectively be pulled from "example.com/foo/bar/myimage:latest".
 | 	// effectively be pulled from "example.com/foo/bar/myimage:latest".
 | ||||||
| 	// If no Prefix is specified, it defaults to the specified location.
 | 	// If no Prefix is specified, it defaults to the specified location.
 | ||||||
| 	Prefix string `toml:"prefix"` | 	Prefix string `toml:"prefix"` | ||||||
|  | 	// A registry is an Endpoint too
 | ||||||
|  | 	Endpoint | ||||||
|  | 	// The registry's mirrors.
 | ||||||
|  | 	Mirrors []Endpoint `toml:"mirror,omitempty"` | ||||||
|  | 	// If true, pulling from the registry will be blocked.
 | ||||||
|  | 	Blocked bool `toml:"blocked,omitempty"` | ||||||
|  | 	// If true, mirrors will only be used for digest pulls. Pulling images by
 | ||||||
|  | 	// tag can potentially yield different images, depending on which endpoint
 | ||||||
|  | 	// we pull from.  Forcing digest-pulls for mirrors avoids that issue.
 | ||||||
|  | 	MirrorByDigestOnly bool `toml:"mirror-by-digest-only,omitempty"` | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // PullSource consists of an Endpoint and a Reference. Note that the reference is
 | // PullSource consists of an Endpoint and a Reference. Note that the reference is
 | ||||||
|  |  | ||||||
|  | @ -491,14 +491,21 @@ func (s *storageImageDestination) TryReusingBlob(ctx context.Context, blobinfo t | ||||||
| 
 | 
 | ||||||
| 	// Does the blob correspond to a known DiffID which we already have available?
 | 	// Does the blob correspond to a known DiffID which we already have available?
 | ||||||
| 	// Because we must return the size, which is unknown for unavailable compressed blobs, the returned BlobInfo refers to the
 | 	// Because we must return the size, which is unknown for unavailable compressed blobs, the returned BlobInfo refers to the
 | ||||||
| 	// uncompressed layer, and that can happen only if canSubstitute.
 | 	// uncompressed layer, and that can happen only if canSubstitute, or if the incoming manifest already specifies the size.
 | ||||||
| 	if canSubstitute { | 	if canSubstitute || blobinfo.Size != -1 { | ||||||
| 		if uncompressedDigest := cache.UncompressedDigest(blobinfo.Digest); uncompressedDigest != "" && uncompressedDigest != blobinfo.Digest { | 		if uncompressedDigest := cache.UncompressedDigest(blobinfo.Digest); uncompressedDigest != "" && uncompressedDigest != blobinfo.Digest { | ||||||
| 			layers, err := s.imageRef.transport.store.LayersByUncompressedDigest(uncompressedDigest) | 			layers, err := s.imageRef.transport.store.LayersByUncompressedDigest(uncompressedDigest) | ||||||
| 			if err != nil && errors.Cause(err) != storage.ErrLayerUnknown { | 			if err != nil && errors.Cause(err) != storage.ErrLayerUnknown { | ||||||
| 				return false, types.BlobInfo{}, errors.Wrapf(err, `Error looking for layers with digest %q`, uncompressedDigest) | 				return false, types.BlobInfo{}, errors.Wrapf(err, `Error looking for layers with digest %q`, uncompressedDigest) | ||||||
| 			} | 			} | ||||||
| 			if len(layers) > 0 { | 			if len(layers) > 0 { | ||||||
|  | 				if blobinfo.Size != -1 { | ||||||
|  | 					s.blobDiffIDs[blobinfo.Digest] = layers[0].UncompressedDigest | ||||||
|  | 					return true, blobinfo, nil | ||||||
|  | 				} | ||||||
|  | 				if !canSubstitute { | ||||||
|  | 					return false, types.BlobInfo{}, fmt.Errorf("Internal error: canSubstitute was expected to be true for blobInfo %v", blobinfo) | ||||||
|  | 				} | ||||||
| 				s.blobDiffIDs[uncompressedDigest] = layers[0].UncompressedDigest | 				s.blobDiffIDs[uncompressedDigest] = layers[0].UncompressedDigest | ||||||
| 				return true, types.BlobInfo{ | 				return true, types.BlobInfo{ | ||||||
| 					Digest:    uncompressedDigest, | 					Digest:    uncompressedDigest, | ||||||
|  | @ -627,7 +634,7 @@ func (s *storageImageDestination) Commit(ctx context.Context) error { | ||||||
| 		if !ok { | 		if !ok { | ||||||
| 			// Try to find the layer with contents matching that blobsum.
 | 			// Try to find the layer with contents matching that blobsum.
 | ||||||
| 			layer := "" | 			layer := "" | ||||||
| 			layers, err2 := s.imageRef.transport.store.LayersByUncompressedDigest(blob.Digest) | 			layers, err2 := s.imageRef.transport.store.LayersByUncompressedDigest(diffID) | ||||||
| 			if err2 == nil && len(layers) > 0 { | 			if err2 == nil && len(layers) > 0 { | ||||||
| 				layer = layers[0].ID | 				layer = layers[0].ID | ||||||
| 			} else { | 			} else { | ||||||
|  |  | ||||||
|  | @ -1,4 +1,4 @@ | ||||||
| // +build !containers_image_ostree_stub,linux
 | // +build containers_image_ostree,linux
 | ||||||
| 
 | 
 | ||||||
| package alltransports | package alltransports | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,4 +1,4 @@ | ||||||
| // +build containers_image_ostree_stub !linux
 | // +build !containers_image_ostree !linux
 | ||||||
| 
 | 
 | ||||||
| package alltransports | package alltransports | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -398,6 +398,7 @@ type ImageInspectInfo struct { | ||||||
| 	Architecture  string | 	Architecture  string | ||||||
| 	Os            string | 	Os            string | ||||||
| 	Layers        []string | 	Layers        []string | ||||||
|  | 	Env           []string | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // DockerAuthConfig contains authorization information for connecting to a registry.
 | // DockerAuthConfig contains authorization information for connecting to a registry.
 | ||||||
|  |  | ||||||
|  | @ -8,7 +8,7 @@ const ( | ||||||
| 	// VersionMinor is for functionality in a backwards-compatible manner
 | 	// VersionMinor is for functionality in a backwards-compatible manner
 | ||||||
| 	VersionMinor = 0 | 	VersionMinor = 0 | ||||||
| 	// VersionPatch is for backwards-compatible bug fixes
 | 	// VersionPatch is for backwards-compatible bug fixes
 | ||||||
| 	VersionPatch = 0 | 	VersionPatch = 1 | ||||||
| 
 | 
 | ||||||
| 	// VersionDev indicates development branch. Releases will be empty string.
 | 	// VersionDev indicates development branch. Releases will be empty string.
 | ||||||
| 	VersionDev = "" | 	VersionDev = "" | ||||||
|  |  | ||||||
|  | @ -1,8 +1,8 @@ | ||||||
| language: go | language: go | ||||||
| sudo: false | sudo: false | ||||||
| go: | go: | ||||||
|   - 1.8.x |   - 1.10.x | ||||||
|   - 1.9.x |   - tip | ||||||
| 
 | 
 | ||||||
| before_install: | before_install: | ||||||
|   - go get -t -v ./... |   - go get -t -v ./... | ||||||
|  |  | ||||||
|  | @ -31,8 +31,6 @@ _Note:_ it is preferable to go get from github.com, rather than gopkg.in. See is | ||||||
|     p := mpb.New( |     p := mpb.New( | ||||||
|         // override default (80) width |         // override default (80) width | ||||||
|         mpb.WithWidth(64), |         mpb.WithWidth(64), | ||||||
|         // override default "[=>-]" format |  | ||||||
|         mpb.WithFormat("╢▌▌░╟"), |  | ||||||
|         // override default 120ms refresh rate |         // override default 120ms refresh rate | ||||||
|         mpb.WithRefreshRate(180*time.Millisecond), |         mpb.WithRefreshRate(180*time.Millisecond), | ||||||
|     ) |     ) | ||||||
|  | @ -41,6 +39,8 @@ _Note:_ it is preferable to go get from github.com, rather than gopkg.in. See is | ||||||
|     name := "Single Bar:" |     name := "Single Bar:" | ||||||
|     // adding a single bar |     // adding a single bar | ||||||
|     bar := p.AddBar(int64(total), |     bar := p.AddBar(int64(total), | ||||||
|  |         // override default "[=>-]" style | ||||||
|  |         mpb.BarStyle("╢▌▌░╟"), | ||||||
|         mpb.PrependDecorators( |         mpb.PrependDecorators( | ||||||
|             // display our name with one space on the right |             // display our name with one space on the right | ||||||
|             decor.Name(name, decor.WC{W: len(name) + 1, C: decor.DidentRight}), |             decor.Name(name, decor.WC{W: len(name) + 1, C: decor.DidentRight}), | ||||||
|  |  | ||||||
|  | @ -2,6 +2,7 @@ package mpb | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"bytes" | 	"bytes" | ||||||
|  | 	"context" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"io" | 	"io" | ||||||
| 	"io/ioutil" | 	"io/ioutil" | ||||||
|  | @ -11,21 +12,8 @@ import ( | ||||||
| 	"unicode/utf8" | 	"unicode/utf8" | ||||||
| 
 | 
 | ||||||
| 	"github.com/vbauerster/mpb/decor" | 	"github.com/vbauerster/mpb/decor" | ||||||
| 	"github.com/vbauerster/mpb/internal" |  | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| const ( |  | ||||||
| 	rLeft = iota |  | ||||||
| 	rFill |  | ||||||
| 	rTip |  | ||||||
| 	rEmpty |  | ||||||
| 	rRight |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| const formatLen = 5 |  | ||||||
| 
 |  | ||||||
| type barRunes [formatLen]rune |  | ||||||
| 
 |  | ||||||
| // Bar represents a progress Bar
 | // Bar represents a progress Bar
 | ||||||
| type Bar struct { | type Bar struct { | ||||||
| 	priority int | 	priority int | ||||||
|  | @ -45,15 +33,30 @@ type Bar struct { | ||||||
| 	shutdown chan struct{} | 	shutdown chan struct{} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // Filler interface.
 | ||||||
|  | // Bar renders by calling Filler's Fill method. You can literally have
 | ||||||
|  | // any bar kind, by implementing this interface and passing it to the
 | ||||||
|  | // Add method.
 | ||||||
|  | type Filler interface { | ||||||
|  | 	Fill(w io.Writer, width int, s *decor.Statistics) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // FillerFunc is function type adapter to convert function into Filler.
 | ||||||
|  | type FillerFunc func(w io.Writer, width int, stat *decor.Statistics) | ||||||
|  | 
 | ||||||
|  | func (f FillerFunc) Fill(w io.Writer, width int, stat *decor.Statistics) { | ||||||
|  | 	f(w, width, stat) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| type ( | type ( | ||||||
| 	bState struct { | 	bState struct { | ||||||
|  | 		filler             Filler | ||||||
| 		id                 int | 		id                 int | ||||||
| 		width              int | 		width              int | ||||||
|  | 		alignment          int | ||||||
| 		total              int64 | 		total              int64 | ||||||
| 		current            int64 | 		current            int64 | ||||||
| 		runes              barRunes | 		trimSpace          bool | ||||||
| 		trimLeftSpace      bool |  | ||||||
| 		trimRightSpace     bool |  | ||||||
| 		toComplete         bool | 		toComplete         bool | ||||||
| 		removeOnComplete   bool | 		removeOnComplete   bool | ||||||
| 		barClearOnComplete bool | 		barClearOnComplete bool | ||||||
|  | @ -73,8 +76,8 @@ type ( | ||||||
| 		runningBar *Bar | 		runningBar *Bar | ||||||
| 	} | 	} | ||||||
| 	refill struct { | 	refill struct { | ||||||
| 		char rune | 		r     rune | ||||||
| 		till int64 | 		limit int64 | ||||||
| 	} | 	} | ||||||
| 	frameReader struct { | 	frameReader struct { | ||||||
| 		io.Reader | 		io.Reader | ||||||
|  | @ -84,14 +87,20 @@ type ( | ||||||
| 	} | 	} | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func newBar(wg *sync.WaitGroup, id int, total int64, cancel <-chan struct{}, options ...BarOption) *Bar { | func newBar( | ||||||
| 	if total <= 0 { | 	ctx context.Context, | ||||||
| 		total = time.Now().Unix() | 	wg *sync.WaitGroup, | ||||||
| 	} | 	filler Filler, | ||||||
|  | 	id, width int, | ||||||
|  | 	total int64, | ||||||
|  | 	options ...BarOption, | ||||||
|  | ) *Bar { | ||||||
| 
 | 
 | ||||||
| 	s := &bState{ | 	s := &bState{ | ||||||
|  | 		filler:   filler, | ||||||
| 		id:       id, | 		id:       id, | ||||||
| 		priority: id, | 		priority: id, | ||||||
|  | 		width:    width, | ||||||
| 		total:    total, | 		total:    total, | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -104,6 +113,9 @@ func newBar(wg *sync.WaitGroup, id int, total int64, cancel <-chan struct{}, opt | ||||||
| 	s.bufP = bytes.NewBuffer(make([]byte, 0, s.width)) | 	s.bufP = bytes.NewBuffer(make([]byte, 0, s.width)) | ||||||
| 	s.bufB = bytes.NewBuffer(make([]byte, 0, s.width)) | 	s.bufB = bytes.NewBuffer(make([]byte, 0, s.width)) | ||||||
| 	s.bufA = bytes.NewBuffer(make([]byte, 0, s.width)) | 	s.bufA = bytes.NewBuffer(make([]byte, 0, s.width)) | ||||||
|  | 	if s.newLineExtendFn != nil { | ||||||
|  | 		s.bufNL = bytes.NewBuffer(make([]byte, 0, s.width)) | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	b := &Bar{ | 	b := &Bar{ | ||||||
| 		priority:      s.priority, | 		priority:      s.priority, | ||||||
|  | @ -121,11 +133,7 @@ func newBar(wg *sync.WaitGroup, id int, total int64, cancel <-chan struct{}, opt | ||||||
| 		b.priority = b.runningBar.priority | 		b.priority = b.runningBar.priority | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if s.newLineExtendFn != nil { | 	go b.serve(ctx, wg, s) | ||||||
| 		s.bufNL = bytes.NewBuffer(make([]byte, 0, s.width)) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	go b.serve(wg, s, cancel) |  | ||||||
| 	return b | 	return b | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -178,52 +186,42 @@ func (b *Bar) Current() int64 { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // SetTotal sets total dynamically.
 | // SetTotal sets total dynamically.
 | ||||||
| // Set final to true, when total is known, it will trigger bar complete event.
 | // Set complete to true, to trigger bar complete event now.
 | ||||||
| func (b *Bar) SetTotal(total int64, final bool) bool { | func (b *Bar) SetTotal(total int64, complete bool) { | ||||||
| 	select { | 	select { | ||||||
| 	case b.operateState <- func(s *bState) { | 	case b.operateState <- func(s *bState) { | ||||||
| 		if total > 0 { | 		s.total = total | ||||||
| 			s.total = total | 		if complete && !s.toComplete { | ||||||
| 		} |  | ||||||
| 		if final { |  | ||||||
| 			s.current = s.total | 			s.current = s.total | ||||||
| 			s.toComplete = true | 			s.toComplete = true | ||||||
| 		} | 		} | ||||||
| 	}: | 	}: | ||||||
| 		return true |  | ||||||
| 	case <-b.done: | 	case <-b.done: | ||||||
| 		return false |  | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // SetRefill sets fill rune to r, up until n.
 | // SetRefill sets refill, if supported by underlying Filler.
 | ||||||
| func (b *Bar) SetRefill(n int, r rune) { | func (b *Bar) SetRefill(amount int64) { | ||||||
| 	if n <= 0 { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	b.operateState <- func(s *bState) { | 	b.operateState <- func(s *bState) { | ||||||
| 		s.refill = &refill{r, int64(n)} | 		if f, ok := s.filler.(interface{ SetRefill(int64) }); ok { | ||||||
|  | 			f.SetRefill(amount) | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // RefillBy is deprecated, use SetRefill
 |  | ||||||
| func (b *Bar) RefillBy(n int, r rune) { |  | ||||||
| 	b.SetRefill(n, r) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Increment is a shorthand for b.IncrBy(1).
 | // Increment is a shorthand for b.IncrBy(1).
 | ||||||
| func (b *Bar) Increment() { | func (b *Bar) Increment() { | ||||||
| 	b.IncrBy(1) | 	b.IncrBy(1) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // IncrBy increments progress bar by amount of n.
 | // IncrBy increments progress bar by amount of n.
 | ||||||
| // wdd is optional work duration i.e. time.Since(start),
 | // wdd is optional work duration i.e. time.Since(start), which expected
 | ||||||
| // which expected to be provided, if any ewma based decorator is used.
 | // to be provided, if any ewma based decorator is used.
 | ||||||
| func (b *Bar) IncrBy(n int, wdd ...time.Duration) { | func (b *Bar) IncrBy(n int, wdd ...time.Duration) { | ||||||
| 	select { | 	select { | ||||||
| 	case b.operateState <- func(s *bState) { | 	case b.operateState <- func(s *bState) { | ||||||
| 		s.current += int64(n) | 		s.current += int64(n) | ||||||
| 		if s.current >= s.total { | 		if s.total > 0 && s.current >= s.total { | ||||||
| 			s.current = s.total | 			s.current = s.total | ||||||
| 			s.toComplete = true | 			s.toComplete = true | ||||||
| 		} | 		} | ||||||
|  | @ -238,9 +236,9 @@ func (b *Bar) IncrBy(n int, wdd ...time.Duration) { | ||||||
| // Completed reports whether the bar is in completed state.
 | // Completed reports whether the bar is in completed state.
 | ||||||
| func (b *Bar) Completed() bool { | func (b *Bar) Completed() bool { | ||||||
| 	// omit select here, because primary usage of the method is for loop
 | 	// omit select here, because primary usage of the method is for loop
 | ||||||
| 	// condition, like 	for !bar.Completed() {...}
 | 	// condition, like for !bar.Completed() {...} so when toComplete=true
 | ||||||
| 	// so when toComplete=true it is called once (at which time, the bar is still alive),
 | 	// it is called once (at which time, the bar is still alive), then
 | ||||||
| 	// then quits the loop and never suppose to be called afterwards.
 | 	// quits the loop and never suppose to be called afterwards.
 | ||||||
| 	return <-b.boolCh | 	return <-b.boolCh | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -253,8 +251,9 @@ func (b *Bar) wSyncTable() [][]chan int { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (b *Bar) serve(wg *sync.WaitGroup, s *bState, cancel <-chan struct{}) { | func (b *Bar) serve(ctx context.Context, wg *sync.WaitGroup, s *bState) { | ||||||
| 	defer wg.Done() | 	defer wg.Done() | ||||||
|  | 	cancel := ctx.Done() | ||||||
| 	for { | 	for { | ||||||
| 		select { | 		select { | ||||||
| 		case op := <-b.operateState: | 		case op := <-b.operateState: | ||||||
|  | @ -322,8 +321,6 @@ func (b *Bar) render(debugOut io.Writer, tw int) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *bState) draw(termWidth int) io.Reader { | func (s *bState) draw(termWidth int) io.Reader { | ||||||
| 	defer s.bufA.WriteByte('\n') |  | ||||||
| 
 |  | ||||||
| 	if s.panicMsg != "" { | 	if s.panicMsg != "" { | ||||||
| 		return strings.NewReader(fmt.Sprintf(fmt.Sprintf("%%.%ds\n", termWidth), s.panicMsg)) | 		return strings.NewReader(fmt.Sprintf(fmt.Sprintf("%%.%ds\n", termWidth), s.panicMsg)) | ||||||
| 	} | 	} | ||||||
|  | @ -338,77 +335,32 @@ func (s *bState) draw(termWidth int) io.Reader { | ||||||
| 		s.bufA.WriteString(d.Decor(stat)) | 		s.bufA.WriteString(d.Decor(stat)) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	prependCount := utf8.RuneCount(s.bufP.Bytes()) |  | ||||||
| 	appendCount := utf8.RuneCount(s.bufA.Bytes()) |  | ||||||
| 
 |  | ||||||
| 	if s.barClearOnComplete && s.completeFlushed { | 	if s.barClearOnComplete && s.completeFlushed { | ||||||
|  | 		s.bufA.WriteByte('\n') | ||||||
| 		return io.MultiReader(s.bufP, s.bufA) | 		return io.MultiReader(s.bufP, s.bufA) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	s.fillBar(s.width) | 	prependCount := utf8.RuneCount(s.bufP.Bytes()) | ||||||
| 	barCount := utf8.RuneCount(s.bufB.Bytes()) | 	appendCount := utf8.RuneCount(s.bufA.Bytes()) | ||||||
| 	totalCount := prependCount + barCount + appendCount |  | ||||||
| 	if spaceCount := 0; totalCount > termWidth { |  | ||||||
| 		if !s.trimLeftSpace { |  | ||||||
| 			spaceCount++ |  | ||||||
| 		} |  | ||||||
| 		if !s.trimRightSpace { |  | ||||||
| 			spaceCount++ |  | ||||||
| 		} |  | ||||||
| 		s.fillBar(termWidth - prependCount - appendCount - spaceCount) |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	return io.MultiReader(s.bufP, s.bufB, s.bufA) | 	if !s.trimSpace { | ||||||
| } | 		// reserve space for edge spaces
 | ||||||
| 
 | 		termWidth -= 2 | ||||||
| func (s *bState) fillBar(width int) { |  | ||||||
| 	defer func() { |  | ||||||
| 		s.bufB.WriteRune(s.runes[rRight]) |  | ||||||
| 		if !s.trimRightSpace { |  | ||||||
| 			s.bufB.WriteByte(' ') |  | ||||||
| 		} |  | ||||||
| 	}() |  | ||||||
| 
 |  | ||||||
| 	s.bufB.Reset() |  | ||||||
| 	if !s.trimLeftSpace { |  | ||||||
| 		s.bufB.WriteByte(' ') | 		s.bufB.WriteByte(' ') | ||||||
| 	} | 	} | ||||||
| 	s.bufB.WriteRune(s.runes[rLeft]) |  | ||||||
| 	if width <= 2 { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	// bar s.width without leftEnd and rightEnd runes
 | 	if prependCount+s.width+appendCount > termWidth { | ||||||
| 	barWidth := width - 2 | 		s.filler.Fill(s.bufB, termWidth-prependCount-appendCount, stat) | ||||||
| 
 |  | ||||||
| 	completedWidth := internal.Percentage(s.total, s.current, int64(barWidth)) |  | ||||||
| 
 |  | ||||||
| 	if s.refill != nil { |  | ||||||
| 		till := internal.Percentage(s.total, s.refill.till, int64(barWidth)) |  | ||||||
| 		// append refill rune
 |  | ||||||
| 		var i int64 |  | ||||||
| 		for i = 0; i < till; i++ { |  | ||||||
| 			s.bufB.WriteRune(s.refill.char) |  | ||||||
| 		} |  | ||||||
| 		for i = till; i < completedWidth; i++ { |  | ||||||
| 			s.bufB.WriteRune(s.runes[rFill]) |  | ||||||
| 		} |  | ||||||
| 	} else { | 	} else { | ||||||
| 		var i int64 | 		s.filler.Fill(s.bufB, s.width, stat) | ||||||
| 		for i = 0; i < completedWidth; i++ { |  | ||||||
| 			s.bufB.WriteRune(s.runes[rFill]) |  | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if completedWidth < int64(barWidth) && completedWidth > 0 { | 	if !s.trimSpace { | ||||||
| 		_, size := utf8.DecodeLastRune(s.bufB.Bytes()) | 		s.bufB.WriteByte(' ') | ||||||
| 		s.bufB.Truncate(s.bufB.Len() - size) |  | ||||||
| 		s.bufB.WriteRune(s.runes[rTip]) |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	for i := completedWidth; i < int64(barWidth); i++ { | 	s.bufA.WriteByte('\n') | ||||||
| 		s.bufB.WriteRune(s.runes[rEmpty]) | 	return io.MultiReader(s.bufP, s.bufB, s.bufA) | ||||||
| 	} |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *bState) wSyncTable() [][]chan int { | func (s *bState) wSyncTable() [][]chan int { | ||||||
|  | @ -442,14 +394,6 @@ func newStatistics(s *bState) *decor.Statistics { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func strToBarRunes(format string) (array barRunes) { |  | ||||||
| 	for i, n := 0, 0; len(format) > 0; i++ { |  | ||||||
| 		array[i], n = utf8.DecodeRuneInString(format) |  | ||||||
| 		format = format[n:] |  | ||||||
| 	} |  | ||||||
| 	return |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func countLines(b []byte) int { | func countLines(b []byte) int { | ||||||
| 	return bytes.Count(b, []byte("\n")) | 	return bytes.Count(b, []byte("\n")) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -0,0 +1,111 @@ | ||||||
|  | package mpb | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"io" | ||||||
|  | 	"unicode/utf8" | ||||||
|  | 
 | ||||||
|  | 	"github.com/vbauerster/mpb/decor" | ||||||
|  | 	"github.com/vbauerster/mpb/internal" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | const ( | ||||||
|  | 	rLeft = iota | ||||||
|  | 	rFill | ||||||
|  | 	rTip | ||||||
|  | 	rEmpty | ||||||
|  | 	rRight | ||||||
|  | 	rRevTip | ||||||
|  | 	rRefill | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | var defaultBarStyle = "[=>-]<+" | ||||||
|  | 
 | ||||||
|  | type barFiller struct { | ||||||
|  | 	format       [][]byte | ||||||
|  | 	refillAmount int64 | ||||||
|  | 	reverse      bool | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func newDefaultBarFiller() Filler { | ||||||
|  | 	bf := &barFiller{ | ||||||
|  | 		format: make([][]byte, utf8.RuneCountInString(defaultBarStyle)), | ||||||
|  | 	} | ||||||
|  | 	bf.setStyle(defaultBarStyle) | ||||||
|  | 	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) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (s *barFiller) setReverse() { | ||||||
|  | 	s.reverse = true | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (s *barFiller) SetRefill(amount int64) { | ||||||
|  | 	s.refillAmount = amount | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (s *barFiller) Fill(w io.Writer, width int, stat *decor.Statistics) { | ||||||
|  | 
 | ||||||
|  | 	// don't count rLeft and rRight [brackets]
 | ||||||
|  | 	width -= 2 | ||||||
|  | 	if width < 2 { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	w.Write(s.format[rLeft]) | ||||||
|  | 	if width == 2 { | ||||||
|  | 		w.Write(s.format[rRight]) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	bb := make([][]byte, width) | ||||||
|  | 
 | ||||||
|  | 	cwidth := int(internal.Percentage(stat.Total, stat.Current, int64(width))) | ||||||
|  | 
 | ||||||
|  | 	for i := 0; i < cwidth; i++ { | ||||||
|  | 		bb[i] = s.format[rFill] | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if s.refillAmount > 0 { | ||||||
|  | 		var rwidth int | ||||||
|  | 		if s.refillAmount > stat.Current { | ||||||
|  | 			rwidth = cwidth | ||||||
|  | 		} else { | ||||||
|  | 			rwidth = int(internal.Percentage(stat.Total, int64(s.refillAmount), int64(width))) | ||||||
|  | 		} | ||||||
|  | 		for i := 0; i < rwidth; i++ { | ||||||
|  | 			bb[i] = s.format[rRefill] | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if cwidth > 0 && cwidth < width { | ||||||
|  | 		bb[cwidth-1] = s.format[rTip] | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for i := cwidth; i < width; i++ { | ||||||
|  | 		bb[i] = s.format[rEmpty] | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if s.reverse { | ||||||
|  | 		if cwidth > 0 && cwidth < width { | ||||||
|  | 			bb[cwidth-1] = s.format[rRevTip] | ||||||
|  | 		} | ||||||
|  | 		for i := len(bb) - 1; i >= 0; i-- { | ||||||
|  | 			w.Write(bb[i]) | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		for i := 0; i < len(bb); i++ { | ||||||
|  | 			w.Write(bb[i]) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	w.Write(s.format[rRight]) | ||||||
|  | } | ||||||
|  | @ -6,11 +6,10 @@ import ( | ||||||
| 	"github.com/vbauerster/mpb/decor" | 	"github.com/vbauerster/mpb/decor" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // BarOption is a function option which changes the default behavior of a bar,
 | // BarOption is a function option which changes the default behavior of a bar.
 | ||||||
| // if passed to p.AddBar(int64, ...BarOption)
 |  | ||||||
| type BarOption func(*bState) | type BarOption func(*bState) | ||||||
| 
 | 
 | ||||||
| // AppendDecorators let you inject decorators to the bar's right side
 | // AppendDecorators let you inject decorators to the bar's right side.
 | ||||||
| func AppendDecorators(appenders ...decor.Decorator) BarOption { | func AppendDecorators(appenders ...decor.Decorator) BarOption { | ||||||
| 	return func(s *bState) { | 	return func(s *bState) { | ||||||
| 		for _, decorator := range appenders { | 		for _, decorator := range appenders { | ||||||
|  | @ -25,7 +24,7 @@ func AppendDecorators(appenders ...decor.Decorator) BarOption { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // PrependDecorators let you inject decorators to the bar's left side
 | // PrependDecorators let you inject decorators to the bar's left side.
 | ||||||
| func PrependDecorators(prependers ...decor.Decorator) BarOption { | func PrependDecorators(prependers ...decor.Decorator) BarOption { | ||||||
| 	return func(s *bState) { | 	return func(s *bState) { | ||||||
| 		for _, decorator := range prependers { | 		for _, decorator := range prependers { | ||||||
|  | @ -40,85 +39,155 @@ func PrependDecorators(prependers ...decor.Decorator) BarOption { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // BarTrimLeft trims left side space of the bar
 | // BarID sets bar id.
 | ||||||
| func BarTrimLeft() BarOption { |  | ||||||
| 	return func(s *bState) { |  | ||||||
| 		s.trimLeftSpace = true |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // BarTrimRight trims right space of the bar
 |  | ||||||
| func BarTrimRight() BarOption { |  | ||||||
| 	return func(s *bState) { |  | ||||||
| 		s.trimRightSpace = true |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // BarTrim trims both left and right spaces of the bar
 |  | ||||||
| func BarTrim() BarOption { |  | ||||||
| 	return func(s *bState) { |  | ||||||
| 		s.trimLeftSpace = true |  | ||||||
| 		s.trimRightSpace = true |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // BarID overwrites internal bar id
 |  | ||||||
| func BarID(id int) BarOption { | func BarID(id int) BarOption { | ||||||
| 	return func(s *bState) { | 	return func(s *bState) { | ||||||
| 		s.id = id | 		s.id = id | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // BarRemoveOnComplete is a flag, if set whole bar line will be removed on complete event.
 | // BarWidth sets bar width independent of the container.
 | ||||||
| // If both BarRemoveOnComplete and BarClearOnComplete are set, first bar section gets cleared
 | func BarWidth(width int) BarOption { | ||||||
| // and then whole bar line gets removed completely.
 | 	return func(s *bState) { | ||||||
|  | 		s.width = width | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // BarRemoveOnComplete is a flag, if set whole bar line will be removed
 | ||||||
|  | // on complete event. If both BarRemoveOnComplete and BarClearOnComplete
 | ||||||
|  | // are set, first bar section gets cleared and then whole bar line
 | ||||||
|  | // gets removed completely.
 | ||||||
| func BarRemoveOnComplete() BarOption { | func BarRemoveOnComplete() BarOption { | ||||||
| 	return func(s *bState) { | 	return func(s *bState) { | ||||||
| 		s.removeOnComplete = true | 		s.removeOnComplete = true | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // BarReplaceOnComplete is indicator for delayed bar start, after the `runningBar` is complete.
 | // BarReplaceOnComplete is indicator for delayed bar start, after the
 | ||||||
| // To achieve bar replacement effect, `runningBar` should has its `BarRemoveOnComplete` option set.
 | // `runningBar` is complete. To achieve bar replacement effect,
 | ||||||
|  | // `runningBar` should has its `BarRemoveOnComplete` option set.
 | ||||||
| func BarReplaceOnComplete(runningBar *Bar) BarOption { | func BarReplaceOnComplete(runningBar *Bar) BarOption { | ||||||
|  | 	return BarParkTo(runningBar) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // BarParkTo same as BarReplaceOnComplete
 | ||||||
|  | func BarParkTo(runningBar *Bar) BarOption { | ||||||
| 	return func(s *bState) { | 	return func(s *bState) { | ||||||
| 		s.runningBar = runningBar | 		s.runningBar = runningBar | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // BarClearOnComplete is a flag, if set will clear bar section on complete event.
 | // BarClearOnComplete is a flag, if set will clear bar section on
 | ||||||
| // If you need to remove a whole bar line, refer to BarRemoveOnComplete.
 | // complete event. If you need to remove a whole bar line, refer to
 | ||||||
|  | // BarRemoveOnComplete.
 | ||||||
| func BarClearOnComplete() BarOption { | func BarClearOnComplete() BarOption { | ||||||
| 	return func(s *bState) { | 	return func(s *bState) { | ||||||
| 		s.barClearOnComplete = true | 		s.barClearOnComplete = true | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // BarPriority sets bar's priority.
 | // BarPriority sets bar's priority. Zero is highest priority, i.e. bar
 | ||||||
| // Zero is highest priority, i.e. bar will be on top.
 | // will be on top. If `BarReplaceOnComplete` option is supplied, this
 | ||||||
| // If `BarReplaceOnComplete` option is supplied, this option is ignored.
 | // option is ignored.
 | ||||||
| func BarPriority(priority int) BarOption { | func BarPriority(priority int) BarOption { | ||||||
| 	return func(s *bState) { | 	return func(s *bState) { | ||||||
| 		s.priority = priority | 		s.priority = priority | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // BarNewLineExtend takes user defined efn, which gets called each render cycle.
 | // BarNewLineExtend takes user defined efn, which gets called each
 | ||||||
| // Any write to provided writer of efn, will appear on new line of respective bar.
 | // render cycle. Any write to provided writer of efn, will appear on
 | ||||||
|  | // new line of respective bar.
 | ||||||
| func BarNewLineExtend(efn func(io.Writer, *decor.Statistics)) BarOption { | func BarNewLineExtend(efn func(io.Writer, *decor.Statistics)) BarOption { | ||||||
| 	return func(s *bState) { | 	return func(s *bState) { | ||||||
| 		s.newLineExtendFn = efn | 		s.newLineExtendFn = efn | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func barWidth(w int) BarOption { | // TrimSpace trims bar's edge spaces.
 | ||||||
|  | func TrimSpace() BarOption { | ||||||
| 	return func(s *bState) { | 	return func(s *bState) { | ||||||
| 		s.width = w | 		s.trimSpace = true | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func barFormat(format string) BarOption { | // BarStyle sets custom bar style, default one is "[=>-]<+".
 | ||||||
|  | //
 | ||||||
|  | //	'[' left bracket rune
 | ||||||
|  | //
 | ||||||
|  | //	'=' fill rune
 | ||||||
|  | //
 | ||||||
|  | //	'>' tip rune
 | ||||||
|  | //
 | ||||||
|  | //	'-' empty rune
 | ||||||
|  | //
 | ||||||
|  | //	']' right bracket rune
 | ||||||
|  | //
 | ||||||
|  | //	'<' reverse tip rune, used when BarReverse option is set
 | ||||||
|  | //
 | ||||||
|  | //	'+' refill rune, used when *Bar.SetRefill(int64) is called
 | ||||||
|  | //
 | ||||||
|  | // It's ok to provide first five runes only, for example mpb.BarStyle("╢▌▌░╟")
 | ||||||
|  | func BarStyle(style string) BarOption { | ||||||
|  | 	chk := func(filler Filler) (interface{}, bool) { | ||||||
|  | 		if style == "" { | ||||||
|  | 			return nil, false | ||||||
|  | 		} | ||||||
|  | 		t, ok := filler.(*barFiller) | ||||||
|  | 		return t, ok | ||||||
|  | 	} | ||||||
|  | 	cb := func(t interface{}) { | ||||||
|  | 		t.(*barFiller).setStyle(style) | ||||||
|  | 	} | ||||||
|  | 	return MakeFillerTypeSpecificBarOption(chk, cb) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // BarReverse reverse mode, bar will progress from right to left.
 | ||||||
|  | func BarReverse() BarOption { | ||||||
|  | 	chk := func(filler Filler) (interface{}, bool) { | ||||||
|  | 		t, ok := filler.(*barFiller) | ||||||
|  | 		return t, ok | ||||||
|  | 	} | ||||||
|  | 	cb := func(t interface{}) { | ||||||
|  | 		t.(*barFiller).setReverse() | ||||||
|  | 	} | ||||||
|  | 	return MakeFillerTypeSpecificBarOption(chk, cb) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // SpinnerStyle sets custom spinner style.
 | ||||||
|  | // Effective when Filler type is spinner.
 | ||||||
|  | func SpinnerStyle(frames []string) BarOption { | ||||||
|  | 	chk := func(filler Filler) (interface{}, bool) { | ||||||
|  | 		if len(frames) == 0 { | ||||||
|  | 			return nil, false | ||||||
|  | 		} | ||||||
|  | 		t, ok := filler.(*spinnerFiller) | ||||||
|  | 		return t, ok | ||||||
|  | 	} | ||||||
|  | 	cb := func(t interface{}) { | ||||||
|  | 		t.(*spinnerFiller).frames = frames | ||||||
|  | 	} | ||||||
|  | 	return MakeFillerTypeSpecificBarOption(chk, cb) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // MakeFillerTypeSpecificBarOption makes BarOption specific to Filler's
 | ||||||
|  | // actual type. If you implement your own Filler, so most probably
 | ||||||
|  | // you'll need this. See BarStyle or SpinnerStyle for example.
 | ||||||
|  | func MakeFillerTypeSpecificBarOption( | ||||||
|  | 	typeChecker func(Filler) (interface{}, bool), | ||||||
|  | 	cb func(interface{}), | ||||||
|  | ) BarOption { | ||||||
| 	return func(s *bState) { | 	return func(s *bState) { | ||||||
| 		s.runes = strToBarRunes(format) | 		if t, ok := typeChecker(s.filler); ok { | ||||||
|  | 			cb(t) | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | // OptionOnCondition returns option when condition evaluates to true.
 | ||||||
|  | func OptionOnCondition(option BarOption, condition func() bool) BarOption { | ||||||
|  | 	if condition() { | ||||||
|  | 		return option | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -22,8 +22,8 @@ var ( | ||||||
| 	clearCursorAndLine = cursorUp + clearLine | 	clearCursorAndLine = cursorUp + clearLine | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // Writer is a buffered the writer that updates the terminal.
 | // Writer is a buffered the writer that updates the terminal.  The
 | ||||||
| // The contents of writer will be flushed when Flush is called.
 | // contents of writer will be flushed when Flush is called.
 | ||||||
| type Writer struct { | type Writer struct { | ||||||
| 	out        io.Writer | 	out        io.Writer | ||||||
| 	buf        bytes.Buffer | 	buf        bytes.Buffer | ||||||
|  | @ -64,11 +64,13 @@ func (w *Writer) WriteString(s string) (n int, err error) { | ||||||
| 	return w.buf.WriteString(s) | 	return w.buf.WriteString(s) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // ReadFrom reads from the provided io.Reader and writes to the underlying buffer.
 | // ReadFrom reads from the provided io.Reader and writes to the
 | ||||||
|  | // underlying buffer.
 | ||||||
| func (w *Writer) ReadFrom(r io.Reader) (n int64, err error) { | func (w *Writer) ReadFrom(r io.Reader) (n int64, err error) { | ||||||
| 	return w.buf.ReadFrom(r) | 	return w.buf.ReadFrom(r) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // GetWidth returns width of underlying terminal.
 | ||||||
| func (w *Writer) GetWidth() (int, error) { | func (w *Writer) GetWidth() (int, error) { | ||||||
| 	if w.isTerminal { | 	if w.isTerminal { | ||||||
| 		tw, _, err := terminal.GetSize(w.fd) | 		tw, _, err := terminal.GetSize(w.fd) | ||||||
|  |  | ||||||
|  | @ -8,7 +8,7 @@ import ( | ||||||
| 	"syscall" | 	"syscall" | ||||||
| 	"unsafe" | 	"unsafe" | ||||||
| 
 | 
 | ||||||
| 	"github.com/mattn/go-isatty" | 	isatty "github.com/mattn/go-isatty" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| var kernel32 = syscall.NewLazyDLL("kernel32.dll") | var kernel32 = syscall.NewLazyDLL("kernel32.dll") | ||||||
|  |  | ||||||
|  | @ -141,12 +141,14 @@ func CountersNoUnit(pairFormat string, wcc ...WC) Decorator { | ||||||
| 	return Counters(0, pairFormat, wcc...) | 	return Counters(0, pairFormat, wcc...) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // CountersKibiByte is a wrapper around Counters with predefined unit UnitKiB (bytes/1024).
 | // CountersKibiByte is a wrapper around Counters with predefined unit
 | ||||||
|  | // UnitKiB (bytes/1024).
 | ||||||
| func CountersKibiByte(pairFormat string, wcc ...WC) Decorator { | func CountersKibiByte(pairFormat string, wcc ...WC) Decorator { | ||||||
| 	return Counters(UnitKiB, pairFormat, wcc...) | 	return Counters(UnitKiB, pairFormat, wcc...) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // CountersKiloByte is a wrapper around Counters with predefined unit UnitKB (bytes/1000).
 | // CountersKiloByte is a wrapper around Counters with predefined unit
 | ||||||
|  | // UnitKB (bytes/1000).
 | ||||||
| func CountersKiloByte(pairFormat string, wcc ...WC) Decorator { | func CountersKiloByte(pairFormat string, wcc ...WC) Decorator { | ||||||
| 	return Counters(UnitKB, pairFormat, wcc...) | 	return Counters(UnitKB, pairFormat, wcc...) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -31,8 +31,12 @@ const ( | ||||||
| 	DSyncSpaceR = DSyncWidth | DextraSpace | DidentRight | 	DSyncSpaceR = DSyncWidth | DextraSpace | DidentRight | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | // TimeStyle enum.
 | ||||||
|  | type TimeStyle int | ||||||
|  | 
 | ||||||
|  | // TimeStyle kinds.
 | ||||||
| const ( | const ( | ||||||
| 	ET_STYLE_GO = iota | 	ET_STYLE_GO TimeStyle = iota | ||||||
| 	ET_STYLE_HHMMSS | 	ET_STYLE_HHMMSS | ||||||
| 	ET_STYLE_HHMM | 	ET_STYLE_HHMM | ||||||
| 	ET_STYLE_MMSS | 	ET_STYLE_MMSS | ||||||
|  | @ -47,35 +51,37 @@ type Statistics struct { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Decorator interface.
 | // Decorator interface.
 | ||||||
| // A decorator must implement this interface, in order to be used with mpb library.
 | // A decorator must implement this interface, in order to be used with
 | ||||||
|  | // mpb library.
 | ||||||
| type Decorator interface { | type Decorator interface { | ||||||
| 	Decor(*Statistics) string | 	Decor(*Statistics) string | ||||||
| 	Syncable | 	Syncable | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Syncable interface.
 | // Syncable interface.
 | ||||||
| // All decorators implement this interface implicitly.
 | // All decorators implement this interface implicitly. Its Syncable
 | ||||||
| // Its Syncable method exposes width sync channel, if sync is enabled.
 | // method exposes width sync channel, if sync is enabled.
 | ||||||
| type Syncable interface { | type Syncable interface { | ||||||
| 	Syncable() (bool, chan int) | 	Syncable() (bool, chan int) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // OnCompleteMessenger interface.
 | // OnCompleteMessenger interface.
 | ||||||
| // Decorators implementing this interface suppose to return provided string on complete event.
 | // Decorators implementing this interface suppose to return provided
 | ||||||
|  | // string on complete event.
 | ||||||
| type OnCompleteMessenger interface { | type OnCompleteMessenger interface { | ||||||
| 	OnCompleteMessage(string) | 	OnCompleteMessage(string) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // AmountReceiver interface.
 | // AmountReceiver interface.
 | ||||||
| // If decorator needs to receive increment amount,
 | // If decorator needs to receive increment amount, so this is the right
 | ||||||
| // so this is the right interface to implement.
 | // interface to implement.
 | ||||||
| type AmountReceiver interface { | type AmountReceiver interface { | ||||||
| 	NextAmount(int, ...time.Duration) | 	NextAmount(int, ...time.Duration) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // ShutdownListener interface.
 | // ShutdownListener interface.
 | ||||||
| // If decorator needs to be notified once upon bar shutdown event,
 | // If decorator needs to be notified once upon bar shutdown event, so
 | ||||||
| // so this is the right interface to implement.
 | // this is the right interface to implement.
 | ||||||
| type ShutdownListener interface { | type ShutdownListener interface { | ||||||
| 	Shutdown() | 	Shutdown() | ||||||
| } | } | ||||||
|  | @ -90,6 +96,7 @@ var ( | ||||||
| 
 | 
 | ||||||
| // WC is a struct with two public fields W and C, both of int type.
 | // WC is a struct with two public fields W and C, both of int type.
 | ||||||
| // W represents width and C represents bit set of width related config.
 | // W represents width and C represents bit set of width related config.
 | ||||||
|  | // A decorator should embed WC, in order to become Syncable.
 | ||||||
| type WC struct { | type WC struct { | ||||||
| 	W      int | 	W      int | ||||||
| 	C      int | 	C      int | ||||||
|  | @ -126,12 +133,13 @@ func (wc *WC) Init() { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // Syncable is implementation of Syncable interface.
 | ||||||
| func (wc *WC) Syncable() (bool, chan int) { | func (wc *WC) Syncable() (bool, chan int) { | ||||||
| 	return (wc.C & DSyncWidth) != 0, wc.wsync | 	return (wc.C & DSyncWidth) != 0, wc.wsync | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // OnComplete returns decorator, which wraps provided decorator, with sole
 | // OnComplete returns decorator, which wraps provided decorator, with
 | ||||||
| // purpose to display provided message on complete event.
 | // sole purpose to display provided message on complete event.
 | ||||||
| //
 | //
 | ||||||
| //	`decorator` Decorator to wrap
 | //	`decorator` Decorator to wrap
 | ||||||
| //
 | //
 | ||||||
|  |  | ||||||
|  | @ -10,7 +10,7 @@ import ( | ||||||
| //	`style` one of [ET_STYLE_GO|ET_STYLE_HHMMSS|ET_STYLE_HHMM|ET_STYLE_MMSS]
 | //	`style` one of [ET_STYLE_GO|ET_STYLE_HHMMSS|ET_STYLE_HHMM|ET_STYLE_MMSS]
 | ||||||
| //
 | //
 | ||||||
| //	`wcc` optional WC config
 | //	`wcc` optional WC config
 | ||||||
| func Elapsed(style int, wcc ...WC) Decorator { | func Elapsed(style TimeStyle, wcc ...WC) Decorator { | ||||||
| 	var wc WC | 	var wc WC | ||||||
| 	for _, widthConf := range wcc { | 	for _, widthConf := range wcc { | ||||||
| 		wc = widthConf | 		wc = widthConf | ||||||
|  | @ -26,7 +26,7 @@ func Elapsed(style int, wcc ...WC) Decorator { | ||||||
| 
 | 
 | ||||||
| type elapsedDecorator struct { | type elapsedDecorator struct { | ||||||
| 	WC | 	WC | ||||||
| 	style       int | 	style       TimeStyle | ||||||
| 	startTime   time.Time | 	startTime   time.Time | ||||||
| 	msg         string | 	msg         string | ||||||
| 	completeMsg *string | 	completeMsg *string | ||||||
|  |  | ||||||
|  | @ -6,7 +6,6 @@ import ( | ||||||
| 	"time" | 	"time" | ||||||
| 
 | 
 | ||||||
| 	"github.com/VividCortex/ewma" | 	"github.com/VividCortex/ewma" | ||||||
| 	"github.com/vbauerster/mpb/internal" |  | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| type TimeNormalizer func(time.Duration) time.Duration | type TimeNormalizer func(time.Duration) time.Duration | ||||||
|  | @ -18,7 +17,7 @@ type TimeNormalizer func(time.Duration) time.Duration | ||||||
| //	`age` is the previous N samples to average over.
 | //	`age` is the previous N samples to average over.
 | ||||||
| //
 | //
 | ||||||
| //	`wcc` optional WC config
 | //	`wcc` optional WC config
 | ||||||
| func EwmaETA(style int, age float64, wcc ...WC) Decorator { | func EwmaETA(style TimeStyle, age float64, wcc ...WC) Decorator { | ||||||
| 	return MovingAverageETA(style, ewma.NewMovingAverage(age), NopNormalizer(), wcc...) | 	return MovingAverageETA(style, ewma.NewMovingAverage(age), NopNormalizer(), wcc...) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -31,7 +30,7 @@ func EwmaETA(style int, age float64, wcc ...WC) Decorator { | ||||||
| //	`normalizer` available implementations are [NopNormalizer|FixedIntervalTimeNormalizer|MaxTolerateTimeNormalizer]
 | //	`normalizer` available implementations are [NopNormalizer|FixedIntervalTimeNormalizer|MaxTolerateTimeNormalizer]
 | ||||||
| //
 | //
 | ||||||
| //	`wcc` optional WC config
 | //	`wcc` optional WC config
 | ||||||
| func MovingAverageETA(style int, average MovingAverage, normalizer TimeNormalizer, wcc ...WC) Decorator { | func MovingAverageETA(style TimeStyle, average MovingAverage, normalizer TimeNormalizer, wcc ...WC) Decorator { | ||||||
| 	var wc WC | 	var wc WC | ||||||
| 	for _, widthConf := range wcc { | 	for _, widthConf := range wcc { | ||||||
| 		wc = widthConf | 		wc = widthConf | ||||||
|  | @ -48,7 +47,7 @@ func MovingAverageETA(style int, average MovingAverage, normalizer TimeNormalize | ||||||
| 
 | 
 | ||||||
| type movingAverageETA struct { | type movingAverageETA struct { | ||||||
| 	WC | 	WC | ||||||
| 	style       int | 	style       TimeStyle | ||||||
| 	average     ewma.MovingAverage | 	average     ewma.MovingAverage | ||||||
| 	completeMsg *string | 	completeMsg *string | ||||||
| 	normalizer  TimeNormalizer | 	normalizer  TimeNormalizer | ||||||
|  | @ -59,7 +58,7 @@ func (d *movingAverageETA) Decor(st *Statistics) string { | ||||||
| 		return d.FormatMsg(*d.completeMsg) | 		return d.FormatMsg(*d.completeMsg) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	v := internal.Round(d.average.Value()) | 	v := math.Round(d.average.Value()) | ||||||
| 	remaining := d.normalizer(time.Duration((st.Total - st.Current) * int64(v))) | 	remaining := d.normalizer(time.Duration((st.Total - st.Current) * int64(v))) | ||||||
| 	hours := int64((remaining / time.Hour) % 60) | 	hours := int64((remaining / time.Hour) % 60) | ||||||
| 	minutes := int64((remaining / time.Minute) % 60) | 	minutes := int64((remaining / time.Minute) % 60) | ||||||
|  | @ -105,7 +104,7 @@ func (d *movingAverageETA) OnCompleteMessage(msg string) { | ||||||
| //	`style` one of [ET_STYLE_GO|ET_STYLE_HHMMSS|ET_STYLE_HHMM|ET_STYLE_MMSS]
 | //	`style` one of [ET_STYLE_GO|ET_STYLE_HHMMSS|ET_STYLE_HHMM|ET_STYLE_MMSS]
 | ||||||
| //
 | //
 | ||||||
| //	`wcc` optional WC config
 | //	`wcc` optional WC config
 | ||||||
| func AverageETA(style int, wcc ...WC) Decorator { | func AverageETA(style TimeStyle, wcc ...WC) Decorator { | ||||||
| 	var wc WC | 	var wc WC | ||||||
| 	for _, widthConf := range wcc { | 	for _, widthConf := range wcc { | ||||||
| 		wc = widthConf | 		wc = widthConf | ||||||
|  | @ -121,7 +120,7 @@ func AverageETA(style int, wcc ...WC) Decorator { | ||||||
| 
 | 
 | ||||||
| type averageETA struct { | type averageETA struct { | ||||||
| 	WC | 	WC | ||||||
| 	style       int | 	style       TimeStyle | ||||||
| 	startTime   time.Time | 	startTime   time.Time | ||||||
| 	completeMsg *string | 	completeMsg *string | ||||||
| } | } | ||||||
|  | @ -133,7 +132,7 @@ func (d *averageETA) Decor(st *Statistics) string { | ||||||
| 
 | 
 | ||||||
| 	var str string | 	var str string | ||||||
| 	timeElapsed := time.Since(d.startTime) | 	timeElapsed := time.Since(d.startTime) | ||||||
| 	v := internal.Round(float64(timeElapsed) / float64(st.Current)) | 	v := math.Round(float64(timeElapsed) / float64(st.Current)) | ||||||
| 	if math.IsInf(v, 0) || math.IsNaN(v) { | 	if math.IsInf(v, 0) || math.IsNaN(v) { | ||||||
| 		v = 0 | 		v = 0 | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -6,9 +6,9 @@ import ( | ||||||
| 	"github.com/VividCortex/ewma" | 	"github.com/VividCortex/ewma" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // MovingAverage is the interface that computes a moving average over a time-
 | // MovingAverage is the interface that computes a moving average over
 | ||||||
| // series stream of numbers. The average may be over a window or exponentially
 | // a time-series stream of numbers. The average may be over a window
 | ||||||
| // decaying.
 | // or exponentially decaying.
 | ||||||
| type MovingAverage interface { | type MovingAverage interface { | ||||||
| 	Add(float64) | 	Add(float64) | ||||||
| 	Value() float64 | 	Value() float64 | ||||||
|  | @ -57,7 +57,8 @@ func (s *medianEwma) Add(v float64) { | ||||||
| 	s.count++ | 	s.count++ | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // NewMedianEwma is ewma based MovingAverage, which gets its values from median MovingAverage.
 | // NewMedianEwma is ewma based MovingAverage, which gets its values
 | ||||||
|  | // from median MovingAverage.
 | ||||||
| func NewMedianEwma(age ...float64) MovingAverage { | func NewMedianEwma(age ...float64) MovingAverage { | ||||||
| 	return &medianEwma{ | 	return &medianEwma{ | ||||||
| 		MovingAverage: ewma.NewMovingAverage(age...), | 		MovingAverage: ewma.NewMovingAverage(age...), | ||||||
|  |  | ||||||
|  | @ -137,7 +137,8 @@ func EwmaSpeed(unit int, unitFormat string, age float64, wcc ...WC) Decorator { | ||||||
| 	return MovingAverageSpeed(unit, unitFormat, ewma.NewMovingAverage(age), wcc...) | 	return MovingAverageSpeed(unit, unitFormat, ewma.NewMovingAverage(age), wcc...) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // MovingAverageSpeed decorator relies on MovingAverage implementation to calculate its average.
 | // MovingAverageSpeed decorator relies on MovingAverage implementation
 | ||||||
|  | // to calculate its average.
 | ||||||
| //
 | //
 | ||||||
| //	`unit` one of [0|UnitKiB|UnitKB] zero for no unit
 | //	`unit` one of [0|UnitKiB|UnitKB] zero for no unit
 | ||||||
| //
 | //
 | ||||||
|  |  | ||||||
|  | @ -1,10 +1,12 @@ | ||||||
| package internal | package internal | ||||||
| 
 | 
 | ||||||
|  | import "math" | ||||||
|  | 
 | ||||||
| // Percentage is a helper function, to calculate percentage.
 | // Percentage is a helper function, to calculate percentage.
 | ||||||
| func Percentage(total, current, width int64) int64 { | func Percentage(total, current, width int64) int64 { | ||||||
| 	if total <= 0 { | 	if total <= 0 { | ||||||
| 		return 0 | 		return 0 | ||||||
| 	} | 	} | ||||||
| 	p := float64(width*current) / float64(total) | 	p := float64(width*current) / float64(total) | ||||||
| 	return int64(Round(p)) | 	return int64(math.Round(p)) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,49 +0,0 @@ | ||||||
| package internal |  | ||||||
| 
 |  | ||||||
| import "math" |  | ||||||
| 
 |  | ||||||
| const ( |  | ||||||
| 	uvone    = 0x3FF0000000000000 |  | ||||||
| 	mask     = 0x7FF |  | ||||||
| 	shift    = 64 - 11 - 1 |  | ||||||
| 	bias     = 1023 |  | ||||||
| 	signMask = 1 << 63 |  | ||||||
| 	fracMask = 1<<shift - 1 |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| // Round returns the nearest integer, rounding half away from zero.
 |  | ||||||
| //
 |  | ||||||
| // Special cases are:
 |  | ||||||
| //	Round(±0) = ±0
 |  | ||||||
| //	Round(±Inf) = ±Inf
 |  | ||||||
| //	Round(NaN) = NaN
 |  | ||||||
| func Round(x float64) float64 { |  | ||||||
| 	// Round is a faster implementation of:
 |  | ||||||
| 	//
 |  | ||||||
| 	// func Round(x float64) float64 {
 |  | ||||||
| 	//   t := Trunc(x)
 |  | ||||||
| 	//   if Abs(x-t) >= 0.5 {
 |  | ||||||
| 	//     return t + Copysign(1, x)
 |  | ||||||
| 	//   }
 |  | ||||||
| 	//   return t
 |  | ||||||
| 	// }
 |  | ||||||
| 	bits := math.Float64bits(x) |  | ||||||
| 	e := uint(bits>>shift) & mask |  | ||||||
| 	if e < bias { |  | ||||||
| 		// Round abs(x) < 1 including denormals.
 |  | ||||||
| 		bits &= signMask // +-0
 |  | ||||||
| 		if e == bias-1 { |  | ||||||
| 			bits |= uvone // +-1
 |  | ||||||
| 		} |  | ||||||
| 	} else if e < bias+shift { |  | ||||||
| 		// Round any abs(x) >= 1 containing a fractional component [0,1).
 |  | ||||||
| 		//
 |  | ||||||
| 		// Numbers with larger exponents are returned unchanged since they
 |  | ||||||
| 		// must be either an integer, infinity, or NaN.
 |  | ||||||
| 		const half = 1 << (shift - 1) |  | ||||||
| 		e -= bias |  | ||||||
| 		bits += half >> e |  | ||||||
| 		bits &^= fracMask >> e |  | ||||||
| 	} |  | ||||||
| 	return math.Float64frombits(bits) |  | ||||||
| } |  | ||||||
|  | @ -1,29 +1,30 @@ | ||||||
| package mpb | package mpb | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
|  | 	"context" | ||||||
| 	"io" | 	"io" | ||||||
| 	"sync" | 	"sync" | ||||||
| 	"time" | 	"time" | ||||||
| 	"unicode/utf8" |  | ||||||
| 
 | 
 | ||||||
| 	"github.com/vbauerster/mpb/cwriter" | 	"github.com/vbauerster/mpb/cwriter" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // ProgressOption is a function option which changes the default behavior of
 | // ProgressOption is a function option which changes the default
 | ||||||
| // progress pool, if passed to mpb.New(...ProgressOption)
 | // behavior of progress pool, if passed to mpb.New(...ProgressOption).
 | ||||||
| type ProgressOption func(*pState) | type ProgressOption func(*pState) | ||||||
| 
 | 
 | ||||||
| // WithWaitGroup provides means to have a single joint point.
 | // WithWaitGroup provides means to have a single joint point. If
 | ||||||
| // If *sync.WaitGroup is provided, you can safely call just p.Wait()
 | // *sync.WaitGroup is provided, you can safely call just p.Wait()
 | ||||||
| // without calling Wait() on provided *sync.WaitGroup.
 | // without calling Wait() on provided *sync.WaitGroup. Makes sense
 | ||||||
| // Makes sense when there are more than one bar to render.
 | // when there are more than one bar to render.
 | ||||||
| func WithWaitGroup(wg *sync.WaitGroup) ProgressOption { | func WithWaitGroup(wg *sync.WaitGroup) ProgressOption { | ||||||
| 	return func(s *pState) { | 	return func(s *pState) { | ||||||
| 		s.uwg = wg | 		s.uwg = wg | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // WithWidth overrides default width 80
 | // WithWidth sets container width. Default is 80. Bars inherit this
 | ||||||
|  | // width, as long as no BarWidth is applied.
 | ||||||
| func WithWidth(w int) ProgressOption { | func WithWidth(w int) ProgressOption { | ||||||
| 	return func(s *pState) { | 	return func(s *pState) { | ||||||
| 		if w >= 0 { | 		if w >= 0 { | ||||||
|  | @ -32,16 +33,7 @@ func WithWidth(w int) ProgressOption { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // WithFormat overrides default bar format "[=>-]"
 | // WithRefreshRate overrides default 120ms refresh rate.
 | ||||||
| func WithFormat(format string) ProgressOption { |  | ||||||
| 	return func(s *pState) { |  | ||||||
| 		if utf8.RuneCountInString(format) == formatLen { |  | ||||||
| 			s.format = format |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // WithRefreshRate overrides default 120ms refresh rate
 |  | ||||||
| func WithRefreshRate(d time.Duration) ProgressOption { | func WithRefreshRate(d time.Duration) ProgressOption { | ||||||
| 	return func(s *pState) { | 	return func(s *pState) { | ||||||
| 		if d < 10*time.Millisecond { | 		if d < 10*time.Millisecond { | ||||||
|  | @ -59,22 +51,25 @@ func WithManualRefresh(ch <-chan time.Time) ProgressOption { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // WithCancel provide your cancel channel,
 | // WithContext provided context will be used for cancellation purposes.
 | ||||||
| // which you plan to close at some point.
 | func WithContext(ctx context.Context) ProgressOption { | ||||||
| func WithCancel(ch <-chan struct{}) ProgressOption { |  | ||||||
| 	return func(s *pState) { | 	return func(s *pState) { | ||||||
| 		s.cancel = ch | 		if ctx == nil { | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		s.ctx = ctx | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // WithShutdownNotifier provided chanel will be closed, after all bars have been rendered.
 | // WithShutdownNotifier provided chanel will be closed, after all bars
 | ||||||
|  | // have been rendered.
 | ||||||
| func WithShutdownNotifier(ch chan struct{}) ProgressOption { | func WithShutdownNotifier(ch chan struct{}) ProgressOption { | ||||||
| 	return func(s *pState) { | 	return func(s *pState) { | ||||||
| 		s.shutdownNotifier = ch | 		s.shutdownNotifier = ch | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // WithOutput overrides default output os.Stdout
 | // WithOutput overrides default output os.Stdout.
 | ||||||
| func WithOutput(w io.Writer) ProgressOption { | func WithOutput(w io.Writer) ProgressOption { | ||||||
| 	return func(s *pState) { | 	return func(s *pState) { | ||||||
| 		if w == nil { | 		if w == nil { | ||||||
|  |  | ||||||
|  | @ -1,15 +0,0 @@ | ||||||
| //+build go1.7
 |  | ||||||
| 
 |  | ||||||
| package mpb |  | ||||||
| 
 |  | ||||||
| import "context" |  | ||||||
| 
 |  | ||||||
| // WithContext provided context will be used for cancellation purposes
 |  | ||||||
| func WithContext(ctx context.Context) ProgressOption { |  | ||||||
| 	return func(s *pState) { |  | ||||||
| 		if ctx == nil { |  | ||||||
| 			panic("ctx must not be nil") |  | ||||||
| 		} |  | ||||||
| 		s.cancel = ctx.Done() |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  | @ -2,6 +2,7 @@ package mpb | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"container/heap" | 	"container/heap" | ||||||
|  | 	"context" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"io" | 	"io" | ||||||
| 	"io/ioutil" | 	"io/ioutil" | ||||||
|  | @ -17,8 +18,6 @@ const ( | ||||||
| 	prr = 120 * time.Millisecond | 	prr = 120 * time.Millisecond | ||||||
| 	// default width
 | 	// default width
 | ||||||
| 	pwidth = 80 | 	pwidth = 80 | ||||||
| 	// default format
 |  | ||||||
| 	pformat = "[=>-]" |  | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // Progress represents the container that renders Progress bars
 | // Progress represents the container that renders Progress bars
 | ||||||
|  | @ -42,24 +41,24 @@ type pState struct { | ||||||
| 	pMatrix         map[int][]chan int | 	pMatrix         map[int][]chan int | ||||||
| 	aMatrix         map[int][]chan int | 	aMatrix         map[int][]chan int | ||||||
| 
 | 
 | ||||||
| 	// following are provided by user
 | 	// following are provided/overrided by user
 | ||||||
|  | 	ctx              context.Context | ||||||
| 	uwg              *sync.WaitGroup | 	uwg              *sync.WaitGroup | ||||||
| 	manualRefreshCh  <-chan time.Time | 	manualRefreshCh  <-chan time.Time | ||||||
| 	cancel           <-chan struct{} |  | ||||||
| 	shutdownNotifier chan struct{} | 	shutdownNotifier chan struct{} | ||||||
| 	waitBars         map[*Bar]*Bar | 	waitBars         map[*Bar]*Bar | ||||||
| 	debugOut         io.Writer | 	debugOut         io.Writer | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // New creates new Progress instance, which orchestrates bars rendering process.
 | // New creates new Progress instance, which orchestrates bars rendering
 | ||||||
| // Accepts mpb.ProgressOption funcs for customization.
 | // process. Accepts mpb.ProgressOption funcs for customization.
 | ||||||
| func New(options ...ProgressOption) *Progress { | func New(options ...ProgressOption) *Progress { | ||||||
| 	pq := make(priorityQueue, 0) | 	pq := make(priorityQueue, 0) | ||||||
| 	heap.Init(&pq) | 	heap.Init(&pq) | ||||||
| 	s := &pState{ | 	s := &pState{ | ||||||
|  | 		ctx:      context.Background(), | ||||||
| 		bHeap:    &pq, | 		bHeap:    &pq, | ||||||
| 		width:    pwidth, | 		width:    pwidth, | ||||||
| 		format:   pformat, |  | ||||||
| 		cw:       cwriter.New(os.Stdout), | 		cw:       cwriter.New(os.Stdout), | ||||||
| 		rr:       prr, | 		rr:       prr, | ||||||
| 		waitBars: make(map[*Bar]*Bar), | 		waitBars: make(map[*Bar]*Bar), | ||||||
|  | @ -84,12 +83,28 @@ func New(options ...ProgressOption) *Progress { | ||||||
| 
 | 
 | ||||||
| // AddBar creates a new progress bar and adds to the container.
 | // AddBar creates a new progress bar and adds to the container.
 | ||||||
| func (p *Progress) AddBar(total int64, options ...BarOption) *Bar { | func (p *Progress) AddBar(total int64, options ...BarOption) *Bar { | ||||||
|  | 	return p.Add(total, newDefaultBarFiller(), options...) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // AddSpinner creates a new spinner bar and adds to the container.
 | ||||||
|  | func (p *Progress) AddSpinner(total int64, alignment SpinnerAlignment, options ...BarOption) *Bar { | ||||||
|  | 	filler := &spinnerFiller{ | ||||||
|  | 		frames:    defaultSpinnerStyle, | ||||||
|  | 		alignment: alignment, | ||||||
|  | 	} | ||||||
|  | 	return p.Add(total, filler, options...) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Add creates a bar which renders itself by provided filler.
 | ||||||
|  | func (p *Progress) Add(total int64, filler Filler, options ...BarOption) *Bar { | ||||||
|  | 	if filler == nil { | ||||||
|  | 		filler = newDefaultBarFiller() | ||||||
|  | 	} | ||||||
| 	p.wg.Add(1) | 	p.wg.Add(1) | ||||||
| 	result := make(chan *Bar) | 	result := make(chan *Bar) | ||||||
| 	select { | 	select { | ||||||
| 	case p.operateState <- func(s *pState) { | 	case p.operateState <- func(s *pState) { | ||||||
| 		options = append(options, barWidth(s.width), barFormat(s.format)) | 		b := newBar(s.ctx, p.wg, filler, s.idCounter, s.width, total, options...) | ||||||
| 		b := newBar(p.wg, s.idCounter, total, s.cancel, options...) |  | ||||||
| 		if b.runningBar != nil { | 		if b.runningBar != nil { | ||||||
| 			s.waitBars[b.runningBar] = b | 			s.waitBars[b.runningBar] = b | ||||||
| 		} else { | 		} else { | ||||||
|  | @ -106,10 +121,10 @@ func (p *Progress) AddBar(total int64, options ...BarOption) *Bar { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Abort is only effective while bar progress is running,
 | // Abort is only effective while bar progress is running, it means
 | ||||||
| // it means remove bar now without waiting for its completion.
 | // remove bar now without waiting for its completion. If bar is already
 | ||||||
| // If bar is already completed, there is nothing to abort.
 | // completed, there is nothing to abort. If you need to remove bar
 | ||||||
| // If you need to remove bar after completion, use BarRemoveOnComplete BarOption.
 | // after completion, use BarRemoveOnComplete BarOption.
 | ||||||
| func (p *Progress) Abort(b *Bar, remove bool) { | func (p *Progress) Abort(b *Bar, remove bool) { | ||||||
| 	select { | 	select { | ||||||
| 	case p.operateState <- func(s *pState) { | 	case p.operateState <- func(s *pState) { | ||||||
|  | @ -145,9 +160,10 @@ func (p *Progress) BarCount() int { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Wait first waits for user provided *sync.WaitGroup, if any,
 | // Wait first waits for user provided *sync.WaitGroup, if any, then
 | ||||||
| // then waits far all bars to complete and finally shutdowns master goroutine.
 | // waits far all bars to complete and finally shutdowns master goroutine.
 | ||||||
| // After this method has been called, there is no way to reuse *Progress instance.
 | // After this method has been called, there is no way to reuse *Progress
 | ||||||
|  | // instance.
 | ||||||
| func (p *Progress) Wait() { | func (p *Progress) Wait() { | ||||||
| 	if p.uwg != nil { | 	if p.uwg != nil { | ||||||
| 		p.uwg.Wait() | 		p.uwg.Wait() | ||||||
|  | @ -205,8 +221,8 @@ func (s *pState) flush(lineCount int) error { | ||||||
| 		defer func() { | 		defer func() { | ||||||
| 			if frameReader.toShutdown { | 			if frameReader.toShutdown { | ||||||
| 				// shutdown at next flush, in other words decrement underlying WaitGroup
 | 				// shutdown at next flush, in other words decrement underlying WaitGroup
 | ||||||
| 				// only after the bar with completed state has been flushed.
 | 				// only after the bar with completed state has been flushed. this
 | ||||||
| 				// this ensures no bar ends up with less than 100% rendered.
 | 				// ensures no bar ends up with less than 100% rendered.
 | ||||||
| 				s.shutdownPending = append(s.shutdownPending, bar) | 				s.shutdownPending = append(s.shutdownPending, bar) | ||||||
| 				if replacementBar, ok := s.waitBars[bar]; ok { | 				if replacementBar, ok := s.waitBars[bar]; ok { | ||||||
| 					heap.Push(s.bHeap, replacementBar) | 					heap.Push(s.bHeap, replacementBar) | ||||||
|  |  | ||||||
|  | @ -0,0 +1,48 @@ | ||||||
|  | package mpb | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"io" | ||||||
|  | 	"strings" | ||||||
|  | 	"unicode/utf8" | ||||||
|  | 
 | ||||||
|  | 	"github.com/vbauerster/mpb/decor" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // SpinnerAlignment enum.
 | ||||||
|  | type SpinnerAlignment int | ||||||
|  | 
 | ||||||
|  | // SpinnerAlignment kinds.
 | ||||||
|  | const ( | ||||||
|  | 	SpinnerOnLeft SpinnerAlignment = iota | ||||||
|  | 	SpinnerOnMiddle | ||||||
|  | 	SpinnerOnRight | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | var defaultSpinnerStyle = []string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"} | ||||||
|  | 
 | ||||||
|  | type spinnerFiller struct { | ||||||
|  | 	frames    []string | ||||||
|  | 	count     uint | ||||||
|  | 	alignment SpinnerAlignment | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (s *spinnerFiller) Fill(w io.Writer, width int, stat *decor.Statistics) { | ||||||
|  | 
 | ||||||
|  | 	frame := s.frames[s.count%uint(len(s.frames))] | ||||||
|  | 	frameWidth := utf8.RuneCountInString(frame) | ||||||
|  | 
 | ||||||
|  | 	if width < frameWidth { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	switch rest := width - frameWidth; s.alignment { | ||||||
|  | 	case SpinnerOnLeft: | ||||||
|  | 		io.WriteString(w, frame+strings.Repeat(" ", rest)) | ||||||
|  | 	case SpinnerOnMiddle: | ||||||
|  | 		str := strings.Repeat(" ", rest/2) + frame + strings.Repeat(" ", rest/2+rest%2) | ||||||
|  | 		io.WriteString(w, str) | ||||||
|  | 	case SpinnerOnRight: | ||||||
|  | 		io.WriteString(w, strings.Repeat(" ", rest)+frame) | ||||||
|  | 	} | ||||||
|  | 	s.count++ | ||||||
|  | } | ||||||
|  | @ -38,7 +38,7 @@ github.com/containernetworking/cni/pkg/invoke | ||||||
| github.com/containernetworking/cni/pkg/types | github.com/containernetworking/cni/pkg/types | ||||||
| github.com/containernetworking/cni/pkg/types/020 | github.com/containernetworking/cni/pkg/types/020 | ||||||
| github.com/containernetworking/cni/pkg/types/current | github.com/containernetworking/cni/pkg/types/current | ||||||
| # github.com/containers/image v2.0.0+incompatible | # github.com/containers/image v2.0.1+incompatible | ||||||
| github.com/containers/image/copy | github.com/containers/image/copy | ||||||
| github.com/containers/image/directory | github.com/containers/image/directory | ||||||
| github.com/containers/image/docker | github.com/containers/image/docker | ||||||
|  | @ -318,7 +318,7 @@ github.com/ulikunitz/xz/internal/hash | ||||||
| github.com/vbatts/tar-split/tar/asm | github.com/vbatts/tar-split/tar/asm | ||||||
| github.com/vbatts/tar-split/tar/storage | github.com/vbatts/tar-split/tar/storage | ||||||
| github.com/vbatts/tar-split/archive/tar | github.com/vbatts/tar-split/archive/tar | ||||||
| # github.com/vbauerster/mpb v3.3.4+incompatible | # github.com/vbauerster/mpb v3.4.0+incompatible | ||||||
| github.com/vbauerster/mpb | github.com/vbauerster/mpb | ||||||
| github.com/vbauerster/mpb/decor | github.com/vbauerster/mpb/decor | ||||||
| github.com/vbauerster/mpb/cwriter | github.com/vbauerster/mpb/cwriter | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue