vendor github.com/containers/image/v5@v5.2.0

See release notes:
	https://github.com/containers/image/releases/tag/v5.2.0

Fixes: #4877
Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
This commit is contained in:
Valentin Rothberg
2020-02-03 16:09:04 +01:00
parent 5092c078ec
commit 801977b40d
38 changed files with 804 additions and 255 deletions

2
go.mod
View File

@ -12,7 +12,7 @@ require (
github.com/containernetworking/plugins v0.8.5
github.com/containers/buildah v1.13.1
github.com/containers/conmon v2.0.10+incompatible
github.com/containers/image/v5 v5.1.0
github.com/containers/image/v5 v5.2.0
github.com/containers/psgo v1.4.0
github.com/containers/storage v1.15.8
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f

8
go.sum
View File

@ -30,6 +30,8 @@ github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdko
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdcM=
github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA=
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8=
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0=
@ -91,6 +93,8 @@ github.com/containers/image/v5 v5.0.0 h1:arnXgbt1ucsC/ndtSpiQY87rA0UjhF+/xQnPzqd
github.com/containers/image/v5 v5.0.0/go.mod h1:MgiLzCfIeo8lrHi+4Lb8HP+rh513sm0Mlk6RrhjFOLY=
github.com/containers/image/v5 v5.1.0 h1:5FjAvPJniamuNNIQHkh4PnsL+n+xzs6Aonzaz5dqTEo=
github.com/containers/image/v5 v5.1.0/go.mod h1:BKlMD34WxRo1ruGHHEOrPQP0Qci7SWoPwU6fS7arsCU=
github.com/containers/image/v5 v5.2.0 h1:DowY5OII5x9Pb6Pt76vnHU79BgG4/jdwhZjeAj2R+t8=
github.com/containers/image/v5 v5.2.0/go.mod h1:IAub4gDGvXoxaIAdNy4e3FbVTDPVNMv9F0UfVVFbYCU=
github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b h1:Q8ePgVfHDplZ7U33NwHZkrVELsZP5fYj9pM5WBZB2GE=
github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b/go.mod h1:9rfv8iPl1ZP7aqh9YA68wnZv2NUDbXdcdPHVz0pFbPY=
github.com/containers/ocicrypt v0.0.0-20190930154801-b87a4a69c741 h1:8tQkOcednLJtUcZgK7sPglscXtxvMOnFOa6wd09VWLM=
@ -370,6 +374,8 @@ github.com/mrunalp/fileutils v0.0.0-20171103030105-7d4729fb3618 h1:7InQ7/zrOh6Sl
github.com/mrunalp/fileutils v0.0.0-20171103030105-7d4729fb3618/go.mod h1:x8F1gnqOkIEiO4rqoeEEEqQbo7HjGMTvyoq3gej4iT0=
github.com/mtrmac/gpgme v0.0.0-20170102180018-b2432428689c h1:xa+eQWKuJ9MbB9FBL/eoNvDFvveAkz2LQoz8PzX7Q/4=
github.com/mtrmac/gpgme v0.0.0-20170102180018-b2432428689c/go.mod h1:GhAqVMEWnTcW2dxoD/SO3n2enrgWl3y6Dnx4m59GvcA=
github.com/mtrmac/gpgme v0.1.1 h1:a5ISnvahzTzBH0m/klhehN68N+9+/jLwhpPFtH3oPAQ=
github.com/mtrmac/gpgme v0.1.1/go.mod h1:GYYHnGSuS7HK3zVS2n3y73y0okK/BeKzwnn5jgiVFNI=
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
@ -544,6 +550,8 @@ github.com/vbauerster/mpb v3.4.0+incompatible h1:mfiiYw87ARaeRW6x5gWwYRUawxaW1tL
github.com/vbauerster/mpb v3.4.0+incompatible/go.mod h1:zAHG26FUhVKETRu+MWqYXcI70POlC6N8up9p1dID7SU=
github.com/vbauerster/mpb/v4 v4.11.1 h1:ZOYQSVHgmeanXsbyC44aDg76tBGCS/54Rk8VkL8dJGA=
github.com/vbauerster/mpb/v4 v4.11.1/go.mod h1:vMLa1J/ZKC83G2lB/52XpqT+ZZtFG4aZOdKhmpRL1uM=
github.com/vbauerster/mpb/v4 v4.11.2 h1:ynkUoKzi65DZ1UsQPx7sgi/KN6G9f7br+Us2nKm35AM=
github.com/vbauerster/mpb/v4 v4.11.2/go.mod h1:jIuIRCltGJUnm6DCyPVkwjlLUk4nHTH+m4eD14CdFF0=
github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk=
github.com/vishvananda/netlink v1.0.0 h1:bqNY2lgheFIu1meHUFSH3d7vG93AFyqg3oGbJCOJgSM=
github.com/vishvananda/netlink v1.0.0/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk=

21
vendor/github.com/acarl005/stripansi/LICENSE generated vendored Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2018 Andrew Carlson
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

30
vendor/github.com/acarl005/stripansi/README.md generated vendored Normal file
View File

@ -0,0 +1,30 @@
Strip ANSI
==========
This Go package removes ANSI escape codes from strings.
Ideally, we would prevent these from appearing in any text we want to process.
However, sometimes this can't be helped, and we need to be able to deal with that noise.
This will use a regexp to remove those unwanted escape codes.
## Install
```sh
$ go get -u github.com/acarl005/stripansi
```
## Usage
```go
import (
"fmt"
"github.com/acarl005/stripansi"
)
func main() {
msg := "\x1b[38;5;140m foo\x1b[0m bar"
cleanMsg := stripansi.Strip(msg)
fmt.Println(cleanMsg) // " foo bar"
}
```

13
vendor/github.com/acarl005/stripansi/stripansi.go generated vendored Normal file
View File

@ -0,0 +1,13 @@
package stripansi
import (
"regexp"
)
const ansi = "[\u001B\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[a-zA-Z\\d]*)*)?\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PRZcf-ntqry=><~]))"
var re = regexp.MustCompile(ansi)
func Strip(str string) string {
return re.ReplaceAllString(str, "")
}

View File

@ -709,7 +709,7 @@ func checkImageDestinationForCurrentRuntime(ctx context.Context, sys *types.Syst
wantedOS = sys.OSChoice
}
if wantedOS != c.OS {
return fmt.Errorf("Image operating system mismatch: image uses %q, expecting %q", c.OS, wantedOS)
logrus.Infof("Image operating system mismatch: image uses %q, expecting %q", c.OS, wantedOS)
}
wantedArch := runtime.GOARCH
@ -717,7 +717,7 @@ func checkImageDestinationForCurrentRuntime(ctx context.Context, sys *types.Syst
wantedArch = sys.ArchitectureChoice
}
if wantedArch != c.Architecture {
return fmt.Errorf("Image architecture mismatch: image uses %q, expecting %q", c.Architecture, wantedArch)
logrus.Infof("Image architecture mismatch: image uses %q, expecting %q", c.Architecture, wantedArch)
}
}
return nil

View File

