From 79e21601b0f26989f17337ee867ef61582b537fd Mon Sep 17 00:00:00 2001 From: poornas Date: Wed, 12 Aug 2020 17:32:24 -0700 Subject: [PATCH] fix: web handlers to enforce replication (#10249) This PR also preserves source ETag for replication --- cmd/bucket-replication.go | 47 +++++++++++++++++++++++++++++++++++---- cmd/bucket-targets.go | 7 ++++-- cmd/erasure-multipart.go | 3 +++ cmd/erasure-object.go | 5 +++-- cmd/http/headers.go | 3 +++ cmd/object-api-options.go | 8 ++++++- cmd/object-handlers.go | 8 ++++--- cmd/web-handlers.go | 29 +++++++++++++++++++++++- go.mod | 9 ++++---- go.sum | 25 +++++++-------------- 10 files changed, 109 insertions(+), 35 deletions(-) diff --git a/cmd/bucket-replication.go b/cmd/bucket-replication.go index c5653195b..189daa209 100644 --- a/cmd/bucket-replication.go +++ b/cmd/bucket-replication.go @@ -19,6 +19,7 @@ package cmd import ( "context" "net/http" + "strings" "time" miniogo "github.com/minio/minio-go/v7" @@ -75,8 +76,23 @@ func validateReplicationDestination(ctx context.Context, bucket string, rCfg *re return false, BucketRemoteTargetNotFound{Bucket: bucket} } +func mustReplicateWeb(ctx context.Context, r *http.Request, bucket, object string, meta map[string]string, replStatus string, permErr APIErrorCode) bool { + if permErr != ErrNone { + return false + } + return mustReplicater(ctx, r, bucket, object, meta, replStatus) +} + // mustReplicate returns true if object meets replication criteria. func mustReplicate(ctx context.Context, r *http.Request, bucket, object string, meta map[string]string, replStatus string) bool { + if s3Err := isPutActionAllowed(getRequestAuthType(r), bucket, object, r, iampolicy.GetReplicationConfigurationAction); s3Err != ErrNone { + return false + } + return mustReplicater(ctx, r, bucket, object, meta, replStatus) +} + +// mustReplicater returns true if object meets replication criteria. +func mustReplicater(ctx context.Context, r *http.Request, bucket, object string, meta map[string]string, replStatus string) bool { if globalIsGateway { return false } @@ -86,9 +102,6 @@ func mustReplicate(ctx context.Context, r *http.Request, bucket, object string, if replication.StatusType(replStatus) == replication.Replica { return false } - if s3Err := isPutActionAllowed(getRequestAuthType(r), bucket, object, r, iampolicy.GetReplicationConfigurationAction); s3Err != ErrNone { - return false - } cfg, err := getReplicationConfig(ctx, bucket) if err != nil { return false @@ -110,9 +123,11 @@ func putReplicationOpts(dest replication.Destination, objInfo ObjectInfo) (putOp if k == xhttp.AmzBucketReplicationStatus { continue } + if strings.HasPrefix(strings.ToLower(k), ReservedMetadataPrefixLower) { + continue + } meta[k] = v } - tag, err := tags.ParseObjectTags(objInfo.UserTags) if err != nil { return @@ -130,6 +145,7 @@ func putReplicationOpts(dest replication.Destination, objInfo ObjectInfo) (putOp ReplicationVersionID: objInfo.VersionID, ReplicationStatus: miniogo.ReplicationStatusReplica, ReplicationMTime: objInfo.ModTime, + ReplicationETag: objInfo.ETag, } if mode, ok := objInfo.UserDefined[xhttp.AmzObjectLockMode]; ok { rmode := miniogo.RetentionMode(mode) @@ -219,3 +235,26 @@ func replicateObject(ctx context.Context, bucket, object, versionID string, obje logger.LogIf(ctx, err) } } + +// filterReplicationStatusMetadata filters replication status metadata for COPY +func filterReplicationStatusMetadata(metadata map[string]string) map[string]string { + // Copy on write + dst := metadata + var copied bool + delKey := func(key string) { + if _, ok := metadata[key]; !ok { + return + } + if !copied { + dst = make(map[string]string, len(metadata)) + for k, v := range metadata { + dst[k] = v + } + copied = true + } + delete(dst, key) + } + + delKey(xhttp.AmzBucketReplicationStatus) + return dst +} diff --git a/cmd/bucket-targets.go b/cmd/bucket-targets.go index 24059ff2f..c42b7818b 100644 --- a/cmd/bucket-targets.go +++ b/cmd/bucket-targets.go @@ -90,11 +90,14 @@ func (sys *BucketTargetSys) SetTarget(ctx context.Context, bucket string, tgt *m return BucketReplicationSourceNotVersioned{Bucket: bucket} } vcfg, err := clnt.GetBucketVersioning(ctx, tgt.TargetBucket) - if err != nil || vcfg.Status != string(versioning.Enabled) { + if err != nil { if isErrBucketNotFound(err) { return BucketRemoteTargetNotFound{Bucket: tgt.TargetBucket} } - return BucketReplicationTargetNotVersioned{Bucket: tgt.TargetBucket} + if vcfg.Status != string(versioning.Enabled) { + return BucketReplicationTargetNotVersioned{Bucket: tgt.TargetBucket} + } + return err } } diff --git a/cmd/erasure-multipart.go b/cmd/erasure-multipart.go index 2f2711ef2..d06bed106 100644 --- a/cmd/erasure-multipart.go +++ b/cmd/erasure-multipart.go @@ -702,6 +702,9 @@ func (er erasureObjects) CompleteMultipartUpload(ctx context.Context, bucket str // Save successfully calculated md5sum. fi.Metadata["etag"] = s3MD5 + if opts.UserDefined["etag"] != "" { // preserve ETag if set + fi.Metadata["etag"] = opts.UserDefined["etag"] + } // Save the consolidated actual size. fi.Metadata[ReservedMetadataPrefix+"actual-size"] = strconv.FormatInt(objectActualSize, 10) diff --git a/cmd/erasure-object.go b/cmd/erasure-object.go index 796a20fe2..b4b73ae79 100644 --- a/cmd/erasure-object.go +++ b/cmd/erasure-object.go @@ -710,8 +710,9 @@ func (er erasureObjects) putObject(ctx context.Context, bucket string, object st Hash: bitrotWriterSum(w), }) } - - opts.UserDefined["etag"] = r.MD5CurrentHexString() + if opts.UserDefined["etag"] == "" { + opts.UserDefined["etag"] = r.MD5CurrentHexString() + } // Guess content-type from the extension if possible. if opts.UserDefined["content-type"] == "" { diff --git a/cmd/http/headers.go b/cmd/http/headers.go index dbed10e5c..a9806a07b 100644 --- a/cmd/http/headers.go +++ b/cmd/http/headers.go @@ -123,6 +123,9 @@ const ( // Header indicates if the mtime should be preserved by client MinIOSourceMTime = "x-minio-source-mtime" + + // Header indicates if the etag should be preserved by client + MinIOSourceETag = "x-minio-source-etag" ) // Common http query params S3 API diff --git a/cmd/object-api-options.go b/cmd/object-api-options.go index a34633b4f..61912ccd1 100644 --- a/cmd/object-api-options.go +++ b/cmd/object-api-options.go @@ -164,7 +164,13 @@ func putOpts(ctx context.Context, r *http.Request, bucket, object string, metada } else { opts.MTime = UTCNow() } - + etag := strings.TrimSpace(r.Header.Get(xhttp.MinIOSourceETag)) + if etag != "" { + if metadata == nil { + metadata = make(map[string]string) + } + metadata["etag"] = etag + } // In the case of multipart custom format, the metadata needs to be checked in addition to header to see if it // is SSE-S3 encrypted, primarily because S3 protocol does not require SSE-S3 headers in PutObjectPart calls if GlobalGatewaySSE.SSES3() && (crypto.S3.IsRequested(r.Header) || crypto.S3.IsEncrypted(metadata)) { diff --git a/cmd/object-handlers.go b/cmd/object-handlers.go index 2fbf8430d..319348849 100644 --- a/cmd/object-handlers.go +++ b/cmd/object-handlers.go @@ -1155,6 +1155,8 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re if objTags != "" { srcInfo.UserDefined[xhttp.AmzObjectTagging] = objTags } + srcInfo.UserDefined = filterReplicationStatusMetadata(srcInfo.UserDefined) + srcInfo.UserDefined = objectlock.FilterObjectLockMetadata(srcInfo.UserDefined, true, true) retPerms := isPutActionAllowed(getRequestAuthType(r), dstBucket, dstObject, r, iampolicy.PutObjectRetentionAction) holdPerms := isPutActionAllowed(getRequestAuthType(r), dstBucket, dstObject, r, iampolicy.PutObjectLegalHoldAction) @@ -1259,7 +1261,7 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re response := generateCopyObjectResponse(objInfo.ETag, objInfo.ModTime) encodedSuccessResponse := encodeResponse(response) if mustReplicate(ctx, r, dstBucket, dstObject, objInfo.UserDefined, objInfo.ReplicationStatus.String()) { - defer replicateObject(ctx, dstBucket, dstObject, objInfo.VersionID, objectAPI, &eventArgs{ + defer replicateObject(context.Background(), dstBucket, dstObject, objInfo.VersionID, objectAPI, &eventArgs{ EventName: event.ObjectCreatedCopy, BucketName: dstBucket, Object: objInfo, @@ -1575,7 +1577,7 @@ func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Req } } if mustReplicate(ctx, r, bucket, object, metadata, "") { - defer replicateObject(ctx, bucket, object, objInfo.VersionID, objectAPI, &eventArgs{ + defer replicateObject(context.Background(), bucket, object, objInfo.VersionID, objectAPI, &eventArgs{ EventName: event.ObjectCreatedPut, BucketName: bucket, Object: objInfo, @@ -2650,7 +2652,7 @@ func (api objectAPIHandlers) CompleteMultipartUploadHandler(w http.ResponseWrite setPutObjHeaders(w, objInfo, false) if mustReplicate(ctx, r, bucket, object, objInfo.UserDefined, objInfo.ReplicationStatus.String()) { - defer replicateObject(ctx, bucket, object, objInfo.VersionID, objectAPI, &eventArgs{ + defer replicateObject(context.Background(), bucket, object, objInfo.VersionID, objectAPI, &eventArgs{ EventName: event.ObjectCreatedCompleteMultipartUpload, BucketName: bucket, Object: objInfo, diff --git a/cmd/web-handlers.go b/cmd/web-handlers.go index bb78584a2..994da4304 100644 --- a/cmd/web-handlers.go +++ b/cmd/web-handlers.go @@ -48,6 +48,7 @@ import ( "github.com/minio/minio/pkg/auth" objectlock "github.com/minio/minio/pkg/bucket/object/lock" "github.com/minio/minio/pkg/bucket/policy" + "github.com/minio/minio/pkg/bucket/replication" "github.com/minio/minio/pkg/event" "github.com/minio/minio/pkg/handlers" "github.com/minio/minio/pkg/hash" @@ -961,6 +962,7 @@ func (web *webAPIHandlers) Upload(w http.ResponseWriter, r *http.Request) { retPerms := ErrAccessDenied holdPerms := ErrAccessDenied + replPerms := ErrAccessDenied if authErr != nil { if authErr == errNoAuthToken { // Check if anonymous (non-owner) has access to upload objects. @@ -1016,6 +1018,17 @@ func (web *webAPIHandlers) Upload(w http.ResponseWriter, r *http.Request) { }) { holdPerms = ErrNone } + if globalIAMSys.IsAllowed(iampolicy.Args{ + AccountName: claims.AccessKey, + Action: iampolicy.GetReplicationConfigurationAction, + BucketName: bucket, + ConditionValues: getConditionValues(r, "", claims.AccessKey, claims.Map()), + IsOwner: owner, + ObjectName: object, + Claims: claims.Map(), + }) { + replPerms = ErrNone + } } // Check if bucket is a reserved bucket name or invalid. @@ -1082,6 +1095,10 @@ func (web *webAPIHandlers) Upload(w http.ResponseWriter, r *http.Request) { } } + mustReplicate := mustReplicateWeb(ctx, r, bucket, object, metadata, "", replPerms) + if mustReplicate { + metadata[xhttp.AmzBucketReplicationStatus] = string(replication.Pending) + } pReader = NewPutObjReader(hashReader, nil, nil) // get gateway encryption options opts, err := putOpts(ctx, r, bucket, object, metadata) @@ -1155,7 +1172,17 @@ func (web *webAPIHandlers) Upload(w http.ResponseWriter, r *http.Request) { } } } - + if mustReplicate { + defer replicateObject(context.Background(), bucket, object, objInfo.VersionID, objectAPI, &eventArgs{ + EventName: event.ObjectCreatedPut, + BucketName: bucket, + Object: objInfo, + ReqParams: extractReqParams(r), + RespElements: extractRespElements(w), + UserAgent: r.UserAgent(), + Host: handlers.GetSourceIP(r), + }, false) + } // Notify object created event. sendEvent(eventArgs{ EventName: event.ObjectCreatedPut, diff --git a/go.mod b/go.mod index e7596e232..33dd4d184 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( git.apache.org/thrift.git v0.13.0 github.com/Azure/azure-pipeline-go v0.2.1 github.com/Azure/azure-storage-blob-go v0.8.0 - github.com/Azure/go-autorest/autorest/adal v0.9.0 // indirect + github.com/Azure/go-autorest/autorest/adal v0.9.1 // indirect github.com/Shopify/sarama v1.24.1 github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect github.com/alecthomas/participle v0.2.1 @@ -47,13 +47,13 @@ require ( github.com/miekg/dns v1.1.8 github.com/minio/cli v1.22.0 github.com/minio/highwayhash v1.0.0 - github.com/minio/minio-go/v7 v7.0.3 + github.com/minio/minio-go/v7 v7.0.5-0.20200811211821-14ed05478889 github.com/minio/selfupdate v0.3.1 github.com/minio/sha256-simd v0.1.1 github.com/minio/simdjson-go v0.1.5 github.com/minio/sio v0.2.0 github.com/mitchellh/go-homedir v1.1.0 - github.com/mmcloughlin/avo v0.0.0-20200523190732-4439b6b2c061 // indirect + github.com/mmcloughlin/avo v0.0.0-20200803215136-443f81d77104 // indirect github.com/montanaflynn/stats v0.5.0 github.com/nats-io/nats-server/v2 v2.1.7 github.com/nats-io/nats-streaming-server v0.18.0 // indirect @@ -76,14 +76,13 @@ require ( github.com/tidwall/sjson v1.0.4 github.com/tinylib/msgp v1.1.2 github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a - github.com/willf/bitset v1.1.10 // indirect + github.com/willf/bitset v1.1.11 // indirect github.com/willf/bloom v2.0.3+incompatible github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c go.etcd.io/etcd/v3 v3.3.0-rc.0.0.20200707003333-58bb8ae09f8e golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899 golang.org/x/net v0.0.0-20200707034311-ab3426394381 golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae - golang.org/x/tools v0.0.0-20200724172932-b5fc9d354d99 // indirect google.golang.org/api v0.5.0 gopkg.in/jcmturner/gokrb5.v7 v7.3.0 gopkg.in/ldap.v3 v3.0.3 diff --git a/go.sum b/go.sum index 97e6b9900..5b676d7b0 100644 --- a/go.sum +++ b/go.sum @@ -10,8 +10,8 @@ github.com/Azure/azure-storage-blob-go v0.8.0 h1:53qhf0Oxa0nOjgbDeeYPUeyiNmafAFE github.com/Azure/azure-storage-blob-go v0.8.0/go.mod h1:lPI3aLPpuLTeUwh1sViKXFxwl2B6teiRqI0deQUvsw0= github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest/autorest/adal v0.9.0 h1:SigMbuFNuKgc1xcGhaeapbh+8fgsu+GxgDRFyg7f5lM= -github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= +github.com/Azure/go-autorest/autorest/adal v0.9.1 h1:xjPqigMQe2+0DAJ5A6MLUPp5D2r2Io8qHCuCMMI/yJU= +github.com/Azure/go-autorest/autorest/adal v0.9.1/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= github.com/Azure/go-autorest/autorest/mocks v0.4.0 h1:z20OWOSG5aCye0HEkDp6TPmP17ZcfeMxPi6HnSALa8c= @@ -291,8 +291,8 @@ github.com/minio/highwayhash v1.0.0 h1:iMSDhgUILCr0TNm8LWlSjF8N0ZIj2qbO8WHp6Q/J2 github.com/minio/highwayhash v1.0.0/go.mod h1:xQboMTeM9nY9v/LlAOxFctujiv5+Aq2hR5dxBpaMbdc= github.com/minio/md5-simd v1.1.0 h1:QPfiOqlZH+Cj9teu0t9b1nTBfPbyTl16Of5MeuShdK4= github.com/minio/md5-simd v1.1.0/go.mod h1:XpBqgZULrMYD3R+M28PcmP0CkI7PEMzB3U77ZrKZ0Gw= -github.com/minio/minio-go/v7 v7.0.3 h1:a2VHaXDlKBcB3J5XJhKVfWBRi1+ZmMWFXABQ8TLlWbA= -github.com/minio/minio-go/v7 v7.0.3/go.mod h1:TA0CQCjJZHM5SJj9IjqR0NmpmQJ6bCbXifAJ3mUU6Hw= +github.com/minio/minio-go/v7 v7.0.5-0.20200811211821-14ed05478889 h1:uO6mz/7ywYA4U/Xl/FzJ/FePE4HIJT76/UFdXFoOqUY= +github.com/minio/minio-go/v7 v7.0.5-0.20200811211821-14ed05478889/go.mod h1:CSt2ETZNs+bIIhWTse0mcZKZWMGrFU7Er7RR0TmkDYk= github.com/minio/selfupdate v0.3.1 h1:BWEFSNnrZVMUWXbXIgLDNDjbejkmpAmZvy/nCz1HlEs= github.com/minio/selfupdate v0.3.1/go.mod h1:b8ThJzzH7u2MkF6PcIra7KaXO9Khf6alWPvMSyTDCFM= github.com/minio/sha256-simd v0.1.1 h1:5QHSlgo3nt5yKOJrC7W8w7X+NFl8cMPZm96iu8kKUJU= @@ -311,8 +311,8 @@ github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUb github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/mmcloughlin/avo v0.0.0-20200523190732-4439b6b2c061 h1:UCU8+cLbbvyxi0sQ9fSeoEhZgvrrD9HKMtX6Gmc1vk8= -github.com/mmcloughlin/avo v0.0.0-20200523190732-4439b6b2c061/go.mod h1:wqKykBG2QzQDJEzvRkcS8x6MiSJkF52hXZsXcjaB3ls= +github.com/mmcloughlin/avo v0.0.0-20200803215136-443f81d77104 h1:ULR/QWMgcgRiZLUjSSJMU+fW+RDMstRdmnDWj9Q+AsA= +github.com/mmcloughlin/avo v0.0.0-20200803215136-443f81d77104/go.mod h1:wqKykBG2QzQDJEzvRkcS8x6MiSJkF52hXZsXcjaB3ls= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= @@ -436,8 +436,8 @@ github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqri github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a h1:0R4NLDRDZX6JcmhJgXi5E4b8Wg84ihbmUKp/GvSPEzc= github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= -github.com/willf/bitset v1.1.10 h1:NotGKqX0KwQ72NUzqrjZq5ipPNDQex9lo3WpaS8L2sc= -github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= +github.com/willf/bitset v1.1.11 h1:N7Z7E9UvjW+sGsEl7k/SJrvY2reP1A07MrGuCjIOjRE= +github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= github.com/willf/bloom v2.0.3+incompatible h1:QDacWdqcAUI1MPOwIQZRy9kOR7yxfyEmxX8Wdm2/JPA= github.com/willf/bloom v2.0.3+incompatible/go.mod h1:MmAltL9pDMNTrvUkxdg0k0q5I0suxmuwp3KbyrZLOZ8= github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c h1:u40Z8hqBAAQyv+vATcGgV0YCnDjqSL7/q/JyPhhJSPk= @@ -447,7 +447,6 @@ github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0 github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0= go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.etcd.io/etcd/v3 v3.3.0-rc.0.0.20200707003333-58bb8ae09f8e h1:HZQLoe71Q24wVyDrGBRcVuogx32U+cPlcm/WoSLUI6c= @@ -488,8 +487,6 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -503,7 +500,6 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191112182307-2180aed22343/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -516,8 +512,6 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -543,7 +537,6 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db h1:6/JqlYfC1CCaLnGceQTI+sDGhC9UBSPAsBqI0Gun6kU= golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -567,8 +560,6 @@ golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200425043458-8463f397d07c h1:iHhCR0b26amDCiiO+kBguKZom9aMF+NrFxh9zeKR/XU= golang.org/x/tools v0.0.0-20200425043458-8463f397d07c/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200724172932-b5fc9d354d99 h1:OHn441rq5CeM5r1xJ0OmY7lfdTvnedi6k+vQiI7G9b8= -golang.org/x/tools v0.0.0-20200724172932-b5fc9d354d99/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=