@ -6,6 +6,7 @@ import (
"io/ioutil"
"os"
"path/filepath"
"runtime"
"github.com/containers/image/v5/types"
"github.com/opencontainers/go-digest"
@ -142,8 +143,11 @@ func (d *dirImageDestination) PutBlob(ctx context.Context, stream io.Reader, inp
return types.BlobInfo{}, err
}
succeeded := false
explicitClosed := false
defer func() {
if !explicitClosed {
blobFile.Close()
}
if !succeeded {
os.Remove(blobFile.Name())
}
@ -164,10 +168,21 @@ func (d *dirImageDestination) PutBlob(ctx context.Context, stream io.Reader, inp
if err := blobFile.Sync(); err != nil {
return types.BlobInfo{}, err
}
// On POSIX systems, blobFile was created with mode 0600, so we need to make it readable.
// On Windows, the “permissions of newly created files” argument to syscall.Open is
// ignored and the file is already readable; besides, blobFile.Chmod, i.e. syscall.Fchmod,
// always fails on Windows.
if runtime.GOOS != "windows" {
if err := blobFile.Chmod(0644); err != nil {
return types.BlobInfo{}, err
}
}
blobPath := d.ref.layerPath(computedDigest)
// need to explicitly close the file, since a rename won't otherwise not work on Windows
blobFile.Close()
explicitClosed = true
if err := os.Rename(blobFile.Name(), blobPath); err != nil {
return types.BlobInfo{}, err
}

View File

@ -6,7 +6,6 @@ import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
"os"
@ -17,6 +16,7 @@ import (
"time"
"github.com/containers/image/v5/docker/reference"
"github.com/containers/image/v5/internal/iolimits"
"github.com/containers/image/v5/pkg/docker/config"
"github.com/containers/image/v5/pkg/sysregistriesv2"
"github.com/containers/image/v5/pkg/tlsclientconfig"
@ -45,6 +45,10 @@ const (
extensionSignatureSchemaVersion = 2 // extensionSignature.Version
extensionSignatureTypeAtomic = "atomic" // extensionSignature.Type
backoffNumIterations = 5
backoffInitialDelay = 2 * time.Second
backoffMaxDelay = 60 * time.Second
)
var systemPerHostCertDirPaths = [2]string{"/etc/containers/certs.d", "/etc/docker/certs.d"}
@ -277,7 +281,7 @@ func CheckAuth(ctx context.Context, sys *types.SystemContext, username, password
}
defer resp.Body.Close()
return httpResponseToError(resp)
return httpResponseToError(resp, "")
}
// SearchResult holds the information of each matching image
@ -351,7 +355,7 @@ func SearchRegistry(ctx context.Context, sys *types.SystemContext, registry, ima
} else {
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
logrus.Debugf("error getting search results from v1 endpoint %q: %v", registry, httpResponseToError(resp))
logrus.Debugf("error getting search results from v1 endpoint %q: %v", registry, httpResponseToError(resp, ""))
} else {
if err := json.NewDecoder(resp.Body).Decode(v1Res); err != nil {
return nil, err
@ -368,7 +372,7 @@ func SearchRegistry(ctx context.Context, sys *types.SystemContext, registry, ima
} else {
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
logrus.Errorf("error getting search results from v2 endpoint %q: %v", registry, httpResponseToError(resp))
logrus.Errorf("error getting search results from v2 endpoint %q: %v", registry, httpResponseToError(resp, ""))
} else {
if err := json.NewDecoder(resp.Body).Decode(v2Res); err != nil {
return nil, err
@ -400,74 +404,64 @@ func (c *dockerClient) makeRequest(ctx context.Context, method, path string, hea
return c.makeRequestToResolvedURL(ctx, method, url, headers, stream, -1, auth, extraScope)
}
// makeRequestToResolvedURL creates and executes a http.Request with the specified parameters, adding authentication and TLS options for the Docker client.
// streamLen, if not -1, specifies the length of the data expected on stream.
// makeRequest should generally be preferred.
// In case of an http 429 status code in the response, it performs an exponential back off starting at 2 seconds for at most 5 iterations.
// If the `Retry-After` header is set in the response, the specified value or date is
// If the stream is non-nil, no back off will be performed.
// TODO(runcom): too many arguments here, use a struct
func (c *dockerClient) makeRequestToResolvedURL(ctx context.Context, method, url string, headers map[string][]string, stream io.Reader, streamLen int64, auth sendAuth, extraScope *authScope) (*http.Response, error) {
var (
res *http.Response
err error
delay int64
)
delay = 2
const numIterations = 5
const maxDelay = 60
// math.Min() only supports float64, so have an anonymous func to avoid
// casting.
min := func(a int64, b int64) int64 {
if a < b {
return a
}
return b
}
nextDelay := func(r *http.Response, delay int64) int64 {
// parseRetryAfter determines the delay required by the "Retry-After" header in res and returns it,
// silently falling back to fallbackDelay if the header is missing or invalid.
func parseRetryAfter(res *http.Response, fallbackDelay time.Duration) time.Duration {
after := res.Header.Get("Retry-After")
if after == "" {
return min(delay, maxDelay)
return fallbackDelay
}
logrus.Debugf("detected 'Retry-After' header %q", after)
// First check if we have a numerical value.
logrus.Debugf("Detected 'Retry-After' header %q", after)
// First, check if we have a numerical value.
if num, err := strconv.ParseInt(after, 10, 64); err == nil {
return min(num, maxDelay)
return time.Duration(num) * time.Second
}
// Secondly check if we have an http date.
// Second, check if we have an HTTP date.
// If the delta between the date and now is positive, use it.
// Otherwise, fall back to using the default exponential back off.
if t, err := http.ParseTime(after); err == nil {
delta := int64(time.Until(t).Seconds())
delta := time.Until(t)
if delta > 0 {
return min(delta, maxDelay)
return delta
}
logrus.Debugf("negative date: falling back to using %d seconds", delay)
return min(delay, maxDelay)
logrus.Debugf("Retry-After date in the past, ignoring it")
return fallbackDelay
}
// If the header contains bogus, fall back to using the default
// exponential back off.
logrus.Debugf("invalid format: falling back to using %d seconds", delay)
return min(delay, maxDelay)
// If the header contents are bogus, fall back to using the default exponential back off.
logrus.Debugf("Invalid Retry-After format, ignoring it")
return fallbackDelay
}
for i := 0; i < numIterations; i++ {
res, err = c.makeRequestToResolvedURLOnce(ctx, method, url, headers, stream, streamLen, auth, extraScope)
if stream == nil && res != nil && res.StatusCode == http.StatusTooManyRequests {
if i < numIterations-1 {
logrus.Errorf("HEADER %v", res.Header)
delay = nextDelay(res, delay) // compute next delay - does NOT exceed maxDelay
logrus.Debugf("too many request to %s: sleeping for %d seconds before next attempt", url, delay)
time.Sleep(time.Duration(delay) * time.Second)
// makeRequestToResolvedURL creates and executes a http.Request with the specified parameters, adding authentication and TLS options for the Docker client.
// streamLen, if not -1, specifies the length of the data expected on stream.
// makeRequest should generally be preferred.
// In case of an HTTP 429 status code in the response, it may automatically retry a few times.
// TODO(runcom): too many arguments here, use a struct
func (c *dockerClient) makeRequestToResolvedURL(ctx context.Context, method, url string, headers map[string][]string, stream io.Reader, streamLen int64, auth sendAuth, extraScope *authScope) (*http.Response, error) {
delay := backoffInitialDelay
attempts := 0
for {
res, err := c.makeRequestToResolvedURLOnce(ctx, method, url, headers, stream, streamLen, auth, extraScope)
attempts++
if res == nil || res.StatusCode != http.StatusTooManyRequests || // Only retry on StatusTooManyRequests, success or other failure is returned to caller immediately
stream != nil || // We can't retry with a body (which is not restartable in the general case)
attempts == backoffNumIterations {
return res, err
}
delay = parseRetryAfter(res, delay)
if delay > backoffMaxDelay {
delay = backoffMaxDelay
}
logrus.Debugf("Too many requests to %s: sleeping for %f seconds before next attempt", url, delay.Seconds())
select {
case <-ctx.Done():
return nil, ctx.Err()
case <-time.After(delay):
// Nothing
}
delay = delay * 2 // exponential back off
}
continue
}
break
}
return res, err
}
// makeRequestToResolvedURLOnce creates and executes a http.Request with the specified parameters, adding authentication and TLS options for the Docker client.
@ -597,7 +591,7 @@ func (c *dockerClient) getBearerToken(ctx context.Context, challenge challenge,
default:
return nil, errors.Errorf("unexpected http code: %d (%s), URL: %s", res.StatusCode, http.StatusText(res.StatusCode), authReq.URL)
}
tokenBlob, err := ioutil.ReadAll(res.Body)
tokenBlob, err := iolimits.ReadAtMost(res.Body, iolimits.MaxAuthTokenBodySize)
if err != nil {
return nil, err
}
@ -627,7 +621,7 @@ func (c *dockerClient) detectPropertiesHelper(ctx context.Context) error {
defer resp.Body.Close()
logrus.Debugf("Ping %s status %d", url, resp.StatusCode)
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusUnauthorized {
return httpResponseToError(resp)
return httpResponseToError(resp, "")
}
c.challenges = parseAuthHeader(resp.Header)
c.scheme = scheme
@ -690,7 +684,7 @@ func (c *dockerClient) getExtensionsSignatures(ctx context.Context, ref dockerRe
return nil, errors.Wrapf(clientLib.HandleErrorResponse(res), "Error downloading signatures for %s in %s", manifestDigest, ref.ref.Name())
}
body, err := ioutil.ReadAll(res.Body)
body, err := iolimits.ReadAtMost(res.Body, iolimits.MaxSignatureListBodySize)
if err != nil {
return nil, err
}

View File

@ -70,7 +70,7 @@ func GetRepositoryTags(ctx context.Context, sys *types.SystemContext, ref types.
return nil, err
}
defer res.Body.Close()
if err := httpResponseToError(res); err != nil {
if err := httpResponseToError(res, "Error fetching tags list"); err != nil {
return nil, err
}

View File

@ -15,6 +15,7 @@ import (
"strings"
"github.com/containers/image/v5/docker/reference"
"github.com/containers/image/v5/internal/iolimits"
"github.com/containers/image/v5/manifest"
"github.com/containers/image/v5/pkg/blobinfocache/none"
"github.com/containers/image/v5/types"
@ -58,14 +59,16 @@ func (d *dockerImageDestination) Close() error {
}
func (d *dockerImageDestination) SupportedManifestMIMETypes() []string {
return []string{
mimeTypes := []string{
imgspecv1.MediaTypeImageManifest,
manifest.DockerV2Schema2MediaType,
imgspecv1.MediaTypeImageIndex,
manifest.DockerV2ListMediaType,
manifest.DockerV2Schema1SignedMediaType,
manifest.DockerV2Schema1MediaType,
}
if d.c.sys == nil || !d.c.sys.DockerDisableDestSchema1MIMETypes {
mimeTypes = append(mimeTypes, manifest.DockerV2Schema1SignedMediaType, manifest.DockerV2Schema1MediaType)
}
return mimeTypes
}
// SupportsSignatures returns an error (to be displayed to the user) if the destination certainly can't store signatures.
@ -620,7 +623,7 @@ sigExists:
}
defer res.Body.Close()
if res.StatusCode != http.StatusCreated {
body, err := ioutil.ReadAll(res.Body)
body, err := iolimits.ReadAtMost(res.Body, iolimits.MaxErrorBodySize)
if err == nil {
logrus.Debugf("Error body %s", string(body))
}

View File

@ -10,8 +10,10 @@ import (
"net/url"
"os"
"strconv"
"strings"
"github.com/containers/image/v5/docker/reference"
"github.com/containers/image/v5/internal/iolimits"
"github.com/containers/image/v5/manifest"
"github.com/containers/image/v5/pkg/sysregistriesv2"
"github.com/containers/image/v5/types"
@ -53,43 +55,77 @@ func newImageSource(ctx context.Context, sys *types.SystemContext, ref dockerRef
// non-mirror original location last; this both transparently handles the case
// of no mirrors configured, and ensures we return the error encountered when
// acessing the upstream location if all endpoints fail.
manifestLoadErr := errors.New("Internal error: newImageSource returned without trying any endpoint")
pullSources, err := registry.PullSourcesFromReference(ref.ref)
if err != nil {
return nil, err
}
type attempt struct {
ref reference.Named
err error
}
attempts := []attempt{}
for _, pullSource := range pullSources {
logrus.Debugf("Trying to pull %q", pullSource.Reference)
dockerRef, err := newReference(pullSource.Reference)
logrus.Debugf("Trying to access %q", pullSource.Reference)
s, err := newImageSourceAttempt(ctx, sys, pullSource, primaryDomain)
if err == nil {
return s, nil
}
logrus.Debugf("Accessing %q failed: %v", pullSource.Reference, err)
attempts = append(attempts, attempt{
ref: pullSource.Reference,
err: err,
})
}
switch len(attempts) {
case 0:
return nil, errors.New("Internal error: newImageSource returned without trying any endpoint")
case 1:
return nil, attempts[0].err // If no mirrors are used, perfectly preserve the error type and add no noise.
default:
// Dont just build a string, try to preserve the typed error.
primary := &attempts[len(attempts)-1]
extras := []string{}
for i := 0; i < len(attempts)-1; i++ {
// This is difficult to fit into a single-line string, when the error can contain arbitrary strings including any metacharacters we decide to use.
// The paired [] at least have some chance of being unambiguous.
extras = append(extras, fmt.Sprintf("[%s: %v]", attempts[i].ref.String(), attempts[i].err))
}
return nil, errors.Wrapf(primary.err, "(Mirrors also failed: %s): %s", strings.Join(extras, "\n"), primary.ref.String())
}
}
// newImageSourceAttempt is an internal helper for newImageSource. Everyone else must call newImageSource.
// Given a pullSource and primaryDomain, return a dockerImageSource if it is reachable.
// The caller must call .Close() on the returned ImageSource.
func newImageSourceAttempt(ctx context.Context, sys *types.SystemContext, pullSource sysregistriesv2.PullSource, primaryDomain string) (*dockerImageSource, error) {
ref, err := newReference(pullSource.Reference)
if err != nil {
return nil, err
}
endpointSys := sys
// sys.DockerAuthConfig does not explicitly specify a registry; we must not blindly send the credentials intended for the primary endpoint to mirrors.
if endpointSys != nil && endpointSys.DockerAuthConfig != nil && reference.Domain(dockerRef.ref) != primaryDomain {
if endpointSys != nil && endpointSys.DockerAuthConfig != nil && reference.Domain(ref.ref) != primaryDomain {
copy := *endpointSys
copy.DockerAuthConfig = nil
endpointSys = &copy
}
client, err := newDockerClientFromRef(endpointSys, dockerRef, false, "pull")
client, err := newDockerClientFromRef(endpointSys, ref, false, "pull")
if err != nil {
return nil, err
}
client.tlsClientConfig.InsecureSkipVerify = pullSource.Endpoint.Insecure
testImageSource := &dockerImageSource{
ref: dockerRef,
s := &dockerImageSource{
ref: ref,
c: client,
}
manifestLoadErr = testImageSource.ensureManifestIsLoaded(ctx)
if manifestLoadErr == nil {
return testImageSource, nil
if err := s.ensureManifestIsLoaded(ctx); err != nil {
return nil, err
}
}
return nil, manifestLoadErr
return s, nil
}
// Reference returns the reference used to set up this source, _as specified by the user_
@ -156,7 +192,8 @@ func (s *dockerImageSource) fetchManifest(ctx context.Context, tagOrDigest strin
if res.StatusCode != http.StatusOK {
return nil, "", errors.Wrapf(client.HandleErrorResponse(res), "Error reading manifest %s in %s", tagOrDigest, s.ref.ref.Name())
}
manblob, err := ioutil.ReadAll(res.Body)
manblob, err := iolimits.ReadAtMost(res.Body, iolimits.MaxManifestBodySize)
if err != nil {
return nil, "", err
}
@ -239,7 +276,7 @@ func (s *dockerImageSource) GetBlob(ctx context.Context, info types.BlobInfo, ca
if err != nil {
return nil, 0, err
}
if err := httpResponseToError(res); err != nil {
if err := httpResponseToError(res, "Error fetching blob"); err != nil {
return nil, 0, err
}
cache.RecordKnownLocation(s.ref.Transport(), bicTransportScope(s.ref), info.Digest, newBICLocationReference(s.ref))
@ -342,7 +379,7 @@ func (s *dockerImageSource) getOneSignature(ctx context.Context, url *url.URL) (
} else if res.StatusCode != http.StatusOK {
return nil, false, errors.Errorf("Error reading signature from %s: status %d (%s)", url.String(), res.StatusCode, http.StatusText(res.StatusCode))
}
sig, err := ioutil.ReadAll(res.Body)
sig, err := iolimits.ReadAtMost(res.Body, iolimits.MaxSignatureBodySize)
if err != nil {
return nil, false, err
}
@ -401,7 +438,7 @@ func deleteImage(ctx context.Context, sys *types.SystemContext, ref dockerRefere
return err
}
defer get.Body.Close()
manifestBody, err := ioutil.ReadAll(get.Body)
manifestBody, err := iolimits.ReadAtMost(get.Body, iolimits.MaxManifestBodySize)
if err != nil {
return err
}
@ -424,7 +461,7 @@ func deleteImage(ctx context.Context, sys *types.SystemContext, ref dockerRefere
}
defer delete.Body.Close()
body, err := ioutil.ReadAll(delete.Body)
body, err := iolimits.ReadAtMost(delete.Body, iolimits.MaxErrorBodySize)
if err != nil {
return err
}

View File

@ -14,7 +14,7 @@ var (
// docker V1 registry.
ErrV1NotSupported = errors.New("can't talk to a V1 docker registry")
// ErrTooManyRequests is returned when the status code returned is 429
ErrTooManyRequests = errors.New("too many request to registry")
ErrTooManyRequests = errors.New("too many requests to registry")
)
// ErrUnauthorizedForCredentials is returned when the status code returned is 401
@ -26,9 +26,9 @@ func (e ErrUnauthorizedForCredentials) Error() string {
return fmt.Sprintf("unable to retrieve auth token: invalid username/password: %s", e.Err.Error())
}
// httpResponseToError translates the https.Response into an error. It returns
// httpResponseToError translates the https.Response into an error, possibly prefixing it with the supplied context. It returns
// nil if the response is not considered an error.
func httpResponseToError(res *http.Response) error {
func httpResponseToError(res *http.Response, context string) error {
switch res.StatusCode {
case http.StatusOK:
return nil
@ -38,6 +38,9 @@ func httpResponseToError(res *http.Response) error {
err := client.HandleErrorResponse(res)
return ErrUnauthorizedForCredentials{Err: err}
default:
return perrors.Errorf("invalid status code from registry %d (%s)", res.StatusCode, http.StatusText(res.StatusCode))
if context != "" {
context = context + ": "
}
return perrors.Errorf("%sinvalid status code from registry %d (%s)", context, res.StatusCode, http.StatusText(res.StatusCode))
}
}

View File

@ -13,6 +13,7 @@ import (
"time"
"github.com/containers/image/v5/docker/reference"
"github.com/containers/image/v5/internal/iolimits"
"github.com/containers/image/v5/internal/tmpdir"
"github.com/containers/image/v5/manifest"
"github.com/containers/image/v5/types"
@ -143,7 +144,7 @@ func (d *Destination) PutBlob(ctx context.Context, stream io.Reader, inputInfo t
}
if isConfig {
buf, err := ioutil.ReadAll(stream)
buf, err := iolimits.ReadAtMost(stream, iolimits.MaxConfigBodySize)
if err != nil {
return types.BlobInfo{}, errors.Wrap(err, "Error reading Config file stream")
}

View File

@ -11,6 +11,7 @@ import (
"path"
"sync"
"github.com/containers/image/v5/internal/iolimits"
"github.com/containers/image/v5/internal/tmpdir"
"github.com/containers/image/v5/manifest"
"github.com/containers/image/v5/pkg/compression"
@ -203,13 +204,13 @@ func findTarComponent(inputFile io.Reader, path string) (*tar.Reader, *tar.Heade
}
// readTarComponent returns full contents of componentPath.
func (s *Source) readTarComponent(path string) ([]byte, error) {
func (s *Source) readTarComponent(path string, limit int) ([]byte, error) {
file, err := s.openTarComponent(path)
if err != nil {
return nil, errors.Wrapf(err, "Error loading tar component %s", path)
}
defer file.Close()
bytes, err := ioutil.ReadAll(file)
bytes, err := iolimits.ReadAtMost(file, limit)
if err != nil {
return nil, err
}
@ -240,7 +241,7 @@ func (s *Source) ensureCachedDataIsPresentPrivate() error {
}
// Read and parse config.
configBytes, err := s.readTarComponent(tarManifest[0].Config)
configBytes, err := s.readTarComponent(tarManifest[0].Config, iolimits.MaxConfigBodySize)
if err != nil {
return err
}
@ -266,7 +267,7 @@ func (s *Source) ensureCachedDataIsPresentPrivate() error {
// loadTarManifest loads and decodes the manifest.json.
func (s *Source) loadTarManifest() ([]ManifestItem, error) {
// FIXME? Do we need to deal with the legacy format?
bytes, err := s.readTarComponent(manifestFileName)
bytes, err := s.readTarComponent(manifestFileName, iolimits.MaxTarFileManifestSize)
if err != nil {
return nil, err
}

View File

@ -7,10 +7,10 @@ import (
"encoding/hex"
"encoding/json"
"fmt"
"io/ioutil"
"strings"
"github.com/containers/image/v5/docker/reference"
"github.com/containers/image/v5/internal/iolimits"
"github.com/containers/image/v5/manifest"
"github.com/containers/image/v5/pkg/blobinfocache/none"
"github.com/containers/image/v5/types"
@ -102,7 +102,7 @@ func (m *manifestSchema2) ConfigBlob(ctx context.Context) ([]byte, error) {
return nil, err
}
defer stream.Close()
blob, err := ioutil.ReadAll(stream)
blob, err := iolimits.ReadAtMost(stream, iolimits.MaxConfigBodySize)
if err != nil {
return nil, err
}

View File

@ -4,9 +4,9 @@ import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"github.com/containers/image/v5/docker/reference"
"github.com/containers/image/v5/internal/iolimits"
"github.com/containers/image/v5/manifest"
"github.com/containers/image/v5/pkg/blobinfocache/none"
"github.com/containers/image/v5/types"
@ -67,7 +67,7 @@ func (m *manifestOCI1) ConfigBlob(ctx context.Context) ([]byte, error) {
return nil, err
}
defer stream.Close()
blob, err := ioutil.ReadAll(stream)
blob, err := iolimits.ReadAtMost(stream, iolimits.MaxConfigBodySize)
if err != nil {
return nil, err
}

View File

@ -0,0 +1,60 @@
package iolimits
import (
"io"
"io/ioutil"
"github.com/pkg/errors"
)
// All constants below are intended to be used as limits for `ReadAtMost`. The
// immediate use-case for limiting the size of in-memory copied data is to
// protect against OOM DOS attacks as described inCVE-2020-1702. Instead of
// copying data until running out of memory, we error out after hitting the
// specified limit.
const (
// megaByte denotes one megabyte and is intended to be used as a limit in
// `ReadAtMost`.
megaByte = 1 << 20
// MaxManifestBodySize is the maximum allowed size of a manifest. The limit
// of 4 MB aligns with the one of a Docker registry:
// https://github.com/docker/distribution/blob/a8371794149d1d95f1e846744b05c87f2f825e5a/registry/handlers/manifests.go#L30
MaxManifestBodySize = 4 * megaByte
// MaxAuthTokenBodySize is the maximum allowed size of an auth token.
// The limit of 1 MB is considered to be greatly sufficient.
MaxAuthTokenBodySize = megaByte
// MaxSignatureListBodySize is the maximum allowed size of a signature list.
// The limit of 4 MB is considered to be greatly sufficient.
MaxSignatureListBodySize = 4 * megaByte
// MaxSignatureBodySize is the maximum allowed size of a signature.
// The limit of 4 MB is considered to be greatly sufficient.
MaxSignatureBodySize = 4 * megaByte
// MaxErrorBodySize is the maximum allowed size of an error-response body.
// The limit of 1 MB is considered to be greatly sufficient.
MaxErrorBodySize = megaByte
// MaxConfigBodySize is the maximum allowed size of a config blob.
// The limit of 4 MB is considered to be greatly sufficient.
MaxConfigBodySize = 4 * megaByte
// MaxOpenShiftStatusBody is the maximum allowed size of an OpenShift status body.
// The limit of 4 MB is considered to be greatly sufficient.
MaxOpenShiftStatusBody = 4 * megaByte
// MaxTarFileManifestSize is the maximum allowed size of a (docker save)-like manifest (which may contain multiple images)
// The limit of 1 MB is considered to be greatly sufficient.
MaxTarFileManifestSize = megaByte
)
// ReadAtMost reads from reader and errors out if the specified limit (in bytes) is exceeded.
func ReadAtMost(reader io.Reader, limit int) ([]byte, error) {
limitedReader := io.LimitReader(reader, int64(limit+1))
res, err := ioutil.ReadAll(limitedReader)
if err != nil {
return nil, err
}
if len(res) > limit {
return nil, errors.Errorf("exceeded maximum allowed size of %d bytes", limit)
}
return res, nil
}

View File

@ -7,13 +7,13 @@ import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
"strings"
"github.com/containers/image/v5/docker"
"github.com/containers/image/v5/docker/reference"
"github.com/containers/image/v5/internal/iolimits"
"github.com/containers/image/v5/manifest"
"github.com/containers/image/v5/types"
"github.com/containers/image/v5/version"
@ -102,7 +102,7 @@ func (c *openshiftClient) doRequest(ctx context.Context, method, path string, re
return nil, err
}
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
body, err := iolimits.ReadAtMost(res.Body, iolimits.MaxOpenShiftStatusBody)
if err != nil {
return nil, err
}

View File

@ -139,7 +139,7 @@ func (m *gpgmeSigningMechanism) Sign(input []byte, keyIdentity string) ([]byte,
}
// Verify parses unverifiedSignature and returns the content and the signer's identity
func (m gpgmeSigningMechanism) Verify(unverifiedSignature []byte) (contents []byte, keyIdentity string, err error) {
func (m *gpgmeSigningMechanism) Verify(unverifiedSignature []byte) (contents []byte, keyIdentity string, err error) {
signedBuffer := bytes.Buffer{}
signedData, err := gpgme.NewDataWriter(&signedBuffer)
if err != nil {
@ -170,6 +170,6 @@ func (m gpgmeSigningMechanism) Verify(unverifiedSignature []byte) (contents []by
// WARNING: The short key identifier (which correponds to "Key ID" for OpenPGP keys)
// is NOT the same as a "key identity" used in other calls ot this interface, and
// the values may have no recognizable relationship if the public key is not available.
func (m gpgmeSigningMechanism) UntrustedSignatureContents(untrustedSignature []byte) (untrustedContents []byte, shortKeyIdentifier string, err error) {
func (m *gpgmeSigningMechanism) UntrustedSignatureContents(untrustedSignature []byte) (untrustedContents []byte, shortKeyIdentifier string, err error) {
return gpgUntrustedSignatureContents(untrustedSignature)
}

View File

@ -154,6 +154,6 @@ func (m *openpgpSigningMechanism) Verify(unverifiedSignature []byte) (contents [
// WARNING: The short key identifier (which correponds to "Key ID" for OpenPGP keys)
// is NOT the same as a "key identity" used in other calls ot this interface, and
// the values may have no recognizable relationship if the public key is not available.
func (m openpgpSigningMechanism) UntrustedSignatureContents(untrustedSignature []byte) (untrustedContents []byte, shortKeyIdentifier string, err error) {
func (m *openpgpSigningMechanism) UntrustedSignatureContents(untrustedSignature []byte) (untrustedContents []byte, shortKeyIdentifier string, err error) {
return gpgUntrustedSignatureContents(untrustedSignature)
}

View File

@ -547,6 +547,8 @@ type SystemContext struct {
// Note that this field is used mainly to integrate containers/image into projectatomic/docker
// in order to not break any existing docker's integration tests.
DockerDisableV1Ping bool
// If true, dockerImageDestination.SupportedManifestMIMETypes will omit the Schema1 media types from the supported list
DockerDisableDestSchema1MIMETypes bool
// Directory to use for OSTree temporary files
OSTreeTmpDirPath string

View File

@ -6,7 +6,7 @@ const (
// VersionMajor is for an API incompatible changes
VersionMajor = 5
// VersionMinor is for functionality in a backwards-compatible manner
VersionMinor = 1
VersionMinor = 2
// VersionPatch is for backwards-compatible bug fixes
VersionPatch = 0

40
vendor/github.com/mtrmac/gpgme/.appveyor.yml generated vendored Normal file
View File

@ -0,0 +1,40 @@
---
version: 0.{build}
platform: x86
branches:
only:
- master
clone_folder: c:\gopath\src\github.com\proglottis\gpgme
environment:
GOPATH: c:\gopath
GOROOT: C:\go-x86
CGO_LDFLAGS: -LC:\gpg\lib
CGO_CFLAGS: -IC:\gpg\include
GPG_DIR: C:\gpg
install:
- nuget install 7ZipCLI -ExcludeVersion
- set PATH=%appveyor_build_folder%\7ZipCLI\tools;%PATH%
- appveyor DownloadFile https://www.gnupg.org/ftp/gcrypt/binary/gnupg-w32-2.1.20_20170403.exe -FileName gnupg-w32-2.1.20_20170403.exe
- 7z x -o%GPG_DIR% gnupg-w32-2.1.20_20170403.exe
- copy "%GPG_DIR%\lib\libgpg-error.imp" "%GPG_DIR%\lib\libgpg-error.a"
- copy "%GPG_DIR%\lib\libassuan.imp" "%GPG_DIR%\lib\libassuan.a"
- copy "%GPG_DIR%\lib\libgpgme.imp" "%GPG_DIR%\lib\libgpgme.a"
- set PATH=%GOPATH%\bin;%GOROOT%\bin;%GPG_DIR%\bin;C:\MinGW\bin;%PATH%
- C:\cygwin\bin\sed -i 's/"GPG_AGENT_INFO"/"GPG_AGENT_INFO="/;s/C.unsetenv(v)/C.putenv(v)/' %APPVEYOR_BUILD_FOLDER%\gpgme.go
test_script:
- go test -v github.com/proglottis/gpgme
build_script:
- go build -o example_decrypt.exe -i %APPVEYOR_BUILD_FOLDER%\examples\decrypt.go
- go build -o example_encrypt.exe -i %APPVEYOR_BUILD_FOLDER%\examples\encrypt.go
artifacts:
- path: example_decrypt.exe
name: decrypt example binary
- path: example_encrypt.exe
name: encrypt example binary

32
vendor/github.com/mtrmac/gpgme/.travis.yml generated vendored Normal file
View File

@ -0,0 +1,32 @@
---
language: go
os:
- linux
- osx
- windows
dist: xenial
sudo: false
go:
- 1.11
- 1.12
- 1.13
addons:
apt:
packages:
- libgpgme11-dev
homebrew:
packages:
- gnupg
- gnupg@1.4
- gpgme
update: true
matrix:
allow_failures:
- os: windows
before_install:
- if [[ "$TRAVIS_OS_NAME" == "windows" ]]; then choco install msys2; fi
- if [[ "$TRAVIS_OS_NAME" == "windows" ]]; then choco install gpg4win; fi

View File

@ -50,25 +50,25 @@ func gogpgme_writefunc(handle, buffer unsafe.Pointer, size C.size_t) C.ssize_t {
}
//export gogpgme_seekfunc
func gogpgme_seekfunc(handle unsafe.Pointer, offset C.off_t, whence C.int) C.off_t {
func gogpgme_seekfunc(handle unsafe.Pointer, offset C.gpgme_off_t, whence C.int) C.gpgme_off_t {
d := callbackLookup(uintptr(handle)).(*Data)
n, err := d.s.Seek(int64(offset), int(whence))
if err != nil {
C.gpgme_err_set_errno(C.EIO)
return -1
}
return C.off_t(n)
return C.gpgme_off_t(n)
}
// The Data buffer used to communicate with GPGME
type Data struct {
dh C.gpgme_data_t
dh C.gpgme_data_t // WARNING: Call runtime.KeepAlive(d) after ANY passing of d.dh to C
buf []byte
cbs C.struct_gpgme_data_cbs
r io.Reader
w io.Writer
s io.Seeker
cbc uintptr
cbc uintptr // WARNING: Call runtime.KeepAlive(d) after ANY use of d.cbc in C (typically via d.dh)
}
func newData() *Data {
@ -154,12 +154,14 @@ func (d *Data) Close() error {
callbackDelete(d.cbc)
}
_, err := C.gpgme_data_release(d.dh)
runtime.KeepAlive(d)
d.dh = nil
return err
}
func (d *Data) Write(p []byte) (int, error) {
n, err := C.gpgme_data_write(d.dh, unsafe.Pointer(&p[0]), C.size_t(len(p)))
runtime.KeepAlive(d)
if err != nil {
return 0, err
}
@ -171,6 +173,7 @@ func (d *Data) Write(p []byte) (int, error) {
func (d *Data) Read(p []byte) (int, error) {
n, err := C.gpgme_data_read(d.dh, unsafe.Pointer(&p[0]), C.size_t(len(p)))
runtime.KeepAlive(d)
if err != nil {
return 0, err
}
@ -181,11 +184,14 @@ func (d *Data) Read(p []byte) (int, error) {
}
func (d *Data) Seek(offset int64, whence int) (int64, error) {
n, err := C.gpgme_data_seek(d.dh, C.off_t(offset), C.int(whence))
n, err := C.gogpgme_data_seek(d.dh, C.gpgme_off_t(offset), C.int(whence))
runtime.KeepAlive(d)
return int64(n), err
}
// Name returns the associated filename if any
func (d *Data) Name() string {
return C.GoString(C.gpgme_data_get_file_name(d.dh))
res := C.GoString(C.gpgme_data_get_file_name(d.dh))
runtime.KeepAlive(d)
return res
}

3
vendor/github.com/mtrmac/gpgme/go.mod generated vendored Normal file
View File

@ -0,0 +1,3 @@
module github.com/mtrmac/gpgme
go 1.11

View File

@ -8,6 +8,28 @@ void gogpgme_set_passphrase_cb(gpgme_ctx_t ctx, gpgme_passphrase_cb_t cb, uintpt
gpgme_set_passphrase_cb(ctx, cb, (void *)handle);
}
gpgme_off_t gogpgme_data_seek(gpgme_data_t dh, gpgme_off_t offset, int whence) {
return gpgme_data_seek(dh, offset, whence);
}
gpgme_error_t gogpgme_op_assuan_transact_ext(
gpgme_ctx_t ctx,
char* cmd,
uintptr_t data_h,
uintptr_t inquiry_h,
uintptr_t status_h,
gpgme_error_t *operr
){
return gpgme_op_assuan_transact_ext(
ctx,
cmd,
(gpgme_assuan_data_cb_t) gogpgme_assuan_data_callback, (void *)data_h,
(gpgme_assuan_inquire_cb_t) gogpgme_assuan_inquiry_callback, (void *)inquiry_h,
(gpgme_assuan_status_cb_t) gogpgme_assuan_status_callback, (void *)status_h,
operr
);
}
unsigned int key_revoked(gpgme_key_t k) {
return k->revoked;
}

View File

@ -12,6 +12,13 @@ extern off_t gogpgme_seekfunc(void *handle, off_t offset, int whence);
extern gpgme_error_t gogpgme_passfunc(void *hook, char *uid_hint, char *passphrase_info, int prev_was_bad, int fd);
extern gpgme_error_t gogpgme_data_new_from_cbs(gpgme_data_t *dh, gpgme_data_cbs_t cbs, uintptr_t handle);
extern void gogpgme_set_passphrase_cb(gpgme_ctx_t ctx, gpgme_passphrase_cb_t cb, uintptr_t handle);
extern gpgme_off_t gogpgme_data_seek(gpgme_data_t dh, gpgme_off_t offset, int whence);
extern gpgme_error_t gogpgme_op_assuan_transact_ext(gpgme_ctx_t ctx, char *cmd, uintptr_t data_h, uintptr_t inquiry_h , uintptr_t status_h, gpgme_error_t *operr);
extern gpgme_error_t gogpgme_assuan_data_callback(void *opaque, void* data, size_t datalen );
extern gpgme_error_t gogpgme_assuan_inquiry_callback(void *opaque, char* name, char* args);
extern gpgme_error_t gogpgme_assuan_status_callback(void *opaque, char* status, char* args);
extern unsigned int key_revoked(gpgme_key_t k);
extern unsigned int key_expired(gpgme_key_t k);

View File

@ -7,7 +7,6 @@ package gpgme
// #include <gpgme.h>
// #include "go_gpgme.h"
import "C"
import (
"fmt"
"io"
@ -48,7 +47,6 @@ const (
ProtocolAssuan Protocol = C.GPGME_PROTOCOL_ASSUAN
ProtocolG13 Protocol = C.GPGME_PROTOCOL_G13
ProtocolUIServer Protocol = C.GPGME_PROTOCOL_UISERVER
// ProtocolSpawn Protocol = C.GPGME_PROTOCOL_SPAWN // Unavailable in 1.4.3
ProtocolDefault Protocol = C.GPGME_PROTOCOL_DEFAULT
ProtocolUnknown Protocol = C.GPGME_PROTOCOL_UNKNOWN
)
@ -70,7 +68,6 @@ const (
EncryptNoEncryptTo EncryptFlag = C.GPGME_ENCRYPT_NO_ENCRYPT_TO
EncryptPrepare EncryptFlag = C.GPGME_ENCRYPT_PREPARE
EncryptExceptSign EncryptFlag = C.GPGME_ENCRYPT_EXPECT_SIGN
// EncryptNoCompress EncryptFlag = C.GPGME_ENCRYPT_NO_COMPRESS // Unavailable in 1.4.3
)
type HashAlgo int
@ -84,7 +81,6 @@ const (
KeyListModeExtern KeyListMode = C.GPGME_KEYLIST_MODE_EXTERN
KeyListModeSigs KeyListMode = C.GPGME_KEYLIST_MODE_SIGS
KeyListModeSigNotations KeyListMode = C.GPGME_KEYLIST_MODE_SIG_NOTATIONS
// KeyListModeWithSecret KeyListMode = C.GPGME_KEYLIST_MODE_WITH_SECRET // Unavailable in 1.4.3
KeyListModeEphemeral KeyListMode = C.GPGME_KEYLIST_MODE_EPHEMERAL
KeyListModeModeValidate KeyListMode = C.GPGME_KEYLIST_MODE_VALIDATE
)
@ -168,39 +164,60 @@ func EngineCheckVersion(p Protocol) error {
}
type EngineInfo struct {
info C.gpgme_engine_info_t
next *EngineInfo
protocol Protocol
fileName string
homeDir string
version string
requiredVersion string
}
func copyEngineInfo(info C.gpgme_engine_info_t) *EngineInfo {
res := &EngineInfo{
next: nil,
protocol: Protocol(info.protocol),
fileName: C.GoString(info.file_name),
homeDir: C.GoString(info.home_dir),
version: C.GoString(info.version),
requiredVersion: C.GoString(info.req_version),
}
if info.next != nil {
res.next = copyEngineInfo(info.next)
}
return res
}
func (e *EngineInfo) Next() *EngineInfo {
if e.info.next == nil {
return nil
}
return &EngineInfo{info: e.info.next}
return e.next
}
func (e *EngineInfo) Protocol() Protocol {
return Protocol(e.info.protocol)
return e.protocol
}
func (e *EngineInfo) FileName() string {
return C.GoString(e.info.file_name)
return e.fileName
}
func (e *EngineInfo) Version() string {
return C.GoString(e.info.version)
return e.version
}
func (e *EngineInfo) RequiredVersion() string {
return C.GoString(e.info.req_version)
return e.requiredVersion
}
func (e *EngineInfo) HomeDir() string {
return C.GoString(e.info.home_dir)
return e.homeDir
}
func GetEngineInfo() (*EngineInfo, error) {
info := &EngineInfo{}
return info, handleError(C.gpgme_get_engine_info(&info.info))
var cInfo C.gpgme_engine_info_t
err := handleError(C.gpgme_get_engine_info(&cInfo))
if err != nil {
return nil, err
}
return copyEngineInfo(cInfo), nil // It is up to the caller not to invalidate cInfo concurrently until this is done.
}
func SetEngineInfo(proto Protocol, fileName, homeDir string) error {
@ -261,9 +278,9 @@ type Context struct {
KeyError error
callback Callback
cbc uintptr
cbc uintptr // WARNING: Call runtime.KeepAlive(c) after ANY use of c.cbc in C (typically via c.ctx)
ctx C.gpgme_ctx_t
ctx C.gpgme_ctx_t // WARNING: Call runtime.KeepAlive(c) after ANY passing of c.ctx to C
}
func New() (*Context, error) {
@ -281,49 +298,68 @@ func (c *Context) Release() {
callbackDelete(c.cbc)
}
C.gpgme_release(c.ctx)
runtime.KeepAlive(c)
c.ctx = nil
}
func (c *Context) SetArmor(yes bool) {
C.gpgme_set_armor(c.ctx, cbool(yes))
runtime.KeepAlive(c)
}
func (c *Context) Armor() bool {
return C.gpgme_get_armor(c.ctx) != 0
res := C.gpgme_get_armor(c.ctx) != 0
runtime.KeepAlive(c)
return res
}
func (c *Context) SetTextMode(yes bool) {
C.gpgme_set_textmode(c.ctx, cbool(yes))
runtime.KeepAlive(c)
}
func (c *Context) TextMode() bool {
return C.gpgme_get_textmode(c.ctx) != 0
res := C.gpgme_get_textmode(c.ctx) != 0
runtime.KeepAlive(c)
return res
}
func (c *Context) SetProtocol(p Protocol) error {
return handleError(C.gpgme_set_protocol(c.ctx, C.gpgme_protocol_t(p)))
err := handleError(C.gpgme_set_protocol(c.ctx, C.gpgme_protocol_t(p)))
runtime.KeepAlive(c)
return err
}
func (c *Context) Protocol() Protocol {
return Protocol(C.gpgme_get_protocol(c.ctx))
res := Protocol(C.gpgme_get_protocol(c.ctx))
runtime.KeepAlive(c)
return res
}
func (c *Context) SetKeyListMode(m KeyListMode) error {
return handleError(C.gpgme_set_keylist_mode(c.ctx, C.gpgme_keylist_mode_t(m)))
err := handleError(C.gpgme_set_keylist_mode(c.ctx, C.gpgme_keylist_mode_t(m)))
runtime.KeepAlive(c)
return err
}
func (c *Context) KeyListMode() KeyListMode {
return KeyListMode(C.gpgme_get_keylist_mode(c.ctx))
res := KeyListMode(C.gpgme_get_keylist_mode(c.ctx))
runtime.KeepAlive(c)
return res
}
// Unavailable in 1.3.2:
// func (c *Context) SetPinEntryMode(m PinEntryMode) error {
// return handleError(C.gpgme_set_pinentry_mode(c.ctx, C.gpgme_pinentry_mode_t(m)))
// err := handleError(C.gpgme_set_pinentry_mode(c.ctx, C.gpgme_pinentry_mode_t(m)))
// runtime.KeepAlive(c)
// return err
// }
// Unavailable in 1.3.2:
// func (c *Context) PinEntryMode() PinEntryMode {
// return PinEntryMode(C.gpgme_get_pinentry_mode(c.ctx))
// res := PinEntryMode(C.gpgme_get_pinentry_mode(c.ctx))
// runtime.KeepAlive(c)
// return res
// }
func (c *Context) SetCallback(callback Callback) error {
@ -340,11 +376,17 @@ func (c *Context) SetCallback(callback Callback) error {
c.cbc = 0
_, err = C.gogpgme_set_passphrase_cb(c.ctx, nil, 0)
}
runtime.KeepAlive(c)
return err
}
func (c *Context) EngineInfo() *EngineInfo {
return &EngineInfo{info: C.gpgme_ctx_get_engine_info(c.ctx)}
cInfo := C.gpgme_ctx_get_engine_info(c.ctx)
runtime.KeepAlive(c)
// NOTE: c must be live as long as we are accessing cInfo.
res := copyEngineInfo(cInfo)
runtime.KeepAlive(c) // for accesses to cInfo
return res
}
func (c *Context) SetEngineInfo(proto Protocol, fileName, homeDir string) error {
@ -357,19 +399,23 @@ func (c *Context) SetEngineInfo(proto Protocol, fileName, homeDir string) error
chome = C.CString(homeDir)
defer C.free(unsafe.Pointer(chome))
}
return handleError(C.gpgme_ctx_set_engine_info(c.ctx, C.gpgme_protocol_t(proto), cfn, chome))
err := handleError(C.gpgme_ctx_set_engine_info(c.ctx, C.gpgme_protocol_t(proto), cfn, chome))
runtime.KeepAlive(c)
return err
}
func (c *Context) KeyListStart(pattern string, secretOnly bool) error {
cpattern := C.CString(pattern)
defer C.free(unsafe.Pointer(cpattern))
err := C.gpgme_op_keylist_start(c.ctx, cpattern, cbool(secretOnly))
return handleError(err)
err := handleError(C.gpgme_op_keylist_start(c.ctx, cpattern, cbool(secretOnly)))
runtime.KeepAlive(c)
return err
}
func (c *Context) KeyListNext() bool {
c.Key = newKey()
err := handleError(C.gpgme_op_keylist_next(c.ctx, &c.Key.k))
runtime.KeepAlive(c) // implies runtime.KeepAlive(c.Key)
if err != nil {
if e, ok := err.(Error); ok && e.Code() == ErrorEOF {
c.KeyError = nil
@ -383,7 +429,9 @@ func (c *Context) KeyListNext() bool {
}
func (c *Context) KeyListEnd() error {
return handleError(C.gpgme_op_keylist_end(c.ctx))
err := handleError(C.gpgme_op_keylist_end(c.ctx))
runtime.KeepAlive(c)
return err
}
func (c *Context) GetKey(fingerprint string, secret bool) (*Key, error) {
@ -391,7 +439,11 @@ func (c *Context) GetKey(fingerprint string, secret bool) (*Key, error) {
cfpr := C.CString(fingerprint)
defer C.free(unsafe.Pointer(cfpr))
err := handleError(C.gpgme_get_key(c.ctx, cfpr, &key.k, cbool(secret)))
if e, ok := err.(Error); key.k == nil && ok && e.Code() == ErrorEOF {
runtime.KeepAlive(c)
runtime.KeepAlive(key)
keyKIsNil := key.k == nil
runtime.KeepAlive(key)
if e, ok := err.(Error); keyKIsNil && ok && e.Code() == ErrorEOF {
return nil, fmt.Errorf("key %q not found", fingerprint)
}
if err != nil {
@ -401,11 +453,19 @@ func (c *Context) GetKey(fingerprint string, secret bool) (*Key, error) {
}
func (c *Context) Decrypt(ciphertext, plaintext *Data) error {
return handleError(C.gpgme_op_decrypt(c.ctx, ciphertext.dh, plaintext.dh))
err := handleError(C.gpgme_op_decrypt(c.ctx, ciphertext.dh, plaintext.dh))
runtime.KeepAlive(c)
runtime.KeepAlive(ciphertext)
runtime.KeepAlive(plaintext)
return err
}
func (c *Context) DecryptVerify(ciphertext, plaintext *Data) error {
return handleError(C.gpgme_op_decrypt_verify(c.ctx, ciphertext.dh, plaintext.dh))
err := handleError(C.gpgme_op_decrypt_verify(c.ctx, ciphertext.dh, plaintext.dh))
runtime.KeepAlive(c)
runtime.KeepAlive(ciphertext)
runtime.KeepAlive(plaintext)
return err
}
type Signature struct {
@ -432,10 +492,20 @@ func (c *Context) Verify(sig, signedText, plain *Data) (string, []Signature, err
plainPtr = plain.dh
}
err := handleError(C.gpgme_op_verify(c.ctx, sig.dh, signedTextPtr, plainPtr))
runtime.KeepAlive(c)
runtime.KeepAlive(sig)
if signedText != nil {
runtime.KeepAlive(signedText)
}
if plain != nil {
runtime.KeepAlive(plain)
}
if err != nil {
return "", nil, err
}
res := C.gpgme_op_verify_result(c.ctx)
runtime.KeepAlive(c)
// NOTE: c must be live as long as we are accessing res.
sigs := []Signature{}
for s := res.signatures; s != nil; s = s.next {
sig := Signature{
@ -455,7 +525,9 @@ func (c *Context) Verify(sig, signedText, plain *Data) (string, []Signature, err
}
sigs = append(sigs, sig)
}
return C.GoString(res.file_name), sigs, nil
fileName := C.GoString(res.file_name)
runtime.KeepAlive(c) // for all accesses to res above
return fileName, sigs, nil
}
func (c *Context) Encrypt(recipients []*Key, flags EncryptFlag, plaintext, ciphertext *Data) error {
@ -467,18 +539,116 @@ func (c *Context) Encrypt(recipients []*Key, flags EncryptFlag, plaintext, ciphe
*ptr = recipients[i].k
}
err := C.gpgme_op_encrypt(c.ctx, (*C.gpgme_key_t)(recp), C.gpgme_encrypt_flags_t(flags), plaintext.dh, ciphertext.dh)
runtime.KeepAlive(c)
runtime.KeepAlive(recipients)
runtime.KeepAlive(plaintext)
runtime.KeepAlive(ciphertext)
return handleError(err)
}
func (c *Context) Sign(signers []*Key, plain, sig *Data, mode SigMode) error {
C.gpgme_signers_clear(c.ctx)
runtime.KeepAlive(c)
for _, k := range signers {
if err := handleError(C.gpgme_signers_add(c.ctx, k.k)); err != nil {
err := handleError(C.gpgme_signers_add(c.ctx, k.k))
runtime.KeepAlive(c)
runtime.KeepAlive(k)
if err != nil {
C.gpgme_signers_clear(c.ctx)
runtime.KeepAlive(c)
return err
}
}
return handleError(C.gpgme_op_sign(c.ctx, plain.dh, sig.dh, C.gpgme_sig_mode_t(mode)))
err := handleError(C.gpgme_op_sign(c.ctx, plain.dh, sig.dh, C.gpgme_sig_mode_t(mode)))
runtime.KeepAlive(c)
runtime.KeepAlive(plain)
runtime.KeepAlive(sig)
return err
}
type AssuanDataCallback func(data []byte) error
type AssuanInquireCallback func(name, args string) error
type AssuanStatusCallback func(status, args string) error
// AssuanSend sends a raw Assuan command to gpg-agent
func (c *Context) AssuanSend(
cmd string,
data AssuanDataCallback,
inquiry AssuanInquireCallback,
status AssuanStatusCallback,
) error {
var operr C.gpgme_error_t
dataPtr := callbackAdd(&data)
inquiryPtr := callbackAdd(&inquiry)
statusPtr := callbackAdd(&status)
cmdCStr := C.CString(cmd)
defer C.free(unsafe.Pointer(cmdCStr))
err := C.gogpgme_op_assuan_transact_ext(
c.ctx,
cmdCStr,
C.uintptr_t(dataPtr),
C.uintptr_t(inquiryPtr),
C.uintptr_t(statusPtr),
&operr,
)
runtime.KeepAlive(c)
if handleError(operr) != nil {
return handleError(operr)
}
return handleError(err)
}
//export gogpgme_assuan_data_callback
func gogpgme_assuan_data_callback(handle unsafe.Pointer, data unsafe.Pointer, datalen C.size_t) C.gpgme_error_t {
c := callbackLookup(uintptr(handle)).(*AssuanDataCallback)
if *c == nil {
return 0
}
(*c)(C.GoBytes(data, C.int(datalen)))
return 0
}
//export gogpgme_assuan_inquiry_callback
func gogpgme_assuan_inquiry_callback(handle unsafe.Pointer, cName *C.char, cArgs *C.char) C.gpgme_error_t {
name := C.GoString(cName)
args := C.GoString(cArgs)
c := callbackLookup(uintptr(handle)).(*AssuanInquireCallback)
if *c == nil {
return 0
}
(*c)(name, args)
return 0
}
//export gogpgme_assuan_status_callback
func gogpgme_assuan_status_callback(handle unsafe.Pointer, cStatus *C.char, cArgs *C.char) C.gpgme_error_t {
status := C.GoString(cStatus)
args := C.GoString(cArgs)
c := callbackLookup(uintptr(handle)).(*AssuanStatusCallback)
if *c == nil {
return 0
}
(*c)(status, args)
return 0
}
// ExportModeFlags defines how keys are exported from Export
type ExportModeFlags uint
const (
ExportModeExtern ExportModeFlags = C.GPGME_EXPORT_MODE_EXTERN
ExportModeMinimal ExportModeFlags = C.GPGME_EXPORT_MODE_MINIMAL
)
func (c *Context) Export(pattern string, mode ExportModeFlags, data *Data) error {
pat := C.CString(pattern)
defer C.free(unsafe.Pointer(pat))
err := handleError(C.gpgme_op_export(c.ctx, pat, C.gpgme_export_mode_t(mode), data.dh))
runtime.KeepAlive(c)
runtime.KeepAlive(data)
return err
}
// ImportStatusFlags describes the type of ImportStatus.Status. The C API in gpgme.h simply uses "unsigned".
@ -517,10 +687,14 @@ type ImportResult struct {
func (c *Context) Import(keyData *Data) (*ImportResult, error) {
err := handleError(C.gpgme_op_import(c.ctx, keyData.dh))
runtime.KeepAlive(c)
runtime.KeepAlive(keyData)
if err != nil {
return nil, err
}
res := C.gpgme_op_import_result(c.ctx)
runtime.KeepAlive(c)
// NOTE: c must be live as long as we are accessing res.
imports := []ImportStatus{}
for s := res.imports; s != nil; s = s.next {
imports = append(imports, ImportStatus{
@ -529,7 +703,7 @@ func (c *Context) Import(keyData *Data) (*ImportResult, error) {
Status: ImportStatusFlags(s.status),
})
}
return &ImportResult{
importResult := &ImportResult{
Considered: int(res.considered),
NoUserID: int(res.no_user_id),
Imported: int(res.imported),
@ -544,11 +718,13 @@ func (c *Context) Import(keyData *Data) (*ImportResult, error) {
SecretUnchanged: int(res.secret_unchanged),
NotImported: int(res.not_imported),
Imports: imports,
}, nil
}
runtime.KeepAlive(c) // for all accesses to res above
return importResult, nil
}
type Key struct {
k C.gpgme_key_t
k C.gpgme_key_t // WARNING: Call Runtime.KeepAlive(k) after ANY passing of k.k to C
}
func newKey() *Key {
@ -559,85 +735,122 @@ func newKey() *Key {
func (k *Key) Release() {
C.gpgme_key_release(k.k)
runtime.KeepAlive(k)
k.k = nil
}
func (k *Key) Revoked() bool {
return C.key_revoked(k.k) != 0
res := C.key_revoked(k.k) != 0
runtime.KeepAlive(k)
return res
}
func (k *Key) Expired() bool {
return C.key_expired(k.k) != 0
res := C.key_expired(k.k) != 0
runtime.KeepAlive(k)
return res
}
func (k *Key) Disabled() bool {
return C.key_disabled(k.k) != 0
res := C.key_disabled(k.k) != 0
runtime.KeepAlive(k)
return res
}
func (k *Key) Invalid() bool {
return C.key_invalid(k.k) != 0
res := C.key_invalid(k.k) != 0
runtime.KeepAlive(k)
return res
}
func (k *Key) CanEncrypt() bool {
return C.key_can_encrypt(k.k) != 0
res := C.key_can_encrypt(k.k) != 0
runtime.KeepAlive(k)
return res
}
func (k *Key) CanSign() bool {
return C.key_can_sign(k.k) != 0
res := C.key_can_sign(k.k) != 0
runtime.KeepAlive(k)
return res
}
func (k *Key) CanCertify() bool {
return C.key_can_certify(k.k) != 0
res := C.key_can_certify(k.k) != 0
runtime.KeepAlive(k)
return res
}
func (k *Key) Secret() bool {
return C.key_secret(k.k) != 0
res := C.key_secret(k.k) != 0
runtime.KeepAlive(k)
return res
}
func (k *Key) CanAuthenticate() bool {
return C.key_can_authenticate(k.k) != 0
res := C.key_can_authenticate(k.k) != 0
runtime.KeepAlive(k)
return res
}
func (k *Key) IsQualified() bool {
return C.key_is_qualified(k.k) != 0
res := C.key_is_qualified(k.k) != 0
runtime.KeepAlive(k)
return res
}
func (k *Key) Protocol() Protocol {
return Protocol(k.k.protocol)
res := Protocol(k.k.protocol)
runtime.KeepAlive(k)
return res
}
func (k *Key) IssuerSerial() string {
return C.GoString(k.k.issuer_serial)
res := C.GoString(k.k.issuer_serial)
runtime.KeepAlive(k)
return res
}
func (k *Key) IssuerName() string {
return C.GoString(k.k.issuer_name)
res := C.GoString(k.k.issuer_name)
runtime.KeepAlive(k)
return res
}
func (k *Key) ChainID() string {
return C.GoString(k.k.chain_id)
res := C.GoString(k.k.chain_id)
runtime.KeepAlive(k)
return res
}
func (k *Key) OwnerTrust() Validity {
return Validity(k.k.owner_trust)
res := Validity(k.k.owner_trust)
runtime.KeepAlive(k)
return res
}
func (k *Key) SubKeys() *SubKey {
if k.k.subkeys == nil {
subKeys := k.k.subkeys
runtime.KeepAlive(k)
if subKeys == nil {
return nil
}
return &SubKey{k: k.k.subkeys, parent: k}
return &SubKey{k: subKeys, parent: k} // The parent: k reference ensures subKeys remains valid
}
func (k *Key) UserIDs() *UserID {
if k.k.uids == nil {
uids := k.k.uids
runtime.KeepAlive(k)
if uids == nil {
return nil
}
return &UserID{u: k.k.uids, parent: k}
return &UserID{u: uids, parent: k} // The parent: k reference ensures uids remains valid
}
func (k *Key) KeyListMode() KeyListMode {
return KeyListMode(k.k.keylist_mode)
res := KeyListMode(k.k.keylist_mode)
runtime.KeepAlive(k)
return res
}
type SubKey struct {
@ -737,12 +950,3 @@ func (u *UserID) Comment() string {
func (u *UserID) Email() string {
return C.GoString(u.u.email)
}
// This is somewhat of a horrible hack. We need to unset GPG_AGENT_INFO so that gpgme does not pass --use-agent to GPG.
// os.Unsetenv should be enough, but that only calls the underlying C library (which gpgme uses) if cgo is involved
// - and cgo can't be used in tests. So, provide this helper for test initialization.
func unsetenvGPGAgentInfo() {
v := C.CString("GPG_AGENT_INFO")
defer C.free(unsafe.Pointer(v))
C.unsetenv(v)
}

18
vendor/github.com/mtrmac/gpgme/unset_agent_info.go generated vendored Normal file
View File

@ -0,0 +1,18 @@
// +build !windows
package gpgme
// #include <stdlib.h>
import "C"
import (
"unsafe"
)
// This is somewhat of a horrible hack. We need to unset GPG_AGENT_INFO so that gpgme does not pass --use-agent to GPG.
// os.Unsetenv should be enough, but that only calls the underlying C library (which gpgme uses) if cgo is involved
// - and cgo can't be used in tests. So, provide this helper for test initialization.
func unsetenvGPGAgentInfo() {
v := C.CString("GPG_AGENT_INFO")
defer C.free(unsafe.Pointer(v))
C.unsetenv(v)
}

View File

@ -0,0 +1,14 @@
package gpgme
// #include <stdlib.h>
import "C"
import (
"unsafe"
)
// unsetenv is not available in mingw
func unsetenvGPGAgentInfo() {
v := C.CString("GPG_AGENT_INFO=")
defer C.free(unsafe.Pointer(v))
C.putenv(v)
}

View File

@ -10,11 +10,10 @@ import (
// BarOption is a function option which changes the default behavior of a bar.
type BarOption func(*bState)
func (s *bState) addDecorators(dest *[]decor.Decorator, decorators ...decor.Decorator) {
type mergeWrapper interface {
MergeUnwrap() []decor.Decorator
}
func (s *bState) addDecorators(dest *[]decor.Decorator, decorators ...decor.Decorator) {
for _, decorator := range decorators {
if mw, ok := decorator.(mergeWrapper); ok {
*dest = append(*dest, mw.MergeUnwrap()...)

View File

@ -4,6 +4,8 @@ import (
"fmt"
"time"
"unicode/utf8"
"github.com/acarl005/stripansi"
)
const (
@ -120,22 +122,26 @@ type WC struct {
W int
C int
dynFormat string
staticFormat string
wsync chan int
}
// FormatMsg formats final message according to WC.W and WC.C.
// Should be called by any Decorator implementation.
func (wc *WC) FormatMsg(msg string) string {
var format string
runeCount := utf8.RuneCountInString(stripansi.Strip(msg))
ansiCount := utf8.RuneCountInString(msg) - runeCount
if (wc.C & DSyncWidth) != 0 {
wc.wsync <- utf8.RuneCountInString(msg)
max := <-wc.wsync
if (wc.C & DextraSpace) != 0 {
max++
runeCount++
}
return fmt.Sprintf(fmt.Sprintf(wc.dynFormat, max), msg)
wc.wsync <- runeCount
max := <-wc.wsync
format = fmt.Sprintf(wc.dynFormat, ansiCount+max)
} else {
format = fmt.Sprintf(wc.dynFormat, ansiCount+wc.W)
}
return fmt.Sprintf(wc.staticFormat, msg)
return fmt.Sprintf(format, msg)
}
// Init initializes width related config.
@ -145,7 +151,6 @@ func (wc *WC) Init() WC {
wc.dynFormat += "-"
}
wc.dynFormat += "%ds"
wc.staticFormat = fmt.Sprintf(wc.dynFormat, wc.W)
if (wc.C & DSyncWidth) != 0 {
// it's deliberate choice to override wsync on each Init() call,
// this way globals like WCSyncSpace can be reused

View File

@ -2,6 +2,7 @@ package decor
import (
"fmt"
"strings"
"unicode/utf8"
)
@ -28,10 +29,7 @@ func Merge(decorator Decorator, placeholders ...WC) Decorator {
if (wc.C & DSyncWidth) == 0 {
return decorator
}
md.placeHolders[i] = &placeHolderDecorator{
WC: wc.Init(),
wch: make(chan int),
}
md.placeHolders[i] = &placeHolderDecorator{wc.Init()}
}
return md
}
@ -69,29 +67,40 @@ func (d *mergeDecorator) Base() Decorator {
func (d *mergeDecorator) Decor(st *Statistics) string {
msg := d.Decorator.Decor(st)
msgLen := utf8.RuneCountInString(msg)
var space int
for _, ph := range d.placeHolders {
space += <-ph.wch
}
d.wc.wsync <- msgLen - space
max := <-d.wc.wsync
if (d.wc.C & DextraSpace) != 0 {
max++
msgLen++
}
return fmt.Sprintf(fmt.Sprintf(d.wc.dynFormat, max+space), msg)
var total int
max := utf8.RuneCountInString(d.placeHolders[0].FormatMsg(""))
total += max
pw := (msgLen - max) / len(d.placeHolders)
rem := (msgLen - max) % len(d.placeHolders)
var diff int
for i := 1; i < len(d.placeHolders); i++ {
ph := d.placeHolders[i]
width := pw - diff
if (ph.WC.C & DextraSpace) != 0 {
width--
if width < 0 {
width = 0
}
}
max = utf8.RuneCountInString(ph.FormatMsg(strings.Repeat(" ", width)))
total += max
diff = max - pw
}
d.wc.wsync <- pw + rem
max = <-d.wc.wsync
return fmt.Sprintf(fmt.Sprintf(d.wc.dynFormat, max+total), msg)
}
type placeHolderDecorator struct {
WC
wch chan int
}
func (d *placeHolderDecorator) Decor(st *Statistics) string {
go func() {
d.wch <- utf8.RuneCountInString(d.FormatMsg(""))
}()
func (d *placeHolderDecorator) Decor(_ *Statistics) string {
return ""
}

View File

@ -2,6 +2,7 @@ module github.com/vbauerster/mpb/v4
require (
github.com/VividCortex/ewma v1.1.1
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d
golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708
golang.org/x/sys v0.0.0-20191113165036-4c7a9d0fe056 // indirect
)

View File

@ -1,5 +1,7 @@
github.com/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdcM=
github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA=
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8=
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708 h1:pXVtWnwHkrWD9ru3sDxY/qFK/bfc0egRovX91EjWjf4=
golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=

View File

@ -18,9 +18,7 @@ func (prox *proxyReader) Read(p []byte) (n int, err error) {
prox.iT = time.Now()
}
if err == io.EOF {
go func() {
prox.bar.SetTotal(0, true)
}()
go prox.bar.SetTotal(0, true)
}
return
}
@ -37,9 +35,7 @@ func (prox *proxyWriterTo) WriteTo(w io.Writer) (n int64, err error) {
prox.iT = time.Now()
}
if err == io.EOF {
go func() {
prox.bar.SetTotal(0, true)
}()
go prox.bar.SetTotal(0, true)
}
return
}

9
vendor/modules.txt vendored
View File

@ -29,6 +29,8 @@ github.com/Microsoft/hcsshim/internal/wclayer
github.com/Microsoft/hcsshim/osversion
# github.com/VividCortex/ewma v1.1.1
github.com/VividCortex/ewma
# github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d
github.com/acarl005/stripansi
# github.com/beorn7/perks v1.0.1
github.com/beorn7/perks/quantile
# github.com/blang/semver v3.5.1+incompatible
@ -80,7 +82,7 @@ github.com/containers/common/pkg/cgroups
github.com/containers/common/pkg/unshare
# github.com/containers/conmon v2.0.10+incompatible
github.com/containers/conmon/runner/config
# github.com/containers/image/v5 v5.1.0
# github.com/containers/image/v5 v5.2.0
github.com/containers/image/v5/copy
github.com/containers/image/v5/directory
github.com/containers/image/v5/directory/explicitfilepath
@ -91,6 +93,7 @@ github.com/containers/image/v5/docker/policyconfiguration
github.com/containers/image/v5/docker/reference
github.com/containers/image/v5/docker/tarfile
github.com/containers/image/v5/image
github.com/containers/image/v5/internal/iolimits
github.com/containers/image/v5/internal/pkg/keyctl
github.com/containers/image/v5/internal/tmpdir
github.com/containers/image/v5/manifest
@ -337,7 +340,7 @@ github.com/modern-go/reflect2
github.com/morikuni/aec
# github.com/mrunalp/fileutils v0.0.0-20171103030105-7d4729fb3618
github.com/mrunalp/fileutils
# github.com/mtrmac/gpgme v0.0.0-20170102180018-b2432428689c
# github.com/mtrmac/gpgme v0.1.1
github.com/mtrmac/gpgme
# github.com/onsi/ginkgo v1.11.0
github.com/onsi/ginkgo
@ -510,7 +513,7 @@ github.com/varlink/go/varlink/idl
github.com/vbatts/tar-split/archive/tar
github.com/vbatts/tar-split/tar/asm
github.com/vbatts/tar-split/tar/storage
# github.com/vbauerster/mpb/v4 v4.11.1
# github.com/vbauerster/mpb/v4 v4.11.2
github.com/vbauerster/mpb/v4
github.com/vbauerster/mpb/v4/cwriter
github.com/vbauerster/mpb/v4/decor