Import changes for goma server
- 786216f2f72f949629c33852ff63ce33510edc89 fix for publish_to_external_repo
- a87d59872a64457efea7713ce74bbf603d06f70b Revert "remoteexec: use command.working_directory for non...
- 255f0ece2468fea4ee16ac6369e4f1a6ead0a48f remoteexec: batch fetch for gomaInputReader
- 0c2338e24694223b24f59eb000053d72d35da37a [toolchain config] Add feature to load config in parallel
- 7f2dee79b5ef5ec7e22de9486d85804988eed280 remoteexec: fix -fdebug-compilation-dir handling
- 895eb17049240200e8127400d5f970681627256f remoteexec: rename cwd agnostic to relocatable
- 13745f68074d375d7d5482468651a5ae8c568e15 httprpc: increase timeout if ctx is timed-out
- 1bc05465af290e200d74928bf8c4255bc6aff93b Add flag to disable server TLS cert verification
- 23912419bd78500dcbac8f943f3d569c48372414 remoteexec: use slice for gcc.go:pathFlags
- c6ab6f4278cac422bdc7be517db9f81e09faa673 Add OWNERS.
- 50502850a7d3e81152502461412ce998de72f8e3 remoteexec: handle some feature flag for cwd agnotstic ch...
- 3ff07d3135db5523b89bfaa26c55909c68a01939 use go 1.14.1
- 44e354a7a5bd9b23efbae063d0e44f8b22ac672a roll cloud.google.com/go v0.54.0 to v0.55.0
- 5a98cf13e6ca1aed3959be039fbcced7170fa0af cas: reduce memory usage in separateBlobsByByteLimit
- e4a1b73baa5f5385231d531ad20e576200d73a4b handle additional arguments when determining cwdAgnostic
- 36a3e8cb4b2d3fc149448a577978612e55e159bc roll cloud.google.com/go/pubsub v1.3.0 to v1.3.1
- b1abc4046e85064e14f4bcbfddf26f8d6177388e roll github.com/golang/protobuf v1.3.4 to v1.3.5
- 8b839a89b144b1f3bc57d918cb3263ff43fc0e9f roll google.golang.org/grpc v1.27.1 to v1.28.0
- 6f8d3ef24a2ec5017ca3bd4f1b593de86155ee1b roll cloud.google.com/go/pubsub v1.2.0 v1.3.0
- d07964413edc7f99b6261310a35629abd5da7727 roll cloud.google.com/go v0.53.0 to v0.54.0
- a4ccdb5b9835cfdea026468e28ea64b7823759dc mixer: return StatusServiceUnavailable if token expired.
- e96fb73af507d39679747135f97af39a881e500b roll google.golang.org/api v0.19.0 to v0.20.0
- b12d8580773d1afc61377a2c0b03506d7f2f2b69 Revert "use go 1.14"
- 9df336f29c934d537cff0943f148cb229ef7ab3a roll github.com/golang/protobuf v1.3.3 to v1.3.4
- 6c161b3c40e4245d64004b93e01e9a8414e634a9 roll google.golang.org/api v0.18.0 to v0.19.0
- 42578364e8b8d5d0fd7ef596519f5f59c0826fa2 use go 1.14
- 8634f6c6448ff45c89957574cf16451da5a750e3 remoteexec: gcc treats hmap as non-cwd agnostic
- 786f644e76df655907aaca52afd79ce8cd91f7cf remoteexec: Reduce size of ExecResp to < resp msg limit
- d1f6c3a1d14263ba83326299b0c1f7ef5c2adefd roll google.golang.org/api v0.17.0 to v0.18.0
- 3b38a0e70eaadfb67da9fb87d2e3d0b3c7161bda proto: Add new FileBlob::BlobType: FILE_REF
- 2171e844c06091f753b761e7cdcf93175d1182ab remoteexec: Factor out function to create chunked file blob
- d6272703df65f15e24f4e611aba25ad0075e259f remoteexec: call LookupFile() one blob at a time.
- 82052d721336f525e5bb503271f2352f893665fb Record RBE's execution duration into the ExecResp
- b9935b06dec093a18e48e50d4bdc5e8c16257e0d Update goma_data.proto
- 9b0b387d5ac4503f35195432c0042c0f5150a2fb remoteexec: Add unit test for gomaOutput.toFileBlob
- fd8b69d736133b33fdf72eadcb5a9ae50f6f259b Make gomaOutput.outputDirectory concurrent
- 1a1b7a1311160ae5648046b71bba6cb61fb7b0ef remoteexec: Refactor gomaOutput.traverseTree
- 6ab056629bc7313832979576de60080e2a800424 remoteexec: add unit test for gomaOutput.outputFile
- a8a95cf0b9e52dee9040ed1c36eaa41e4ee1f206 remoteexec: Add unit test for traverseTree
- acec3c2918f16f710f0af6339d522e594f4a3e19 remoteexec: Change order of traverseTree and outputDirect...
- 5ce3ce9359a7caa980c32bb843d88ea44338cf96 remoteexec: Add helper funcs for gomaoutput_test
- 0a042edfd6d76d70f3a267e253d9b69736667f6e remoteexec: use command.working_directory for non-Win
- db9890e479d82b71656e815a775679f612dcdf4b roll cloud.google.com/go v0.52.0 to v0.53.0
- b239f112ff79a0b5996495c559efbd67fae343d5 use go 1.13.8
- a181e5a6449ebac79dee577677d459795c207f63 Revert "remoteexec: use command.working_directory"
- b56b40cfbff6469c81475b097fc10ed93ae89450 remoteexec: use command.working_directory
- 8c46d9ac9e240a8c270f8069b26beb55c3bbb702 roll contrib.go.opencensus.io/exporter/stackdriver v0.12....
- 6f6f5357fc33e969a378cd868562bee9bebf59da roll google.golang.org/api v0.16.0 to v0.17.0
- 6550b010d6604a95939b795712120645d6e72e28 roll google.golang.org/grpc v1.27.0 to v1.27.1
- 686ecc2d45a88a4e2ae978151964e481da5d8955 roll cloud.google.com/go/pubsub v1.1.0 to v1.2.0
- 6becb26481d8fc6c30486a8888e4033cd485248a roll google.golang.org/api v0.15.0 to v0.16.0
- 36a475cc07e1159a3016a1cff12e08a9fbde5cda roll go.opencensus.io v0.22.2 to v0.22.3
- aa9c9a7b99f14aa427da8a2365f8073991b92da8 roll github.com/grpc-ecosystem/go-grpc-middleware v1.1.0 ...
GitOrigin-RevId: 786216f2f72f949629c33852ff63ce33510edc89
Change-Id: Iefea5aecd28687323d5d6d6e69f5b66e3d601113
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..d06a3c8
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1,9 @@
+sque@chromium.org
+sque@google.com
+tikuta@chromium.org
+tikuta@google.com
+ukai@chromium.org
+ukai@google.com
+yekuang@google.com
+yyanagisawa@chromium.org
+yyanagisawa@google.com
diff --git a/backend/mixer.go b/backend/mixer.go
index 7419c14..cb59991 100644
--- a/backend/mixer.go
+++ b/backend/mixer.go
@@ -11,6 +11,7 @@
"net/http"
"net/url"
+ "go.chromium.org/goma/server/auth"
"go.chromium.org/goma/server/auth/enduser"
"go.chromium.org/goma/server/log"
pb "go.chromium.org/goma/server/proto/backend"
@@ -148,6 +149,11 @@
ctx, err = m.Auth.Auth(ctx, req)
if err != nil {
code := http.StatusUnauthorized
+ // Making the client to retry with the refreshed token instead of throttling.
+ // Please see (b/150189886) for details.
+ if err == auth.ErrExpired {
+ code = http.StatusServiceUnavailable
+ }
http.Error(w, "The request requires user authentication", code)
logger.Errorf("auth error %s: %d %s: %v", req.URL.Path, code, http.StatusText(code), err)
return
diff --git a/cipd_manifest.txt b/cipd_manifest.txt
index 613b227..35bc87a 100644
--- a/cipd_manifest.txt
+++ b/cipd_manifest.txt
@@ -13,7 +13,7 @@
# https://chrome-infra-packages.appspot.com/
# go
-infra/3pp/tools/go/${platform} version:1.13.7
+infra/3pp/tools/go/${platform} version:1.14.1
# protoc
# If the version you want is missing, please follow the instruction in:
diff --git a/cipd_manifest.versions b/cipd_manifest.versions
index e72fe3a..1fdc1a1 100644
--- a/cipd_manifest.versions
+++ b/cipd_manifest.versions
@@ -2,8 +2,8 @@
# Do not modify manually. All changes will be overwritten.
infra/3pp/tools/go/linux-amd64
- version:1.13.7
- BSP3G-zh4nYyRapTOCGWJvMD1O9I7NwkBnxBnsJStNUC
+ version:1.14.1
+ pqblas-RwD-1Ths7vq8uC2SkNfYXUe8l9Y5ygcpDmdwC
infra/tools/protoc/linux-amd64
protobuf_version:v3.7.0
diff --git a/cmd/exec_server/main.go b/cmd/exec_server/main.go
index 906aae8..a9fc086 100644
--- a/cmd/exec_server/main.go
+++ b/cmd/exec_server/main.go
@@ -67,6 +67,7 @@
remoteexecAddr = flag.String("remoteexec-addr", "", "use remoteexec API endpoint")
remoteInstancePrefix = flag.String("remote-instance-prefix", "", "remote instance name path prefix.")
cmdFilesBucket = flag.String("cmd-files-bucket", "", "cloud storage bucket for command binary files")
+ fetchConfigParallel = flag.Bool("fetch-config-parallel", false, "fetch toolchain configs in parallel")
// Needed for b/120582303, but will be deprecated by b/80508682.
fileLookupConcurrency = flag.Int("file-lookup-concurrency", 20, "concurrency to look up files from file-server")
@@ -130,11 +131,16 @@
}
func configureByLoader(ctx context.Context, loader *command.ConfigMapLoader, inventory *exec.Inventory) (string, error) {
+ logger := log.FromContext(ctx)
+ start := time.Now()
resp, err := loader.Load(ctx)
+ logger.Infof("loader.Load finished in %s", time.Since(start))
if err != nil {
return "", err
}
+ start = time.Now()
err = inventory.Configure(ctx, resp)
+ logger.Infof("inventory.Configure finished in %s", time.Since(start))
if err != nil {
return "", err
}
@@ -196,7 +202,8 @@
cs.loader = &command.ConfigMapLoader{
ConfigMap: cs.configmap,
ConfigLoader: command.ConfigLoader{
- StorageClient: stiface.AdaptClient(gsclient),
+ StorageClient: stiface.AdaptClient(gsclient),
+ EnableParallel: *fetchConfigParallel,
},
}
return cs, nil
@@ -352,6 +359,7 @@
*fileLookupConcurrency = 1
}
casBlobLookupConcurrency := 20
+ outputFileConcurrency := 20
re := &remoteexec.Adapter{
InstancePrefix: *remoteInstancePrefix,
ExecTimeout: 15 * time.Minute,
@@ -366,6 +374,7 @@
},
FileLookupSema: make(chan struct{}, *fileLookupConcurrency),
CASBlobLookupSema: make(chan struct{}, casBlobLookupConcurrency),
+ OutputFileSema: make(chan struct{}, outputFileConcurrency),
}
if *cmdFilesBucket == "" {
diff --git a/command/configmap.go b/command/configmap.go
index 695e446..eb3df35 100644
--- a/command/configmap.go
+++ b/command/configmap.go
@@ -12,6 +12,7 @@
"io/ioutil"
"math/rand"
"path"
+ "runtime"
"sort"
"strings"
"time"
@@ -23,6 +24,7 @@
"github.com/googleapis/google-cloud-go-testing/storage/stiface"
"go.opencensus.io/stats"
"go.opencensus.io/stats/view"
+ "golang.org/x/sync/errgroup"
"google.golang.org/api/iterator"
"go.chromium.org/goma/server/log"
@@ -383,7 +385,8 @@
// ConfigLoader loads toolchain_config from cloud storage.
type ConfigLoader struct {
- StorageClient stiface.Client
+ StorageClient stiface.Client
+ EnableParallel bool
// for test
versionID func() string
@@ -492,6 +495,7 @@
// It sets rc.ServiceAddr as target addr.
func (c *ConfigLoader) Load(ctx context.Context, uri string, rc *cmdpb.RuntimeConfig) ([]*cmdpb.Config, error) {
platform := &cmdpb.RemoteexecPlatform{}
+ parallel := c.EnableParallel
for _, p := range rc.Platform.GetProperties() {
platform.Properties = append(platform.Properties, &cmdpb.RemoteexecPlatform_Property{
Name: p.Name,
@@ -500,7 +504,7 @@
}
platform.HasNsjail = rc.GetPlatformRuntimeConfig().GetHasNsjail()
- confs, err := loadConfigs(ctx, c.StorageClient, uri, rc, platform)
+ confs, err := loadConfigs(ctx, c.StorageClient, uri, rc, platform, parallel)
if err != nil {
return nil, err
}
@@ -671,7 +675,7 @@
return nil
}
-func loadConfigs(ctx context.Context, client stiface.Client, uri string, rc *cmdpb.RuntimeConfig, platform *cmdpb.RemoteexecPlatform) ([]*cmdpb.Config, error) {
+func loadConfigs(ctx context.Context, client stiface.Client, uri string, rc *cmdpb.RuntimeConfig, platform *cmdpb.RemoteexecPlatform, parallel bool) ([]*cmdpb.Config, error) {
logger := log.FromContext(ctx)
bucket, obj, err := splitGCSPath(uri)
if err != nil {
@@ -689,7 +693,10 @@
// pagination?
var confs []*cmdpb.Config
logger.Infof("load from %s", bucket)
+ start := time.Now()
+ var attrsList []*storage.ObjectAttrs
for {
+ // iter does not have an API to read all, so just iterate everything.
attrs, err := iter.Next()
if err == iterator.Done {
break
@@ -697,8 +704,8 @@
if err != nil {
return nil, err
}
- err = checkPrebuilt(rc, attrs.Name)
- if err != nil {
+ // Some string ops, no need to be paralleled.
+ if err := checkPrebuilt(rc, attrs.Name); err != nil {
logger.Infof("prebuilt %s: %v", attrs.Name, err)
continue
}
@@ -707,46 +714,76 @@
logger.Infof("ignore %s", attrs.Name)
continue
}
- d, err := loadDescriptor(ctx, client, bucket, attrs.Name)
- if err != nil {
- return nil, err
- }
- ts, err := ptypes.TimestampProto(attrs.Updated)
- if err != nil {
- return nil, err
- }
-
- // check descriptor.
- err = checkSelector(rc, d.Selector)
- if err != nil {
- logger.Errorf("selector in %s/%s: %v", bucket, attrs.Name, err)
+ attrsList = append(attrsList, attrs)
+ }
+ logger.Infof("iterate over %s took %v", bucket, time.Since(start))
+ start = time.Now()
+ concurrent := 1
+ if parallel {
+ // Limit concurrent requests to NumCPU * 4.
+ concurrent = runtime.NumCPU() * 4
+ }
+ // The ordering of the output should be guaranteed
+ // as unit tests using proto.Equal.
+ var eg errgroup.Group
+ confList := make([]*cmdpb.Config, len(attrsList))
+ sema := make(chan struct{}, concurrent)
+ for i := range attrsList {
+ i := i
+ sema <- struct{}{}
+ eg.Go(func() error {
+ // Limit number of goroutines.
+ defer func() { <-sema }()
+ attrs := attrsList[i]
+ d, err := loadDescriptor(ctx, client, bucket, attrs.Name)
+ if err != nil {
+ return err
+ }
+ ts, err := ptypes.TimestampProto(attrs.Updated)
+ if err != nil {
+ return err
+ }
+ if err = checkSelector(rc, d.Selector); err != nil {
+ logger.Errorf("selector in %s/%s: %v", bucket, attrs.Name, err)
+ return nil
+ }
+ if d.Setup == nil {
+ logger.Errorf("no setup in %s/%s", bucket, attrs.Name)
+ return nil
+ }
+ if d.Setup.PathType == cmdpb.CmdDescriptor_UNKNOWN_PATH_TYPE {
+ logger.Errorf("unknown path type in %s/%s", bucket, attrs.Name)
+ return nil
+ }
+ // TODO: fix config definition.
+ // BuildInfo is used for key for cache key.
+ // include cmd_server hash etc?
+ // BuildInfo.Timestamp is used for dedup in exec_server.
+ confList[i] = &cmdpb.Config{
+ Target: &cmdpb.Target{
+ Addr: rc.ServiceAddr,
+ },
+ BuildInfo: &cmdpb.BuildInfo{
+ Timestamp: ts,
+ },
+ CmdDescriptor: d,
+ RemoteexecPlatform: platform,
+ }
+ return nil
+ })
+ }
+ if err := eg.Wait(); err != nil {
+ return nil, err
+ }
+ for i := range attrsList {
+ attrs := attrsList[i]
+ conf := confList[i]
+ if conf == nil {
continue
}
- if d.Setup == nil {
- logger.Errorf("no setup in %s/%s", bucket, attrs.Name)
- continue
- }
- if d.Setup.PathType == cmdpb.CmdDescriptor_UNKNOWN_PATH_TYPE {
- logger.Errorf("unknown path type in %s/%s", bucket, attrs.Name)
- continue
- }
- // TODO: fix config definition.
- // BuildInfo is used for key for cache key.
- // include cmd_server hash etc?
- // BuildInfo.Timestamp is used for dedup in exec_server.
- conf := &cmdpb.Config{
- Target: &cmdpb.Target{
- Addr: rc.ServiceAddr,
- },
- BuildInfo: &cmdpb.BuildInfo{
- Timestamp: ts,
- },
- CmdDescriptor: d,
- RemoteexecPlatform: platform,
- }
confs = append(confs, conf)
- logger.Infof("%s/%s: %s", bucket, attrs.Name, d.GetSelector())
+ logger.Infof("%s/%s: %s", bucket, attrs.Name, conf.CmdDescriptor.GetSelector())
}
- logger.Infof("loaded from %s: %d configs", bucket, len(confs))
+ logger.Infof("loaded from %s: %d configs using %v", bucket, len(confs), time.Since(start))
return confs, nil
}
diff --git a/go.mod b/go.mod
index 4084ef5..75b7140 100644
--- a/go.mod
+++ b/go.mod
@@ -3,28 +3,28 @@
go 1.12
require (
- cloud.google.com/go v0.52.0
- cloud.google.com/go/pubsub v1.1.0
- cloud.google.com/go/storage v1.5.0
- contrib.go.opencensus.io/exporter/stackdriver v0.12.9
+ cloud.google.com/go v0.55.0
+ cloud.google.com/go/pubsub v1.3.1
+ cloud.google.com/go/storage v1.6.0
+ contrib.go.opencensus.io/exporter/stackdriver v0.13.0
github.com/bazelbuild/remote-apis v0.0.0-20191104140458-e77c4eb2ca48
github.com/bazelbuild/remote-apis-sdks v0.0.0-20200117155253-d02017f96d3b
github.com/fsnotify/fsnotify v1.4.7
- github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7
- github.com/golang/protobuf v1.3.3
+ github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e
+ github.com/golang/protobuf v1.3.5
github.com/gomodule/redigo v2.0.0+incompatible
github.com/google/go-cmp v0.4.0
github.com/google/uuid v1.1.1
github.com/googleapis/gax-go/v2 v2.0.5
github.com/googleapis/google-cloud-go-testing v0.0.0-20190904031503-2d24dde44ba5
- github.com/grpc-ecosystem/go-grpc-middleware v1.1.0
- go.opencensus.io v0.22.2
+ github.com/grpc-ecosystem/go-grpc-middleware v1.2.0
+ go.opencensus.io v0.22.3
go.uber.org/zap v1.10.0
golang.org/x/build v0.0.0-20191031202223-0706ea4fce0c
- golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa
+ golang.org/x/net v0.0.0-20200301022130-244492dfa37a
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
- golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e
- google.golang.org/api v0.15.0
- google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba
- google.golang.org/grpc v1.27.0
+ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a
+ google.golang.org/api v0.20.0
+ google.golang.org/genproto v0.0.0-20200317114155-1f3552e48f24
+ google.golang.org/grpc v1.28.0
)
diff --git a/go.sum b/go.sum
index f9b193a..9b356ad 100644
--- a/go.sum
+++ b/go.sum
@@ -14,22 +14,40 @@
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
cloud.google.com/go v0.52.0 h1:GGslhk/BU052LPlnI1vpp3fcbUs+hQ3E+Doti/3/vF8=
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
+cloud.google.com/go v0.53.0 h1:MZQCQQaRwOrAcuKjiHWHrgKykt4fZyuwF2dtiG3fGW8=
+cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
+cloud.google.com/go v0.54.0 h1:3ithwDMr7/3vpAMXiH+ZQnYbuIsh+OPhUPMFC9enmn0=
+cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
+cloud.google.com/go v0.55.0 h1:eoz/lYxKSL4CNAiaUJ0ZfD1J3bfMYbU5B3rwM1C1EIU=
+cloud.google.com/go v0.55.0/go.mod h1:ZHmoY+/lIMNkN2+fBmuTiqZ4inFhvQad8ft7MT8IV5Y=
cloud.google.com/go/bigquery v1.0.1 h1:hL+ycaJpVE9M7nLoiXb/Pn10ENE2u+oddxbD8uu0ZVU=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0 h1:sAbMqjY1PEQKZBWfbu6Y6bsupJ9c4QdHnzg/VvYTLcE=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
+cloud.google.com/go/bigquery v1.4.0 h1:xE3CPsOgttP4ACBePh79zTKALtXwn/Edhcr16R5hMWU=
+cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
+cloud.google.com/go/bigquery v1.5.0 h1:K2NyuHRuv15ku6eUpe0DQk5ZykPMnSOnvuVf6IHcjaE=
+cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
cloud.google.com/go/datastore v1.0.0 h1:Kt+gOPPp2LEPWp8CSfxhsM8ik9CcyE/gYu+0r+RnZvM=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
+cloud.google.com/go/datastore v1.1.0 h1:/May9ojXjRkPBNVrq+oWLqmWCkr4OU5uRY29bu0mRyQ=
+cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
cloud.google.com/go/pubsub v1.0.1 h1:W9tAK3E57P75u0XLLR82LZyw8VpAnhmyTOxW9qzmyj8=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0 h1:9/vpR43S4aJaROxqQHQ3nH9lfyKKV0dC3vOmnw8ebQQ=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
+cloud.google.com/go/pubsub v1.2.0 h1:Lpy6hKgdcl7a3WGSfJIFmxmcdjSpP6OmBEfcOv1Y680=
+cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
+cloud.google.com/go/pubsub v1.3.1 h1:ukjixP1wl0LpnZ6LWtZJ0mX5tBmjp1f8Sqer8Z2OMUU=
+cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
cloud.google.com/go/storage v1.0.0 h1:VV2nUM3wwLLGh9lSABFgZMjInyUbJeaRSE64WuAIQ+4=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
cloud.google.com/go/storage v1.5.0 h1:RPUcBvDeYgQFMfQu1eBMq6piD1SXmLH+vK3qjewZPus=
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
-contrib.go.opencensus.io/exporter/stackdriver v0.12.9 h1:ZRVpDigsb+nVI/yps/NLDOYzYjFFmm3OCsBhmYocxR0=
-contrib.go.opencensus.io/exporter/stackdriver v0.12.9/go.mod h1:XyyafDnFOsqoxHJgTFycKZMrRUrPThLh2iYTJF6uoO0=
+cloud.google.com/go/storage v1.6.0 h1:UDpwYIwla4jHGzZJaEJYx1tOejbgSoNqsAfHAUYe2r8=
+cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
+contrib.go.opencensus.io/exporter/stackdriver v0.13.0 h1:Jaz7WbqjtfoCPa1KbfisCX+P5aM3DizEY9pQMU0oAQo=
+contrib.go.opencensus.io/exporter/stackdriver v0.13.0/go.mod h1:z2tyTZtPmQ2HvWH4cOmVDgtY+1lomfKdbLnkJvZdc8c=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
@@ -54,6 +72,7 @@
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cznic/cc v0.0.0-20181122101902-d673e9b70d4d/go.mod h1:m3fD/V+XTB35Kh9zw6dzjMY+We0Q7PMf6LLIC4vuG9k=
@@ -71,7 +90,9 @@
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/disintegration/gift v1.2.1/go.mod h1:Jh2i7f7Q2BM7Ezno3PhfezbR1xpUg9dUg3/RlKGr4HI=
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
+github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
@@ -81,6 +102,10 @@
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
+github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
+github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
+github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/godbus/dbus v4.1.0+incompatible/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
@@ -92,11 +117,19 @@
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7 h1:5ZkaAPbicIKTF2I64qf5Fh8Aa83Q/dnOafMYV0OMwjA=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY=
+github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0 h1:28o5sBqPkBsMGnC6b4MvE2TzSr5/AT4c/1fLqVGIwlk=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1 h1:qGJ6qTW+x6xX/my+8YUVl4WNpX9B7+/l2tRsHGZ7f2s=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
+github.com/golang/mock v1.4.0 h1:Rd1kQnQu0Hq3qvJppYSG0HtP+f5LPPUiDswTLiEegLg=
+github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
+github.com/golang/mock v1.4.1 h1:ocYkMQY5RrXTYgXl7ICpV0IXwlEQGwKIsery4gyXa1U=
+github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
+github.com/golang/mock v1.4.3 h1:GV+pQPG/EUUbkh47niozDcADz6go/dUwhVzdUQHIVRw=
+github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@@ -104,6 +137,10 @@
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/golang/protobuf v1.3.4 h1:87PNWwrRvUSnqS4dlcBU/ftvOIBep4sYuBLlh6rX2wk=
+github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/golang/protobuf v1.3.5 h1:F768QJ1E9tib+q5Sc8MkdJi1RxLTbRcTf8LJV56aRls=
+github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0=
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
@@ -135,6 +172,10 @@
github.com/google/pprof v0.0.0-20191028172815-5e965273ee43/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc h1:DLpL8pWq0v4JYoRpEhDfsJhhJyGKCcQM2WPW2TJs31c=
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12 h1:TgXhFz35pKlZuUz1pNlOKk1UCSXPpuUIc144Wd7SxCA=
+github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3 h1:SRgJV+IoxM5MKyFdlSUeNy6/ycRUF2yBAKdAQswoHUk=
+github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
@@ -148,8 +189,8 @@
github.com/googleapis/google-cloud-go-testing v0.0.0-20190904031503-2d24dde44ba5 h1:xOo+ADhpvq/M2z8t7xMswzeaSa5oPDRNjSildIGfF5U=
github.com/googleapis/google-cloud-go-testing v0.0.0-20190904031503-2d24dde44ba5/go.mod h1:xRZIzjtWZ0v0RQc6ob2jLaRvHJu8rShcPkTGPCStK04=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
-github.com/grpc-ecosystem/go-grpc-middleware v1.1.0 h1:THDBEeQ9xZ8JEaCLyLQqXMMdRqNr0QAUJTIkQAUtFjg=
-github.com/grpc-ecosystem/go-grpc-middleware v1.1.0/go.mod h1:f5nM7jw/oeRSadq3xCzHAvxcr8HZnzsqU6ILg/0NiiE=
+github.com/grpc-ecosystem/go-grpc-middleware v1.2.0 h1:0IKlLyQ3Hs9nDaiK5cSHAGmcQEIC8l2Ts1u6x5Dfrqg=
+github.com/grpc-ecosystem/go-grpc-middleware v1.2.0/go.mod h1:mJzapYve32yjrKlk9GbyCZHuPgZsrbyIbyKhSzOpg6s=
github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
@@ -169,6 +210,7 @@
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
@@ -208,6 +250,7 @@
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
+github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.opencensus.io v0.21.0 h1:mU6zScU4U1YAFPHEHYk+3JC4SY7JxgkqS10ZOSyksNg=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4=
@@ -216,6 +259,8 @@
go.opencensus.io v0.22.1/go.mod h1:Ap50jQcDJrx6rB6VgeeFPtuPIf3wMRvRfrfYDO6+BmA=
go.opencensus.io v0.22.2 h1:75k/FF0Q2YM8QYo07VPddOLBslDt1MZOdEslOHvmzAs=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
+go.opencensus.io v0.22.3 h1:8sGtKOrtQqkN1bp2AtX+misvLIlOmsEsNd+9NIcPEm8=
+go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=
@@ -243,6 +288,11 @@
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20191227195350-da58074b4299 h1:zQpM52jfKHG6II1ISZY1ZcpygvuSFZpLwfluuF89XOg=
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
+golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a h1:7Wlg8L54In96HTWOaI4sreLJ6qfyGuvSau5el3fK41Y=
+golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
+golang.org/x/exp v0.0.0-20200207192155-f17229e696bd h1:zkO/Lhoka23X63N9OSzpSeROEUQ5ODw47tM3YWjygbs=
+golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
+golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
@@ -258,12 +308,19 @@
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f h1:J5lckAjkw6qYlOZNj90mLYNTEKDvWeuc1yieZ8qUzUE=
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
+golang.org/x/lint v0.0.0-20200130185559-910be7a94367 h1:0IiAsCRByjO2QjX7ZPkw5oU9x+n1YqRL802rjC0c3Aw=
+golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k=
+golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mobile v0.0.0-20191031020345-0945064e013a/go.mod h1:p895TfNkDgPEmEQrNiOtIl3j98d/tGU95djDj7NfyjQ=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
+golang.org/x/mod v0.2.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-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -278,13 +335,18 @@
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20190912160710-24e19bdeb0f2 h1:4dVFTC832rPn4pomLSz1vA+are2+dU19w1H8OngV7nc=
-golang.org/x/net v0.0.0-20190912160710-24e19bdeb0f2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191101175033-0deb6923b6d9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 h1:efeOvDhwQ29Dj3SdAV/MJf8oukgn+8D8WgaCaRMchF8=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa h1:F+8P+gmewFQYRk6JoLQLwjBCTu3mcIURZfNkVweuRKA=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI=
+golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/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-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0=
+golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421 h1:Wo7BWFiOk0QRFMLYMqJGFMd9CgUAcGx7V+qEg/h5IBI=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -305,6 +367,8 @@
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-20200317015054-43a5402ce75a h1:WXEvlFVvvGxCJLG6REjsT03iWnKLEWinaScsxF2Vm2o=
+golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
@@ -319,14 +383,24 @@
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0 h1:HyfiK1WMnHj5FXFXatD+Qs1A/xC2Run6RzeW1SyHxpc=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190912141932-bc967efca4b8 h1:41hwlulw1prEMBxLQSlMSux1zxJf07B3WPsdjJlKZxE=
-golang.org/x/sys v0.0.0-20190912141932-bc967efca4b8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191104094858-e8c54fb511f6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8 h1:JA8d3MPx/IToSyXZG/RhwYEtfrKO1Fxrqe8KrkiLXKM=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1 h1:gZpLHxUX5BdYLA08Lj4YCJNN/jk7KtquiArPoeX0WvA=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5 h1:LfCXLvNmTYH9kEmVgqbnsWfruoXZIrh4YBgqVHtDvw0=
+golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4 h1:sfkvUWPNGwSV+8/fNqctR5lS2AqCSqYwXdrjCxp/dXo=
+golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 h1:uYVVQ9WP/Ds2ROhcaGPeIdVq0RIXVLwsHlnvJ+cT1So=
+golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200317113312-5766fd39f98d h1:62ap6LNOjDU6uGmKXHJbSfciMoV+FeI1sRXx/pLDL44=
+golang.org/x/sys v0.0.0-20200317113312-5766fd39f98d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2 h1:z99zHgr7hKfrUcX/KsoJk5FJfjTceCKIp96+biqP4To=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -353,17 +427,35 @@
golang.org/x/tools v0.0.0-20190909214602-067311248421/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191010075000-0337d82405ff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191010171213-8abd42400456/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2 h1:EtTFh6h4SAKemS+CURDMTDIANuduG5zKEXShyy18bGA=
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4 h1:Toz2IK7k8rbltAXwNAxKcn9OzqyNfMUhUNjz3sL0NMk=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200117161641-43d50277825c h1:2EA2K0k9bcvvEDlqD8xdlOhCOqq+O/p9Voqi4x9W1YU=
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74 h1:KW20qMcLRWuIgjdCpHFJbVZA7zsDKtFXPNcm7/eI5ZA=
+golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56 h1:DFtSed2q3HtNuVazwVDZ4nSRS/JrZEig0gz2BY4VNrg=
+golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb h1:iKlO7ROJc6SttHKlxzwGytRtBUqX4VARrNTgP2YLX5M=
+golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
+golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d h1:3K34ovZAOnVaUPxanr0j4ghTZTPTA0CnXvjCl+5lZqk=
+golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
+golang.org/x/tools v0.0.0-20200317043434-63da46f3035e h1:8ogAbHWoJTPepnVbNRqXLOpzMkl0rtRsM7crbflc4XM=
+golang.org/x/tools v0.0.0-20200317043434-63da46f3035e/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
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=
@@ -381,6 +473,12 @@
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.15.0 h1:yzlyyDW/J0w8yNFJIhiAJy4kq74S+1DOLdawELNxFMA=
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
+google.golang.org/api v0.17.0 h1:0q95w+VuFtv4PAx4PZVQdBMmYbaCHbnfKaEiDIcVyag=
+google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.20.0 h1:jz2KixHX7EcCPiQrySzPdnYT7DbINAypCqKZ1Z7GM40=
+google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@@ -415,6 +513,19 @@
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba h1:pRj9OXZbwNtbtZtOB4dLwfK4u+EVRMvP+e9zKkg2grM=
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90 h1:7THRSvPuzF1bql5kyFzX0JM0vpGhwuhskgJrJsbZ80Y=
+google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
+google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce h1:1mbrb1tUU+Zmt5C94IGKADBTJZjZXAd+BubWi7r9EiI=
+google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200305110556-506484158171 h1:xes2Q2k+d/+YNXVw0FpZkIDJiaux4OVrRKXRAzH6A0U=
+google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200312145019-da6875a35672 h1:jiDSspVssiikoRPFHT6pYrL+CL6/yIc3b9AuHO/4xik=
+google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200317114155-1f3552e48f24 h1:IGPykv426z7LZSVPlaPufOyphngM4at5uZ7x5alaFvE=
+google.golang.org/genproto v0.0.0-20200317114155-1f3552e48f24/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/grpc v1.19.0 h1:cfg4PD8YEdSFnm7qLV4++93WcmhH2nIUhMjhdCvl3j8=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1 h1:Hz2g2wirWK7H0qIIhGIqRGTuMwTE8HEKFnDZZ7lm9NU=
@@ -424,10 +535,15 @@
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.23.1 h1:q4XQuHFC6I28BKZpo6IYyb3mNO+l7lSOxRuYTCiDfXk=
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.26.0 h1:2dTRdpdFEEhJYQD8EMLB61nnrzSCTbG38PhqdhvOltg=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0 h1:rRYRFMVgRv6E0D70Skyfsr28tDXIuuPZyWGMPdMcnXg=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.27.1 h1:zvIju4sqAGvwKspUQOhwnpcqSbzi7/H6QomNNjTL4sk=
+google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.28.0 h1:bO/TA4OxCOummhSf10siHuG7vJOiwh7SpRpFZDkOgl4=
+google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -448,4 +564,8 @@
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
+honnef.co/go/tools v0.0.1-2020.1.3 h1:sXmLre5bzIR6ypkjXCDI3jHPssRhc8KD/Ome589sc3U=
+honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
+rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
+rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
diff --git a/httprpc/server.go b/httprpc/server.go
index 838431b..6372086 100644
--- a/httprpc/server.go
+++ b/httprpc/server.go
@@ -370,13 +370,16 @@
authOK = true
}
resp, err = h(ctx, req)
+ if err != nil {
+ logger.Warnf("handler error %v; ctx.Err()=%v", err, ctx.Err())
+ }
if opt.Auth != nil && status.Code(err) == codes.Unauthenticated {
logger.Warnf("retry for unauthenticated %v", err)
return rpc.RetriableError{
Err: err,
}
}
- if status.Code(err) == codes.DeadlineExceeded && pctx.Err() == nil {
+ if ((err != nil && ctx.Err() == context.DeadlineExceeded) || status.Code(err) == codes.DeadlineExceeded) && pctx.Err() == nil {
// api call is timed out, but caller's context is not.
// it would happen
// a) timeout was short; api call actually needs more time.
diff --git a/proto/api/goma_data.pb.go b/proto/api/goma_data.pb.go
index 8073e79..d4f5816 100644
--- a/proto/api/goma_data.pb.go
+++ b/proto/api/goma_data.pb.go
@@ -6,6 +6,7 @@
import (
fmt "fmt"
proto "github.com/golang/protobuf/proto"
+ timestamp "github.com/golang/protobuf/ptypes/timestamp"
math "math"
)
@@ -27,6 +28,8 @@
FileBlob_FILE FileBlob_BlobType = 1
FileBlob_FILE_META FileBlob_BlobType = 2
FileBlob_FILE_CHUNK FileBlob_BlobType = 3
+ // ARCHIVE = 4;
+ FileBlob_FILE_REF FileBlob_BlobType = 5
)
var FileBlob_BlobType_name = map[int32]string{
@@ -34,6 +37,7 @@
1: "FILE",
2: "FILE_META",
3: "FILE_CHUNK",
+ 5: "FILE_REF",
}
var FileBlob_BlobType_value = map[string]int32{
@@ -41,6 +45,7 @@
"FILE": 1,
"FILE_META": 2,
"FILE_CHUNK": 3,
+ "FILE_REF": 5,
}
func (x FileBlob_BlobType) Enum() *FileBlob_BlobType {
@@ -340,15 +345,21 @@
// hash_key = sha256(serialized FileBlob)
//
-// for small file (< 2MB)
+// for small file (< 2MB) embedded
// blob_type=FILE, !has_offset(), has_content()
// has_file_size(), hash_key_size() == 0
+//
// for large file
// blob_type=FILE_META, !has_offset(), !has_content()
// has_file_size(), hash_key_size() > 0
// for each hash_key(i)
// blob_type=FILE_CHUNK, has_offset(), has_content(),
// has_file_size(), hash_key_size() == 0
+//
+// for small file (< 2MB), stored as blob_type=FILE in file server with
+// key=hash_key[0]
+// blob_type=FILE_REF, !hash_offset(), !has_content()
+// has_file_size(), hash_key_size() == 1
type FileBlob struct {
BlobType *FileBlob_BlobType `protobuf:"varint,1,req,name=blob_type,json=blobType,enum=devtools_goma.FileBlob_BlobType" json:"blob_type,omitempty"`
// for blob_type=FILE_CHUNK
@@ -883,7 +894,7 @@
// e.g. "os":"linux".
//
// Inspired from swarming bot dimensions.
- // https://cs.chromium.org/chromium/infra/luci/appengine/swarming/proto/bots.proto
+ // https://chromium.googlesource.com/infra/luci/luci-py/+/effbcfcafa96e0840189a98ac485586ba2c2ceb6/appengine/swarming/proto/config/bots.proto
Dimensions []string `protobuf:"bytes,10,rep,name=dimensions" json:"dimensions,omitempty"`
// Requester's path style.
PathStyle *RequesterInfo_PathStyle `protobuf:"varint,11,opt,name=path_style,json=pathStyle,enum=devtools_goma.RequesterInfo_PathStyle" json:"path_style,omitempty"`
@@ -1344,53 +1355,58 @@
return nil
}
-type MultiExecReq struct {
- Req []*ExecReq `protobuf:"bytes,1,rep,name=req" json:"req,omitempty"`
- RequesterInfo *RequesterInfo `protobuf:"bytes,10,opt,name=requester_info,json=requesterInfo" json:"requester_info,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+// Stats of a single RBE execution. This is a subset of
+// https://github.com/bazelbuild/remote-apis/blob/178b756a22d441d8d06873a70bcd0ef01d876467/build/bazel/remote/execution/v2/remote_execution.proto#L789-L819
+type ExecutionStats struct {
+ // When the worker started executing the action command.
+ ExecutionStartTimestamp *timestamp.Timestamp `protobuf:"bytes,1,opt,name=execution_start_timestamp,json=executionStartTimestamp" json:"execution_start_timestamp,omitempty"`
+ // When the worker completed executing the action command.
+ ExecutionCompletedTimestamp *timestamp.Timestamp `protobuf:"bytes,2,opt,name=execution_completed_timestamp,json=executionCompletedTimestamp" json:"execution_completed_timestamp,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
}
-func (m *MultiExecReq) Reset() { *m = MultiExecReq{} }
-func (m *MultiExecReq) String() string { return proto.CompactTextString(m) }
-func (*MultiExecReq) ProtoMessage() {}
-func (*MultiExecReq) Descriptor() ([]byte, []int) {
+func (m *ExecutionStats) Reset() { *m = ExecutionStats{} }
+func (m *ExecutionStats) String() string { return proto.CompactTextString(m) }
+func (*ExecutionStats) ProtoMessage() {}
+func (*ExecutionStats) Descriptor() ([]byte, []int) {
return fileDescriptor_8016d97e57513e7e, []int{8}
}
-func (m *MultiExecReq) XXX_Unmarshal(b []byte) error {
- return xxx_messageInfo_MultiExecReq.Unmarshal(m, b)
+func (m *ExecutionStats) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_ExecutionStats.Unmarshal(m, b)
}
-func (m *MultiExecReq) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- return xxx_messageInfo_MultiExecReq.Marshal(b, m, deterministic)
+func (m *ExecutionStats) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_ExecutionStats.Marshal(b, m, deterministic)
}
-func (m *MultiExecReq) XXX_Merge(src proto.Message) {
- xxx_messageInfo_MultiExecReq.Merge(m, src)
+func (m *ExecutionStats) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_ExecutionStats.Merge(m, src)
}
-func (m *MultiExecReq) XXX_Size() int {
- return xxx_messageInfo_MultiExecReq.Size(m)
+func (m *ExecutionStats) XXX_Size() int {
+ return xxx_messageInfo_ExecutionStats.Size(m)
}
-func (m *MultiExecReq) XXX_DiscardUnknown() {
- xxx_messageInfo_MultiExecReq.DiscardUnknown(m)
+func (m *ExecutionStats) XXX_DiscardUnknown() {
+ xxx_messageInfo_ExecutionStats.DiscardUnknown(m)
}
-var xxx_messageInfo_MultiExecReq proto.InternalMessageInfo
+var xxx_messageInfo_ExecutionStats proto.InternalMessageInfo
-func (m *MultiExecReq) GetReq() []*ExecReq {
+func (m *ExecutionStats) GetExecutionStartTimestamp() *timestamp.Timestamp {
if m != nil {
- return m.Req
+ return m.ExecutionStartTimestamp
}
return nil
}
-func (m *MultiExecReq) GetRequesterInfo() *RequesterInfo {
+func (m *ExecutionStats) GetExecutionCompletedTimestamp() *timestamp.Timestamp {
if m != nil {
- return m.RequesterInfo
+ return m.ExecutionCompletedTimestamp
}
return nil
}
+// NEXT ID TO USE: 82
type ExecResp struct {
Result *ExecResult `protobuf:"bytes,1,opt,name=result" json:"result,omitempty"`
Error *ExecResp_ExecError `protobuf:"varint,2,opt,name=error,enum=devtools_goma.ExecResp_ExecError,def=0" json:"error,omitempty"`
@@ -1428,9 +1444,11 @@
CompilerProxyLocalRun *bool `protobuf:"varint,75,opt,name=compiler_proxy_local_run,json=compilerProxyLocalRun" json:"compiler_proxy_local_run,omitempty"`
CompilerProxyLocalKilled *bool `protobuf:"varint,76,opt,name=compiler_proxy_local_killed,json=compilerProxyLocalKilled" json:"compiler_proxy_local_killed,omitempty"`
CompilerProxyExecRequestRetry *int32 `protobuf:"varint,80,opt,name=compiler_proxy_exec_request_retry,json=compilerProxyExecRequestRetry" json:"compiler_proxy_exec_request_retry,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ // Execution stats collected from RBE
+ ExecutionStats *ExecutionStats `protobuf:"bytes,81,opt,name=execution_stats,json=executionStats" json:"execution_stats,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
}
func (m *ExecResp) Reset() { *m = ExecResp{} }
@@ -1688,88 +1706,9 @@
return 0
}
-type MultiExecResp struct {
- Response []*MultiExecResp_Response `protobuf:"group,1,rep,name=Response,json=response" json:"response,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
-}
-
-func (m *MultiExecResp) Reset() { *m = MultiExecResp{} }
-func (m *MultiExecResp) String() string { return proto.CompactTextString(m) }
-func (*MultiExecResp) ProtoMessage() {}
-func (*MultiExecResp) Descriptor() ([]byte, []int) {
- return fileDescriptor_8016d97e57513e7e, []int{10}
-}
-
-func (m *MultiExecResp) XXX_Unmarshal(b []byte) error {
- return xxx_messageInfo_MultiExecResp.Unmarshal(m, b)
-}
-func (m *MultiExecResp) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- return xxx_messageInfo_MultiExecResp.Marshal(b, m, deterministic)
-}
-func (m *MultiExecResp) XXX_Merge(src proto.Message) {
- xxx_messageInfo_MultiExecResp.Merge(m, src)
-}
-func (m *MultiExecResp) XXX_Size() int {
- return xxx_messageInfo_MultiExecResp.Size(m)
-}
-func (m *MultiExecResp) XXX_DiscardUnknown() {
- xxx_messageInfo_MultiExecResp.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_MultiExecResp proto.InternalMessageInfo
-
-func (m *MultiExecResp) GetResponse() []*MultiExecResp_Response {
+func (m *ExecResp) GetExecutionStats() *ExecutionStats {
if m != nil {
- return m.Response
- }
- return nil
-}
-
-type MultiExecResp_Response struct {
- ResponseCode *int32 `protobuf:"varint,2,opt,name=response_code,json=responseCode" json:"response_code,omitempty"`
- Resp *ExecResp `protobuf:"bytes,3,opt,name=resp" json:"resp,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
-}
-
-func (m *MultiExecResp_Response) Reset() { *m = MultiExecResp_Response{} }
-func (m *MultiExecResp_Response) String() string { return proto.CompactTextString(m) }
-func (*MultiExecResp_Response) ProtoMessage() {}
-func (*MultiExecResp_Response) Descriptor() ([]byte, []int) {
- return fileDescriptor_8016d97e57513e7e, []int{10, 0}
-}
-
-func (m *MultiExecResp_Response) XXX_Unmarshal(b []byte) error {
- return xxx_messageInfo_MultiExecResp_Response.Unmarshal(m, b)
-}
-func (m *MultiExecResp_Response) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- return xxx_messageInfo_MultiExecResp_Response.Marshal(b, m, deterministic)
-}
-func (m *MultiExecResp_Response) XXX_Merge(src proto.Message) {
- xxx_messageInfo_MultiExecResp_Response.Merge(m, src)
-}
-func (m *MultiExecResp_Response) XXX_Size() int {
- return xxx_messageInfo_MultiExecResp_Response.Size(m)
-}
-func (m *MultiExecResp_Response) XXX_DiscardUnknown() {
- xxx_messageInfo_MultiExecResp_Response.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_MultiExecResp_Response proto.InternalMessageInfo
-
-func (m *MultiExecResp_Response) GetResponseCode() int32 {
- if m != nil && m.ResponseCode != nil {
- return *m.ResponseCode
- }
- return 0
-}
-
-func (m *MultiExecResp_Response) GetResp() *ExecResp {
- if m != nil {
- return m.Resp
+ return m.ExecutionStats
}
return nil
}
@@ -1786,7 +1725,7 @@
func (m *StoreFileReq) String() string { return proto.CompactTextString(m) }
func (*StoreFileReq) ProtoMessage() {}
func (*StoreFileReq) Descriptor() ([]byte, []int) {
- return fileDescriptor_8016d97e57513e7e, []int{11}
+ return fileDescriptor_8016d97e57513e7e, []int{10}
}
func (m *StoreFileReq) XXX_Unmarshal(b []byte) error {
@@ -1832,7 +1771,7 @@
func (m *StoreFileResp) String() string { return proto.CompactTextString(m) }
func (*StoreFileResp) ProtoMessage() {}
func (*StoreFileResp) Descriptor() ([]byte, []int) {
- return fileDescriptor_8016d97e57513e7e, []int{12}
+ return fileDescriptor_8016d97e57513e7e, []int{11}
}
func (m *StoreFileResp) XXX_Unmarshal(b []byte) error {
@@ -1872,7 +1811,7 @@
func (m *LookupFileReq) String() string { return proto.CompactTextString(m) }
func (*LookupFileReq) ProtoMessage() {}
func (*LookupFileReq) Descriptor() ([]byte, []int) {
- return fileDescriptor_8016d97e57513e7e, []int{13}
+ return fileDescriptor_8016d97e57513e7e, []int{12}
}
func (m *LookupFileReq) XXX_Unmarshal(b []byte) error {
@@ -1918,7 +1857,7 @@
func (m *LookupFileResp) String() string { return proto.CompactTextString(m) }
func (*LookupFileResp) ProtoMessage() {}
func (*LookupFileResp) Descriptor() ([]byte, []int) {
- return fileDescriptor_8016d97e57513e7e, []int{14}
+ return fileDescriptor_8016d97e57513e7e, []int{13}
}
func (m *LookupFileResp) XXX_Unmarshal(b []byte) error {
@@ -1956,7 +1895,7 @@
func (m *EmptyMessage) String() string { return proto.CompactTextString(m) }
func (*EmptyMessage) ProtoMessage() {}
func (*EmptyMessage) Descriptor() ([]byte, []int) {
- return fileDescriptor_8016d97e57513e7e, []int{15}
+ return fileDescriptor_8016d97e57513e7e, []int{14}
}
func (m *EmptyMessage) XXX_Unmarshal(b []byte) error {
@@ -1988,7 +1927,7 @@
func (m *HttpPortResponse) String() string { return proto.CompactTextString(m) }
func (*HttpPortResponse) ProtoMessage() {}
func (*HttpPortResponse) Descriptor() ([]byte, []int) {
- return fileDescriptor_8016d97e57513e7e, []int{16}
+ return fileDescriptor_8016d97e57513e7e, []int{15}
}
func (m *HttpPortResponse) XXX_Unmarshal(b []byte) error {
@@ -2034,10 +1973,8 @@
proto.RegisterType((*RequesterEnv)(nil), "devtools_goma.RequesterEnv")
proto.RegisterType((*ExecReq)(nil), "devtools_goma.ExecReq")
proto.RegisterType((*ExecReq_Input)(nil), "devtools_goma.ExecReq.Input")
- proto.RegisterType((*MultiExecReq)(nil), "devtools_goma.MultiExecReq")
+ proto.RegisterType((*ExecutionStats)(nil), "devtools_goma.ExecutionStats")
proto.RegisterType((*ExecResp)(nil), "devtools_goma.ExecResp")
- proto.RegisterType((*MultiExecResp)(nil), "devtools_goma.MultiExecResp")
- proto.RegisterType((*MultiExecResp_Response)(nil), "devtools_goma.MultiExecResp.Response")
proto.RegisterType((*StoreFileReq)(nil), "devtools_goma.StoreFileReq")
proto.RegisterType((*StoreFileResp)(nil), "devtools_goma.StoreFileResp")
proto.RegisterType((*LookupFileReq)(nil), "devtools_goma.LookupFileReq")
@@ -2046,164 +1983,168 @@
proto.RegisterType((*HttpPortResponse)(nil), "devtools_goma.HttpPortResponse")
}
-func init() { proto.RegisterFile("api/goma_data.proto", fileDescriptor_8016d97e57513e7e) }
+func init() {
+ proto.RegisterFile("api/goma_data.proto", fileDescriptor_8016d97e57513e7e)
+}
var fileDescriptor_8016d97e57513e7e = []byte{
- // 2482 bytes of a gzipped FileDescriptorProto
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x58, 0xfd, 0x6e, 0x1b, 0xc7,
- 0x11, 0x37, 0xbf, 0x24, 0x72, 0xf8, 0x21, 0x6a, 0xad, 0xd8, 0x67, 0xd9, 0x4e, 0x64, 0x2a, 0x4e,
- 0x54, 0xbb, 0x56, 0x6b, 0x25, 0x69, 0x12, 0x25, 0x6a, 0x43, 0x51, 0x94, 0x45, 0x8b, 0x22, 0xd9,
- 0xa5, 0x18, 0x37, 0x40, 0x81, 0xc3, 0xf1, 0x6e, 0x29, 0x1d, 0x74, 0xbc, 0x3b, 0xed, 0x1d, 0x15,
- 0x31, 0x40, 0x80, 0xbe, 0x43, 0xff, 0xea, 0x5b, 0x14, 0x05, 0xfa, 0x1c, 0x7d, 0x8d, 0x02, 0x7d,
- 0x87, 0xb6, 0xd8, 0xd9, 0x3d, 0x7e, 0x89, 0x94, 0x6d, 0x20, 0xfa, 0x43, 0xd8, 0x9d, 0x99, 0xdf,
- 0xec, 0xec, 0x7c, 0xed, 0x1c, 0xe1, 0xae, 0xe1, 0xdb, 0xbf, 0x39, 0xf3, 0xfa, 0x86, 0x6e, 0x19,
- 0xa1, 0xb1, 0xed, 0x73, 0x2f, 0xf4, 0x48, 0xde, 0x62, 0x57, 0xa1, 0xe7, 0x39, 0x81, 0x2e, 0x38,
- 0xa5, 0xff, 0xc6, 0x20, 0x7d, 0x68, 0x3b, 0x6c, 0xdf, 0xf1, 0xba, 0x64, 0x0f, 0x32, 0x5d, 0xc7,
- 0xeb, 0xea, 0xe1, 0xd0, 0x67, 0x5a, 0x6c, 0x23, 0xbe, 0x55, 0xd8, 0xd9, 0xd8, 0x9e, 0x92, 0xdf,
- 0x8e, 0x64, 0xb7, 0xc5, 0xbf, 0xd3, 0xa1, 0xcf, 0x68, 0xba, 0xab, 0x56, 0xe4, 0x1e, 0x2c, 0x79,
- 0xbd, 0x5e, 0xc0, 0x42, 0x0d, 0x36, 0x62, 0x5b, 0x09, 0xaa, 0x76, 0x44, 0x83, 0x65, 0xd3, 0x73,
- 0x43, 0xe6, 0x86, 0x5a, 0x76, 0x23, 0xb6, 0x95, 0xa3, 0xd1, 0x96, 0x3c, 0x84, 0x4c, 0xcf, 0x76,
- 0x98, 0x1e, 0xd8, 0x3f, 0x31, 0x6d, 0x0d, 0x41, 0x69, 0x41, 0x68, 0xdb, 0x3f, 0x31, 0xf2, 0x00,
- 0xd2, 0xe7, 0x46, 0x70, 0xae, 0x5f, 0xb0, 0xa1, 0xf6, 0xc1, 0x46, 0x62, 0x2b, 0x43, 0x97, 0xc5,
- 0xfe, 0x98, 0x0d, 0x4b, 0x35, 0x48, 0x47, 0xe7, 0x93, 0x35, 0x28, 0x1e, 0xd6, 0xea, 0x55, 0xbd,
- 0xd3, 0x68, 0xb7, 0xaa, 0x95, 0xda, 0x61, 0xad, 0x7a, 0x50, 0xbc, 0x43, 0xd2, 0x90, 0x14, 0xd4,
- 0x62, 0x8c, 0xe4, 0x21, 0x83, 0xfc, 0x93, 0xea, 0x69, 0xb9, 0x18, 0x27, 0x05, 0x00, 0xdc, 0x56,
- 0x8e, 0x3a, 0x8d, 0xe3, 0x62, 0xa2, 0xf4, 0x9f, 0x04, 0x64, 0x2b, 0x5e, 0xbf, 0x6f, 0xb8, 0x56,
- 0xdb, 0x67, 0x26, 0x21, 0x90, 0x74, 0x8d, 0xbe, 0xb8, 0x7e, 0x6c, 0x2b, 0x43, 0x71, 0x2d, 0x2e,
- 0x70, 0xc5, 0x78, 0x60, 0x7b, 0xae, 0x16, 0x47, 0x72, 0xb4, 0x15, 0x57, 0x0e, 0x0d, 0x7e, 0xc6,
- 0x42, 0x2d, 0x81, 0x0c, 0xb5, 0x13, 0x5a, 0xf0, 0x4e, 0x79, 0xbc, 0x13, 0xae, 0xc9, 0x47, 0x90,
- 0xed, 0xda, 0xae, 0xc1, 0x87, 0xba, 0xb8, 0x86, 0x96, 0x44, 0x57, 0x80, 0x24, 0x1d, 0x19, 0xc1,
- 0x39, 0xf9, 0x14, 0x56, 0xd8, 0xe5, 0xc0, 0xbe, 0x32, 0x1c, 0xe6, 0x86, 0x52, 0x28, 0xbb, 0x91,
- 0xd8, 0xca, 0xd1, 0xc2, 0x98, 0x8c, 0x82, 0x9b, 0x90, 0xb7, 0x58, 0x68, 0xd8, 0x0e, 0xb3, 0x74,
- 0xdb, 0xed, 0x79, 0x5a, 0x0a, 0x0f, 0xcf, 0x45, 0xc4, 0x9a, 0xdb, 0xf3, 0xc8, 0x36, 0xdc, 0x75,
- 0x3c, 0xd3, 0x70, 0x74, 0xd3, 0xeb, 0xfb, 0xb6, 0xc3, 0xb8, 0xee, 0x1b, 0xe1, 0xb9, 0xb6, 0x84,
- 0xa2, 0xab, 0xc8, 0xaa, 0x28, 0x4e, 0xcb, 0x08, 0xcf, 0x85, 0x7c, 0x30, 0x0c, 0x42, 0xd6, 0xd7,
- 0x6d, 0xd7, 0x74, 0x06, 0x16, 0x93, 0xf2, 0xcb, 0xe8, 0xf9, 0x55, 0xc9, 0xaa, 0x49, 0x0e, 0xca,
- 0x7f, 0x01, 0xf7, 0xcd, 0xeb, 0x6b, 0x7d, 0x1e, 0x26, 0x8d, 0x98, 0x35, 0xf3, 0xfa, 0xba, 0x7d,
- 0x03, 0xb6, 0x03, 0x1f, 0x28, 0x48, 0x8f, 0x1b, 0x7d, 0xf6, 0xa3, 0xc7, 0x2f, 0x24, 0x28, 0x83,
- 0x20, 0x65, 0xc3, 0x61, 0xc4, 0x9b, 0x31, 0xcd, 0xb1, 0xbb, 0x5c, 0x78, 0x10, 0x11, 0xb9, 0x49,
- 0xd3, 0xea, 0x92, 0x83, 0xf2, 0x98, 0x70, 0xfd, 0xbe, 0x48, 0x38, 0x90, 0xf1, 0x52, 0xdb, 0xd2,
- 0x0f, 0x50, 0x68, 0x0f, 0xba, 0x3e, 0xf7, 0xce, 0xb8, 0xd1, 0x8f, 0xe2, 0x8d, 0xca, 0x54, 0xbc,
- 0xc5, 0x7a, 0x36, 0x52, 0x32, 0xe6, 0x93, 0x91, 0x8a, 0xc2, 0x9b, 0x18, 0x87, 0xb7, 0xf4, 0xd7,
- 0x18, 0xe4, 0x4f, 0x3d, 0xcf, 0x31, 0xcf, 0x0d, 0xdb, 0x5d, 0xa8, 0x9a, 0x40, 0x72, 0x42, 0x27,
- 0xae, 0xe7, 0x69, 0x13, 0x21, 0xb6, 0x03, 0x9d, 0x5d, 0x33, 0x73, 0x10, 0x1a, 0x5d, 0x87, 0x61,
- 0xba, 0xa4, 0x69, 0xce, 0x0e, 0xaa, 0x23, 0x1a, 0x79, 0x02, 0xb9, 0x60, 0xd8, 0x77, 0x6c, 0x57,
- 0xb9, 0x50, 0xa6, 0x41, 0x56, 0xd1, 0x84, 0x2b, 0x4a, 0xff, 0x48, 0x00, 0x08, 0x04, 0x65, 0xc1,
- 0xc0, 0x09, 0xc9, 0x26, 0x64, 0xd9, 0xb5, 0x1d, 0xea, 0x41, 0x68, 0x84, 0x83, 0x00, 0x6b, 0x3c,
- 0xb5, 0x1b, 0x7f, 0xf1, 0x92, 0x82, 0x20, 0xb7, 0x91, 0x2a, 0xce, 0x0e, 0x42, 0xcb, 0x1b, 0x84,
- 0x7a, 0x77, 0xd0, 0xeb, 0x31, 0x8e, 0xc6, 0xe6, 0x68, 0x4e, 0x12, 0xf7, 0x91, 0xa6, 0x84, 0x18,
- 0xe7, 0x91, 0x50, 0x62, 0x24, 0xc4, 0x38, 0x57, 0x42, 0x7b, 0x90, 0x33, 0x65, 0x6d, 0xe9, 0x81,
- 0xcf, 0x4c, 0xbc, 0x44, 0x76, 0x67, 0x7d, 0xa6, 0xa7, 0x4c, 0x94, 0x1f, 0xcd, 0x9a, 0x13, 0xb5,
- 0xb8, 0x07, 0x10, 0x8c, 0xa2, 0xa5, 0xa5, 0x36, 0x12, 0x5b, 0xd9, 0x9d, 0xc7, 0x33, 0xe0, 0xe9,
- 0x70, 0xd2, 0x09, 0x00, 0xf9, 0x0a, 0x96, 0xbc, 0x41, 0xe8, 0x0f, 0x44, 0x16, 0x24, 0xb6, 0xe0,
- 0x46, 0x2f, 0x1b, 0xfb, 0x65, 0xbb, 0x89, 0x72, 0x54, 0xc9, 0xaf, 0xff, 0x0c, 0x4b, 0x92, 0x42,
- 0xd6, 0x01, 0x1b, 0x12, 0xb6, 0x84, 0x2c, 0xba, 0x77, 0xb4, 0x27, 0xcf, 0x21, 0x29, 0x7a, 0x9f,
- 0x96, 0xc3, 0x5b, 0xdd, 0x5f, 0xd0, 0x29, 0x29, 0x0a, 0x91, 0x67, 0xb3, 0x01, 0x15, 0xad, 0x21,
- 0xbd, 0x9b, 0xea, 0x19, 0x4e, 0xc0, 0xa6, 0xe3, 0x5a, 0xfa, 0x57, 0x02, 0xf2, 0x94, 0x5d, 0x0e,
- 0x58, 0x10, 0x32, 0x8e, 0xc5, 0x4c, 0x20, 0x69, 0x58, 0x16, 0x8f, 0x52, 0x49, 0xac, 0x85, 0x69,
- 0x83, 0x80, 0x71, 0x34, 0x4d, 0xa6, 0xd3, 0x68, 0x4f, 0x9e, 0xc1, 0xea, 0xb8, 0xec, 0xb9, 0x77,
- 0x3d, 0xd4, 0x6d, 0x4b, 0xb5, 0xa8, 0x95, 0x88, 0xd1, 0x12, 0xf4, 0x9a, 0x45, 0x4a, 0x90, 0x35,
- 0x7c, 0x5b, 0x8f, 0x3a, 0x9c, 0x88, 0x51, 0x6a, 0x37, 0xb6, 0x43, 0xc1, 0xf0, 0xed, 0xef, 0x55,
- 0x9f, 0x2b, 0x42, 0xc2, 0xb7, 0x2d, 0x4c, 0xb0, 0x14, 0x15, 0x4b, 0xb2, 0x06, 0x29, 0xce, 0x42,
- 0x3e, 0xd4, 0x96, 0x91, 0x26, 0x37, 0x22, 0x2b, 0xf0, 0xc1, 0xe1, 0xec, 0xca, 0x46, 0x6d, 0x69,
- 0xd9, 0x99, 0x04, 0x91, 0x2a, 0x9a, 0x68, 0xec, 0xdd, 0x81, 0xed, 0x58, 0xc2, 0xa6, 0x8c, 0xac,
- 0x4f, 0xdc, 0xd7, 0x2c, 0xf2, 0x21, 0x80, 0x65, 0xf7, 0x99, 0x2b, 0xe4, 0x02, 0x0c, 0x5b, 0x86,
- 0x4e, 0x50, 0x48, 0x15, 0x40, 0x64, 0xba, 0x1e, 0x84, 0x43, 0x47, 0x06, 0xa4, 0xb0, 0xf3, 0xc9,
- 0x8c, 0xe3, 0xa7, 0x3c, 0xb7, 0x2d, 0xaa, 0xa0, 0x2d, 0xa4, 0x69, 0xc6, 0x8f, 0x96, 0xa5, 0xa7,
- 0x50, 0x78, 0xe5, 0xf5, 0x8d, 0xf2, 0xf8, 0x82, 0x77, 0x61, 0xa5, 0xd2, 0xa1, 0xb4, 0xda, 0x38,
- 0xd5, 0xbf, 0xaf, 0xd2, 0x76, 0xad, 0xd9, 0x28, 0xc6, 0x4b, 0xfb, 0x90, 0x19, 0xc1, 0xc9, 0x2a,
- 0xe4, 0x3b, 0x8d, 0xe3, 0x46, 0xf3, 0x4d, 0x43, 0x6f, 0x9f, 0xfe, 0x50, 0xaf, 0x16, 0xef, 0x90,
- 0x15, 0xc8, 0xb6, 0x9a, 0xed, 0xda, 0x9f, 0x14, 0x21, 0x26, 0x64, 0xde, 0xd4, 0x1a, 0x07, 0xcd,
- 0x37, 0x6d, 0x45, 0x8a, 0xbf, 0x4e, 0xa6, 0x97, 0x8a, 0xcb, 0xa5, 0xbf, 0xc5, 0x21, 0x37, 0xb2,
- 0xab, 0xea, 0x5e, 0x89, 0x16, 0x23, 0x8c, 0x35, 0x4d, 0x59, 0xb9, 0xbf, 0x92, 0x2d, 0x46, 0x92,
- 0xb0, 0x87, 0x3d, 0x06, 0x90, 0xed, 0x1b, 0xf9, 0xcf, 0x90, 0x9f, 0x41, 0x0a, 0xb2, 0xd7, 0x20,
- 0x35, 0xe8, 0x1b, 0xc1, 0x85, 0xf6, 0x5c, 0xba, 0x1f, 0x37, 0xc2, 0xfd, 0x57, 0x8c, 0xdb, 0xbd,
- 0xa1, 0xae, 0x12, 0x7f, 0x47, 0x76, 0x0d, 0x49, 0x54, 0x29, 0xfd, 0x10, 0x32, 0x83, 0x80, 0xe9,
- 0xa8, 0x4b, 0xfb, 0x0c, 0x05, 0x44, 0xe2, 0xd4, 0xc5, 0x1e, 0xf3, 0xdd, 0x70, 0x9c, 0xae, 0x61,
- 0x5e, 0x68, 0x9f, 0x4b, 0x5e, 0xb4, 0x27, 0x4f, 0xa1, 0xa0, 0xb4, 0xab, 0x22, 0xd5, 0xbe, 0x40,
- 0xb3, 0xd4, 0x99, 0xaa, 0x8c, 0x45, 0xb7, 0x8e, 0x20, 0xba, 0xed, 0xfa, 0x83, 0x50, 0x17, 0x15,
- 0xa3, 0x7d, 0x2b, 0xbb, 0x75, 0xc4, 0xaa, 0x09, 0x8e, 0xa8, 0x91, 0xd2, 0xbf, 0x97, 0x61, 0x59,
- 0x96, 0xe2, 0xe5, 0x8d, 0x86, 0x21, 0x1a, 0xd4, 0x7b, 0x34, 0x8c, 0x22, 0x24, 0x0c, 0x7e, 0xa6,
- 0xc5, 0xf1, 0x28, 0xb1, 0x14, 0x14, 0xe6, 0x5e, 0x69, 0x09, 0x49, 0x61, 0xee, 0x95, 0xa0, 0x98,
- 0x3f, 0x5a, 0x98, 0xe6, 0x19, 0x2a, 0x96, 0x64, 0x07, 0x52, 0x68, 0xa7, 0x6a, 0x13, 0x8f, 0xe6,
- 0xb6, 0x89, 0xcb, 0x6d, 0xb4, 0x98, 0x4a, 0x51, 0xd1, 0x7a, 0xd9, 0xb5, 0x6f, 0xb8, 0x16, 0xb3,
- 0x74, 0x71, 0x64, 0x01, 0x0f, 0xc8, 0x46, 0xb4, 0x32, 0x3f, 0x9b, 0xe9, 0x5e, 0x2b, 0xef, 0xdb,
- 0xbd, 0x2a, 0x50, 0xe0, 0x51, 0xc6, 0xc8, 0x57, 0xfe, 0x43, 0xec, 0x33, 0x8f, 0x6e, 0x4b, 0x77,
- 0x9a, 0xe7, 0x53, 0x7d, 0xe3, 0x0d, 0xe4, 0x4c, 0xc3, 0x3c, 0x67, 0xba, 0xef, 0x39, 0xb6, 0x39,
- 0xd4, 0x3e, 0xc2, 0x8a, 0x29, 0x2d, 0xb8, 0x61, 0x45, 0x88, 0xb6, 0x50, 0x72, 0xb7, 0x58, 0x6f,
- 0x36, 0x8f, 0x3b, 0x2d, 0xbd, 0xdc, 0x38, 0xd0, 0xdb, 0xa7, 0x4d, 0x5a, 0xa5, 0x59, 0x73, 0xcc,
- 0x26, 0xdf, 0xc1, 0xf8, 0x24, 0x5d, 0x78, 0x78, 0x03, 0x8d, 0x7b, 0xb8, 0xc8, 0xb8, 0xaa, 0x7b,
- 0x45, 0x73, 0x7c, 0xb2, 0x02, 0x36, 0x21, 0x7f, 0xce, 0x78, 0x9f, 0x85, 0xb6, 0xa9, 0xf7, 0x3d,
- 0x8b, 0x69, 0x4f, 0x64, 0xae, 0x46, 0xc4, 0x13, 0xcf, 0x12, 0xc3, 0x5d, 0x2a, 0xe4, 0x86, 0xc9,
- 0xb4, 0x12, 0x32, 0xe5, 0x46, 0xcc, 0x10, 0xec, 0xda, 0x67, 0x66, 0xc8, 0x2c, 0x95, 0xe8, 0x98,
- 0x62, 0x81, 0xb6, 0x29, 0x67, 0x88, 0x88, 0x29, 0x13, 0x5e, 0x24, 0x59, 0x40, 0x7e, 0x0b, 0x6b,
- 0xb3, 0x18, 0xcb, 0xe6, 0x81, 0xf6, 0x31, 0x42, 0xc8, 0x34, 0xe4, 0xc0, 0xe6, 0x01, 0x79, 0x01,
- 0x24, 0x8c, 0xde, 0xf3, 0x68, 0xbe, 0xb1, 0xb4, 0xa7, 0x68, 0xc8, 0xea, 0x88, 0xa3, 0x66, 0x1b,
- 0x8b, 0x54, 0x61, 0x65, 0x2c, 0x2e, 0x92, 0x37, 0xd0, 0x3e, 0xc1, 0x98, 0xcf, 0x06, 0x6c, 0x6a,
- 0x48, 0xa0, 0x85, 0x70, 0x72, 0x1b, 0xac, 0x5f, 0x42, 0x0a, 0x13, 0xed, 0xd6, 0x97, 0x67, 0x72,
- 0x34, 0xce, 0x6d, 0xc4, 0x27, 0x46, 0x63, 0xf2, 0x72, 0x3c, 0x6c, 0xe7, 0x6f, 0x7f, 0x97, 0x22,
- 0xb9, 0x52, 0x17, 0xb2, 0x13, 0x91, 0x17, 0x03, 0xf5, 0x6c, 0xec, 0x8b, 0x31, 0xd1, 0xeb, 0x14,
- 0xb5, 0xd9, 0xa8, 0xff, 0x20, 0x07, 0x69, 0xe4, 0xc9, 0x7d, 0x82, 0x3c, 0x02, 0x6d, 0x16, 0xa6,
- 0xb7, 0x3b, 0x95, 0x4a, 0xb5, 0xdd, 0x2e, 0x26, 0x5f, 0x27, 0xd3, 0x66, 0xd1, 0x2a, 0xfd, 0x0c,
- 0xb9, 0x93, 0x81, 0x13, 0xda, 0x51, 0xb9, 0x6f, 0x41, 0x82, 0xb3, 0x4b, 0x2d, 0x86, 0x7e, 0xba,
- 0x37, 0x3f, 0x2b, 0xa9, 0x10, 0x99, 0x53, 0x0d, 0xf0, 0xde, 0xd5, 0x50, 0xfa, 0x27, 0x81, 0xb4,
- 0x7a, 0xf4, 0x7d, 0xf2, 0x12, 0x96, 0x38, 0x3e, 0xfe, 0xf8, 0xa8, 0x66, 0x77, 0x1e, 0x2c, 0x9c,
- 0x0e, 0xa8, 0x12, 0x24, 0xdf, 0x40, 0x8a, 0x71, 0xee, 0xc9, 0x81, 0xa8, 0xb0, 0xf3, 0x64, 0x3e,
- 0xc2, 0xc7, 0x45, 0x55, 0x08, 0xee, 0xc6, 0x9b, 0xc7, 0x54, 0x62, 0x88, 0x03, 0xf7, 0xbb, 0x86,
- 0xa5, 0x2b, 0x8b, 0x74, 0xce, 0x8c, 0xc0, 0x73, 0x75, 0x53, 0x64, 0x7e, 0x02, 0xd5, 0xfd, 0x7a,
- 0x91, 0xba, 0x7d, 0xc3, 0x52, 0xd7, 0xa2, 0x08, 0xaa, 0x78, 0x16, 0xdb, 0x5d, 0x56, 0x8f, 0x11,
- 0x5d, 0xeb, 0xce, 0x61, 0x8b, 0xea, 0xea, 0xdb, 0x41, 0x60, 0xbb, 0x67, 0xb2, 0x07, 0xe3, 0x97,
- 0x44, 0x86, 0xe6, 0x14, 0x51, 0xa6, 0xd8, 0x53, 0x28, 0x44, 0x42, 0xd2, 0x1c, 0xec, 0x52, 0x19,
- 0x1a, 0x41, 0xa5, 0x3e, 0xa1, 0x0b, 0xaf, 0xa0, 0xf7, 0x59, 0x10, 0x18, 0x67, 0x4c, 0x0d, 0xde,
- 0x39, 0x24, 0x9e, 0x48, 0x1a, 0xd1, 0xe1, 0x59, 0xcf, 0xe3, 0x26, 0xd3, 0x83, 0xd0, 0xe3, 0x6c,
- 0xb2, 0x2c, 0xf5, 0x9e, 0xc7, 0xf5, 0x81, 0xdb, 0x37, 0x42, 0xf3, 0x9c, 0x59, 0x72, 0xa4, 0x9e,
- 0x1a, 0x7e, 0x3e, 0x46, 0x60, 0x5b, 0xe0, 0xc6, 0x15, 0x7b, 0xe8, 0xf1, 0x4e, 0x84, 0xc1, 0x99,
- 0x9b, 0xc1, 0x8b, 0x77, 0x3b, 0x20, 0x1a, 0x64, 0x0a, 0x93, 0x67, 0x7c, 0xfa, 0xb6, 0x33, 0xa2,
- 0x41, 0xa0, 0x0f, 0x2f, 0xdf, 0xed, 0x98, 0x71, 0xab, 0x0e, 0xb4, 0xe2, 0xe4, 0x51, 0xcf, 0xdf,
- 0x76, 0xd4, 0xb8, 0xe9, 0x07, 0xe2, 0x31, 0x96, 0x0d, 0x5a, 0x7e, 0xe5, 0x62, 0x99, 0x23, 0x41,
- 0xd4, 0xf2, 0x77, 0x11, 0xf3, 0xdc, 0x0e, 0xb5, 0x87, 0x98, 0x24, 0x9b, 0x8b, 0x92, 0x04, 0x2b,
- 0xb8, 0xed, 0x0d, 0xb8, 0xc9, 0x94, 0x86, 0x23, 0x3b, 0x24, 0x7b, 0xf0, 0x70, 0x5c, 0x36, 0x37,
- 0x27, 0xc2, 0x75, 0x3c, 0x50, 0x1b, 0x89, 0x54, 0x66, 0x46, 0xc3, 0x6d, 0xb8, 0x3b, 0x03, 0x0a,
- 0xed, 0x3e, 0xc3, 0xa9, 0x22, 0x46, 0x57, 0xa7, 0x06, 0xc9, 0x53, 0xbb, 0xcf, 0x48, 0x1d, 0x36,
- 0x67, 0x0f, 0x89, 0xbe, 0x0b, 0x39, 0xf3, 0xb9, 0x67, 0x4a, 0xfc, 0x67, 0x88, 0xff, 0x68, 0x7a,
- 0x10, 0x55, 0xdf, 0x88, 0x52, 0x0e, 0xb5, 0x35, 0xe0, 0xe3, 0x05, 0xda, 0x44, 0x38, 0x1c, 0xcf,
- 0xb0, 0xa4, 0xba, 0xcf, 0x51, 0xdd, 0xc6, 0x3c, 0x75, 0x87, 0x4a, 0x10, 0xf5, 0xfd, 0x1e, 0x1e,
- 0xcd, 0xe8, 0xe3, 0xbe, 0xa9, 0x9b, 0x86, 0xe3, 0x48, 0x3d, 0x5f, 0xa0, 0x1e, 0x6d, 0x4a, 0x0f,
- 0xf5, 0xcd, 0x8a, 0xe1, 0x38, 0x88, 0x3f, 0x82, 0x27, 0x33, 0x78, 0x4c, 0x0b, 0xce, 0x02, 0xdf,
- 0x73, 0x03, 0x26, 0x95, 0xfc, 0x0e, 0x95, 0x3c, 0x9e, 0x52, 0x22, 0xac, 0xa0, 0x4a, 0x0a, 0x35,
- 0x7d, 0x07, 0x8f, 0xe7, 0x58, 0x22, 0x87, 0x62, 0xd4, 0xf2, 0x25, 0x6a, 0x79, 0x30, 0x6b, 0xca,
- 0xbe, 0x90, 0xb8, 0xe5, 0x2e, 0x01, 0x73, 0x95, 0x82, 0xaf, 0xe6, 0xdf, 0xa5, 0xcd, 0xdc, 0xdb,
- 0xf0, 0x3f, 0x1a, 0x76, 0x28, 0xf1, 0x5f, 0xcf, 0xc7, 0xbf, 0x31, 0xec, 0xf0, 0x16, 0x3c, 0x67,
- 0xe6, 0x95, 0xc4, 0xef, 0xce, 0xc7, 0x53, 0x66, 0x5e, 0xdd, 0xe2, 0x01, 0xdf, 0xe0, 0x91, 0x1f,
- 0xbf, 0x99, 0xef, 0x81, 0x96, 0x90, 0x58, 0x10, 0x0d, 0x35, 0x2f, 0x33, 0xd7, 0x12, 0x1d, 0x0d,
- 0xb5, 0x7c, 0x3b, 0x27, 0x1a, 0x38, 0xe8, 0xb6, 0xa4, 0xd4, 0x02, 0x5b, 0xa4, 0x26, 0x3e, 0x70,
- 0xa5, 0x96, 0xbd, 0x39, 0xb6, 0xa0, 0x16, 0x3a, 0x70, 0x51, 0xc3, 0x1f, 0x6e, 0x78, 0x03, 0xbf,
- 0x82, 0x7a, 0xb6, 0x6b, 0x07, 0xe7, 0xcc, 0xd2, 0x0e, 0x71, 0x68, 0x98, 0x56, 0x20, 0x3e, 0x40,
- 0x0e, 0x95, 0xc0, 0x1c, 0x13, 0x50, 0xc1, 0xb8, 0xfa, 0x5f, 0x2d, 0xd0, 0x50, 0x99, 0xa8, 0xf4,
- 0x79, 0x1a, 0x8c, 0xae, 0xc7, 0x43, 0x66, 0x69, 0x47, 0x88, 0xd7, 0x6e, 0xe0, 0xcb, 0x92, 0x4f,
- 0xbe, 0x86, 0x07, 0xf3, 0xe0, 0xf2, 0xb9, 0xab, 0x21, 0xf8, 0xde, 0x0d, 0x30, 0xbe, 0x71, 0xa4,
- 0xbc, 0xc0, 0x7d, 0xa3, 0xdb, 0xbf, 0x46, 0xf8, 0xfa, 0x4d, 0xf7, 0x8d, 0xae, 0xff, 0x25, 0x68,
- 0x8b, 0x22, 0xa0, 0x1d, 0x23, 0xfa, 0x83, 0xb9, 0xce, 0x9f, 0x73, 0x6b, 0x09, 0xbc, 0xb0, 0x1d,
- 0x87, 0x59, 0x5a, 0x7d, 0xce, 0xad, 0x11, 0x7b, 0x8c, 0xfc, 0x39, 0x39, 0x24, 0x3e, 0xd0, 0x27,
- 0xde, 0x68, 0xf1, 0x81, 0xdb, 0xc2, 0x2f, 0xac, 0xe9, 0x1c, 0x52, 0xb3, 0x89, 0x7c, 0x74, 0x43,
- 0x3e, 0x2c, 0xbd, 0x80, 0xcc, 0xe8, 0xd5, 0x27, 0x4b, 0x10, 0x6f, 0x1e, 0x17, 0xef, 0x10, 0x0d,
- 0xb2, 0xfb, 0xe5, 0x03, 0x9d, 0x56, 0xff, 0xd8, 0xa9, 0xb6, 0x4f, 0x8b, 0xff, 0x8b, 0xfe, 0x62,
- 0xa5, 0x0a, 0xac, 0xcd, 0x7b, 0xd5, 0x49, 0x16, 0xa2, 0x77, 0xbd, 0x78, 0x87, 0x7c, 0x08, 0xeb,
- 0x9d, 0x46, 0xbb, 0xd3, 0x6a, 0x35, 0xe9, 0x69, 0xf5, 0x40, 0xaf, 0x34, 0x4f, 0x5a, 0xb5, 0x7a,
- 0x95, 0xea, 0x87, 0xf5, 0xf2, 0xab, 0x76, 0x31, 0x56, 0xea, 0xa8, 0xb9, 0x4d, 0x76, 0x7d, 0x92,
- 0x83, 0x74, 0xa3, 0xa9, 0x57, 0xca, 0x95, 0x23, 0xf1, 0x6d, 0x9a, 0x87, 0xcc, 0x49, 0xf5, 0x44,
- 0x6d, 0xf1, 0xcb, 0x54, 0x8c, 0x64, 0xe5, 0x57, 0x55, 0x45, 0x8a, 0x93, 0x7b, 0x40, 0xea, 0xcd,
- 0x4a, 0xb9, 0xae, 0x37, 0x3b, 0xa7, 0xad, 0xce, 0xa9, 0xa2, 0x27, 0x5e, 0x27, 0xd3, 0xf7, 0x8a,
- 0xf7, 0x5f, 0x27, 0xd3, 0xf7, 0x8b, 0x9a, 0x1a, 0xdb, 0xfe, 0x1e, 0x83, 0xfc, 0xc4, 0xdc, 0x16,
- 0xf8, 0xa4, 0x0c, 0xe9, 0xa8, 0xed, 0xe1, 0xf4, 0x06, 0x3b, 0x4f, 0x67, 0x1e, 0xa6, 0x29, 0xf9,
- 0xed, 0xa8, 0xfb, 0xd1, 0x11, 0x6c, 0xfd, 0xcf, 0x90, 0x8e, 0xa8, 0x62, 0xc2, 0x18, 0x75, 0x51,
- 0x9c, 0x88, 0xe2, 0xe8, 0xf3, 0x5c, 0x44, 0x44, 0xdf, 0x3c, 0x87, 0xa4, 0xd8, 0xe3, 0xb4, 0x74,
- 0x73, 0xac, 0x8d, 0x8e, 0xa2, 0x28, 0x54, 0xfa, 0x4b, 0x0c, 0x72, 0xf8, 0x04, 0xcb, 0xde, 0x7b,
- 0x39, 0xfa, 0xb1, 0x46, 0xce, 0x9a, 0x6f, 0xf9, 0xb1, 0xe6, 0x17, 0x99, 0x36, 0x9f, 0x41, 0x7e,
- 0xc2, 0x82, 0xc0, 0x9f, 0x9a, 0xda, 0x63, 0xd3, 0x3f, 0x68, 0x7b, 0x90, 0xaf, 0x7b, 0xde, 0xc5,
- 0xc0, 0x8f, 0xcc, 0x5d, 0x2c, 0xfb, 0xcb, 0x18, 0xb7, 0x07, 0x85, 0xc9, 0x03, 0x03, 0x7f, 0xe4,
- 0xa0, 0xf8, 0x3b, 0x38, 0xa8, 0x54, 0x80, 0x5c, 0xb5, 0xef, 0x87, 0x43, 0x35, 0xfd, 0x95, 0x3e,
- 0x81, 0xe2, 0x51, 0x18, 0xfa, 0x2d, 0x8f, 0x87, 0xa3, 0xa0, 0x12, 0x48, 0xfa, 0x1e, 0x0f, 0xe5,
- 0x8f, 0x8c, 0x14, 0xd7, 0xff, 0x0f, 0x00, 0x00, 0xff, 0xff, 0x2d, 0xeb, 0x43, 0x58, 0x93, 0x18,
- 0x00, 0x00,
+ // 2516 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x58, 0xdd, 0x52, 0x23, 0xc7,
+ 0xf5, 0x5f, 0x49, 0x08, 0xa4, 0xa3, 0x0f, 0x44, 0x2f, 0x5e, 0x66, 0x61, 0xf1, 0xb2, 0xc2, 0x6b,
+ 0xf3, 0x5f, 0xff, 0x17, 0x67, 0x71, 0x1c, 0xdb, 0xd8, 0x24, 0x06, 0x21, 0x16, 0x2d, 0x42, 0x92,
+ 0x5b, 0xc8, 0xc4, 0x37, 0x99, 0x1a, 0xcd, 0xb4, 0x60, 0x8a, 0xd1, 0xcc, 0xd0, 0x33, 0x62, 0x91,
+ 0xab, 0x52, 0x95, 0x07, 0xc8, 0x5d, 0xae, 0xf2, 0x1a, 0x79, 0x8a, 0xdc, 0xe5, 0x35, 0x52, 0x95,
+ 0x67, 0x48, 0x25, 0xd5, 0xa7, 0x7b, 0xf4, 0x85, 0xc4, 0xee, 0x56, 0x85, 0x0b, 0x6a, 0xfa, 0x7c,
+ 0xf5, 0xe9, 0xf3, 0xd5, 0xbf, 0x16, 0x3c, 0x34, 0x7c, 0xfb, 0x8b, 0x0b, 0xaf, 0x6b, 0xe8, 0x96,
+ 0x11, 0x1a, 0xdb, 0x3e, 0xf7, 0x42, 0x8f, 0xe4, 0x2c, 0x76, 0x13, 0x7a, 0x9e, 0x13, 0xe8, 0x82,
+ 0xb3, 0xfa, 0xf4, 0xc2, 0xf3, 0x2e, 0x1c, 0xf6, 0x05, 0x32, 0xdb, 0xbd, 0xce, 0x17, 0xa1, 0xdd,
+ 0x65, 0x41, 0x68, 0x74, 0x7d, 0x29, 0x5f, 0xfc, 0x73, 0x1c, 0x52, 0x47, 0xb6, 0xc3, 0x0e, 0x1c,
+ 0xaf, 0x4d, 0xf6, 0x20, 0xdd, 0x76, 0xbc, 0xb6, 0x1e, 0xf6, 0x7d, 0xa6, 0xc5, 0x36, 0xe2, 0x5b,
+ 0xf9, 0x9d, 0x8d, 0xed, 0x31, 0x83, 0xdb, 0x91, 0xec, 0xb6, 0xf8, 0x77, 0xd6, 0xf7, 0x19, 0x4d,
+ 0xb5, 0xd5, 0x17, 0x79, 0x04, 0xf3, 0x5e, 0xa7, 0x13, 0xb0, 0x50, 0x83, 0x8d, 0xd8, 0x56, 0x82,
+ 0xaa, 0x15, 0xd1, 0x60, 0xc1, 0xf4, 0xdc, 0x90, 0xb9, 0xa1, 0x96, 0xd9, 0x88, 0x6d, 0x65, 0x69,
+ 0xb4, 0x24, 0x6b, 0x90, 0xee, 0xd8, 0x0e, 0xd3, 0x03, 0xfb, 0x17, 0xa6, 0x2d, 0xa3, 0x52, 0x4a,
+ 0x10, 0x9a, 0xf6, 0x2f, 0x8c, 0x3c, 0x86, 0xd4, 0xa5, 0x11, 0x5c, 0xea, 0x57, 0xac, 0xaf, 0x7d,
+ 0xb4, 0x91, 0xd8, 0x4a, 0xd3, 0x05, 0xb1, 0x3e, 0x61, 0xfd, 0xe2, 0x39, 0xa4, 0xa2, 0xfd, 0xc9,
+ 0x32, 0x14, 0x8e, 0x2a, 0xd5, 0xb2, 0xde, 0xaa, 0x35, 0x1b, 0xe5, 0x52, 0xe5, 0xa8, 0x52, 0x3e,
+ 0x2c, 0x3c, 0x20, 0x29, 0x98, 0x13, 0xd4, 0x42, 0x8c, 0xe4, 0x20, 0x8d, 0xfc, 0xd3, 0xf2, 0xd9,
+ 0x7e, 0x21, 0x4e, 0xf2, 0x00, 0xb8, 0x2c, 0x1d, 0xb7, 0x6a, 0x27, 0x85, 0x04, 0xc9, 0x42, 0x0a,
+ 0xd7, 0xb4, 0x7c, 0x54, 0x48, 0x16, 0xff, 0x95, 0x80, 0x4c, 0xc9, 0xeb, 0x76, 0x0d, 0xd7, 0x6a,
+ 0xfa, 0xcc, 0x24, 0x04, 0xe6, 0x5c, 0xa3, 0x2b, 0x82, 0x11, 0xdb, 0x4a, 0x53, 0xfc, 0x16, 0xc7,
+ 0xb9, 0x61, 0x3c, 0xb0, 0x3d, 0x57, 0x8b, 0x23, 0x39, 0x5a, 0x8a, 0x00, 0x84, 0x06, 0xbf, 0x60,
+ 0xa1, 0x96, 0x40, 0x86, 0x5a, 0x09, 0x2b, 0x78, 0xc2, 0x1c, 0x9e, 0x10, 0xbf, 0xc9, 0x53, 0xc8,
+ 0xb4, 0x6d, 0xd7, 0xe0, 0x7d, 0x5d, 0x1c, 0x4a, 0x9b, 0xc3, 0xc0, 0x80, 0x24, 0x1d, 0x1b, 0xc1,
+ 0x25, 0xf9, 0x0c, 0x16, 0xd9, 0x75, 0xcf, 0xbe, 0x31, 0x1c, 0xe6, 0x86, 0x52, 0x28, 0xb3, 0x91,
+ 0xd8, 0xca, 0xd2, 0xfc, 0x90, 0x8c, 0x82, 0x9b, 0x90, 0xb3, 0x58, 0x68, 0xd8, 0x0e, 0xb3, 0x74,
+ 0xdb, 0xed, 0x78, 0x5a, 0x12, 0x37, 0xcf, 0x46, 0xc4, 0x8a, 0xdb, 0xf1, 0xc8, 0x36, 0x3c, 0x74,
+ 0x3c, 0xd3, 0x70, 0x74, 0xd3, 0xeb, 0xfa, 0xb6, 0xc3, 0xb8, 0xee, 0x1b, 0xe1, 0xa5, 0x36, 0x8f,
+ 0xa2, 0x4b, 0xc8, 0x2a, 0x29, 0x4e, 0xc3, 0x08, 0x2f, 0x85, 0x7c, 0xd0, 0x0f, 0x42, 0xd6, 0xd5,
+ 0x6d, 0xd7, 0x74, 0x7a, 0x16, 0x93, 0xf2, 0x0b, 0x98, 0x87, 0x25, 0xc9, 0xaa, 0x48, 0x0e, 0xca,
+ 0x7f, 0x05, 0x2b, 0xe6, 0xed, 0xad, 0x3e, 0x4d, 0x27, 0x85, 0x3a, 0xcb, 0xe6, 0xed, 0x6d, 0xf3,
+ 0x8e, 0xda, 0x0e, 0x7c, 0xa4, 0x54, 0x3a, 0xdc, 0xe8, 0xb2, 0xb7, 0x1e, 0xbf, 0x92, 0x4a, 0x69,
+ 0x54, 0x52, 0x3e, 0x1c, 0x45, 0xbc, 0x09, 0xd7, 0x1c, 0xbb, 0xcd, 0x45, 0x04, 0x51, 0x23, 0x3b,
+ 0xea, 0x5a, 0x55, 0x72, 0x50, 0x1e, 0xcb, 0xaf, 0xdb, 0x15, 0xe5, 0x07, 0x32, 0x5f, 0x6a, 0x59,
+ 0xfc, 0x19, 0xf2, 0xcd, 0x5e, 0xdb, 0xe7, 0xde, 0x05, 0x37, 0xba, 0x51, 0xbe, 0xd1, 0x98, 0xca,
+ 0xb7, 0xf8, 0x9e, 0xcc, 0x94, 0xcc, 0xf9, 0x68, 0xa6, 0xa2, 0xf4, 0x26, 0x86, 0xe9, 0x2d, 0xfe,
+ 0x25, 0x06, 0xb9, 0x33, 0xcf, 0x73, 0xcc, 0x4b, 0xc3, 0x76, 0x67, 0x9a, 0x26, 0x30, 0x37, 0x62,
+ 0x13, 0xbf, 0xa7, 0x59, 0x13, 0x29, 0xb6, 0x03, 0x9d, 0xdd, 0x32, 0xb3, 0x17, 0x1a, 0x6d, 0x87,
+ 0x61, 0xb9, 0xa4, 0x68, 0xd6, 0x0e, 0xca, 0x03, 0x1a, 0x79, 0x06, 0xd9, 0xa0, 0xdf, 0x75, 0x6c,
+ 0x57, 0x85, 0x50, 0x96, 0x41, 0x46, 0xd1, 0x44, 0x28, 0x8a, 0x7f, 0x4b, 0x00, 0x08, 0x0d, 0xca,
+ 0x82, 0x9e, 0x13, 0x92, 0x4d, 0xc8, 0xb0, 0x5b, 0x3b, 0xd4, 0x83, 0xd0, 0x08, 0x7b, 0x01, 0x76,
+ 0x7c, 0x72, 0x37, 0xfe, 0xf2, 0x15, 0x05, 0x41, 0x6e, 0x22, 0x55, 0xec, 0x1d, 0x84, 0x96, 0xd7,
+ 0x0b, 0xf5, 0x76, 0xaf, 0xd3, 0x61, 0x1c, 0x9d, 0xcd, 0xd2, 0xac, 0x24, 0x1e, 0x20, 0x4d, 0x09,
+ 0x31, 0xce, 0x23, 0xa1, 0xc4, 0x40, 0x88, 0x71, 0xae, 0x84, 0xf6, 0x20, 0x6b, 0xca, 0xde, 0xd2,
+ 0x03, 0x9f, 0x99, 0x78, 0x88, 0xcc, 0xce, 0xea, 0xc4, 0x84, 0x19, 0x69, 0x3f, 0x9a, 0x31, 0x47,
+ 0x7a, 0x71, 0x0f, 0x20, 0x18, 0x64, 0x4b, 0x4b, 0x6e, 0x24, 0xb6, 0x32, 0x3b, 0xeb, 0x13, 0xca,
+ 0xe3, 0xe9, 0xa4, 0x23, 0x0a, 0xe4, 0x1b, 0x98, 0xf7, 0x7a, 0xa1, 0xdf, 0x13, 0x55, 0x90, 0xd8,
+ 0x82, 0x3b, 0x93, 0x6d, 0x18, 0x97, 0xed, 0x3a, 0xca, 0x51, 0x25, 0xbf, 0xfa, 0x47, 0x98, 0x97,
+ 0x14, 0xb2, 0x0a, 0x38, 0x9e, 0x70, 0x24, 0x64, 0x30, 0xbc, 0x83, 0x35, 0xf9, 0x1c, 0xe6, 0xc4,
+ 0x24, 0xd4, 0xb2, 0x78, 0xaa, 0x95, 0x19, 0x73, 0x93, 0xa2, 0x10, 0x79, 0x31, 0x99, 0x50, 0x31,
+ 0x1a, 0x52, 0xbb, 0xc9, 0x8e, 0xe1, 0x04, 0x6c, 0x3c, 0xaf, 0xc5, 0x7f, 0x24, 0x20, 0x47, 0xd9,
+ 0x75, 0x8f, 0x05, 0x21, 0xe3, 0xd8, 0xcc, 0x04, 0xe6, 0x0c, 0xcb, 0xe2, 0x51, 0x29, 0x89, 0x6f,
+ 0xe1, 0x5a, 0x2f, 0x60, 0x1c, 0x5d, 0x93, 0xe5, 0x34, 0x58, 0x93, 0x17, 0xb0, 0x34, 0x6c, 0x7b,
+ 0xee, 0xdd, 0xf6, 0x75, 0xdb, 0x52, 0x23, 0x6a, 0x31, 0x62, 0x34, 0x04, 0xbd, 0x62, 0x91, 0x22,
+ 0x64, 0x0c, 0xdf, 0xd6, 0xa3, 0x09, 0x27, 0x72, 0x94, 0xdc, 0x8d, 0xed, 0x50, 0x30, 0x7c, 0xfb,
+ 0x27, 0x35, 0xe7, 0x0a, 0x90, 0xf0, 0x6d, 0x0b, 0x0b, 0x2c, 0x49, 0xc5, 0x27, 0x59, 0x86, 0x24,
+ 0x67, 0x21, 0xef, 0x6b, 0x0b, 0x48, 0x93, 0x0b, 0x51, 0x15, 0x78, 0x3f, 0x71, 0x76, 0x63, 0xa3,
+ 0xb5, 0x94, 0x9c, 0x4c, 0x82, 0x48, 0x15, 0x4d, 0x8c, 0xf9, 0x76, 0xcf, 0x76, 0x2c, 0xe1, 0x53,
+ 0x5a, 0xf6, 0x27, 0xae, 0x2b, 0x16, 0xf9, 0x18, 0xc0, 0xb2, 0xbb, 0xcc, 0x15, 0x72, 0x01, 0xa6,
+ 0x2d, 0x4d, 0x47, 0x28, 0xa4, 0x0c, 0x20, 0x2a, 0x5d, 0x0f, 0xc2, 0xbe, 0x23, 0x13, 0x92, 0xdf,
+ 0xf9, 0x74, 0x22, 0xf0, 0x63, 0x91, 0xdb, 0x16, 0x5d, 0xd0, 0x14, 0xd2, 0x34, 0xed, 0x47, 0x9f,
+ 0xc5, 0xe7, 0x90, 0x7f, 0xed, 0x75, 0x8d, 0xfd, 0xe1, 0x01, 0x1f, 0xc2, 0x62, 0xa9, 0x45, 0x69,
+ 0xb9, 0x76, 0xa6, 0xff, 0x54, 0xa6, 0xcd, 0x4a, 0xbd, 0x56, 0x88, 0x17, 0x0f, 0x20, 0x3d, 0x50,
+ 0x27, 0x4b, 0x90, 0x6b, 0xd5, 0x4e, 0x6a, 0xf5, 0xf3, 0x9a, 0xde, 0x3c, 0xfb, 0xb9, 0x5a, 0x2e,
+ 0x3c, 0x20, 0x8b, 0x90, 0x69, 0xd4, 0x9b, 0x95, 0xdf, 0x2b, 0x42, 0x4c, 0xc8, 0x9c, 0x57, 0x6a,
+ 0x87, 0xf5, 0xf3, 0xa6, 0x22, 0xc5, 0xdf, 0xcc, 0xa5, 0xe6, 0x0b, 0x0b, 0xc5, 0xbf, 0xc6, 0x21,
+ 0x3b, 0xf0, 0xab, 0xec, 0xde, 0x88, 0x11, 0x23, 0x9c, 0x35, 0x4d, 0xd9, 0xb9, 0xff, 0x27, 0x47,
+ 0x8c, 0x24, 0xe1, 0x0c, 0x5b, 0x07, 0x90, 0xe3, 0x1b, 0xf9, 0x2f, 0x90, 0x9f, 0x46, 0x0a, 0xb2,
+ 0x97, 0x21, 0xd9, 0xeb, 0x1a, 0xc1, 0x95, 0xf6, 0xb9, 0x0c, 0x3f, 0x2e, 0x44, 0xf8, 0x6f, 0x18,
+ 0xb7, 0x3b, 0x7d, 0x5d, 0x15, 0xfe, 0x8e, 0x9c, 0x1a, 0x92, 0xa8, 0x4a, 0x7a, 0x0d, 0xd2, 0xbd,
+ 0x80, 0xe9, 0x68, 0x4b, 0xfb, 0x12, 0x05, 0x44, 0xe1, 0x54, 0xc5, 0x1a, 0xeb, 0xdd, 0x70, 0x9c,
+ 0xb6, 0x61, 0x5e, 0x69, 0xbf, 0x96, 0xbc, 0x68, 0x4d, 0x9e, 0x43, 0x5e, 0x59, 0x57, 0x4d, 0xaa,
+ 0x7d, 0x85, 0x6e, 0xa9, 0x3d, 0x55, 0x1b, 0x8b, 0x69, 0x1d, 0xa9, 0xe8, 0xb6, 0xeb, 0xf7, 0x42,
+ 0x5d, 0x74, 0x8c, 0xf6, 0xbd, 0x9c, 0xd6, 0x11, 0xab, 0x22, 0x38, 0xa2, 0x47, 0x8a, 0xff, 0x5c,
+ 0x80, 0x05, 0xd9, 0x8a, 0xd7, 0x77, 0x06, 0x86, 0x18, 0x50, 0x1f, 0x30, 0x30, 0x0a, 0x90, 0x30,
+ 0xf8, 0x85, 0x16, 0xc7, 0xad, 0xc4, 0xa7, 0xa0, 0x30, 0xf7, 0x46, 0x4b, 0x48, 0x0a, 0x73, 0x6f,
+ 0x04, 0xc5, 0x7c, 0x6b, 0x61, 0x99, 0xa7, 0xa9, 0xf8, 0x24, 0x3b, 0x90, 0x44, 0x3f, 0xd5, 0x98,
+ 0x78, 0x32, 0x75, 0x4c, 0x5c, 0x6f, 0xa3, 0xc7, 0x54, 0x8a, 0x8a, 0xd1, 0xcb, 0x6e, 0x7d, 0xc3,
+ 0xb5, 0x98, 0xa5, 0x8b, 0x2d, 0xf3, 0xb8, 0x41, 0x26, 0xa2, 0xed, 0xf3, 0x8b, 0x89, 0xe9, 0xb5,
+ 0xf8, 0xa1, 0xd3, 0xab, 0x04, 0x79, 0x1e, 0x55, 0x8c, 0xbc, 0xe5, 0x3f, 0xc6, 0x39, 0xf3, 0xe4,
+ 0xbe, 0x72, 0xa7, 0x39, 0x3e, 0x36, 0x37, 0xce, 0x21, 0x6b, 0x1a, 0xe6, 0x25, 0xd3, 0x7d, 0xcf,
+ 0xb1, 0xcd, 0xbe, 0xf6, 0x14, 0x3b, 0xa6, 0x38, 0xe3, 0x84, 0x25, 0x21, 0xda, 0x40, 0xc9, 0xdd,
+ 0x42, 0xb5, 0x5e, 0x3f, 0x69, 0x35, 0xf4, 0xfd, 0xda, 0xa1, 0xde, 0x3c, 0xab, 0xd3, 0x32, 0xcd,
+ 0x98, 0x43, 0x36, 0xf9, 0x01, 0x86, 0x3b, 0xe9, 0x22, 0xc2, 0x1b, 0xe8, 0xdc, 0xda, 0x2c, 0xe7,
+ 0xca, 0xee, 0x0d, 0xcd, 0xf2, 0xd1, 0x0e, 0xd8, 0x84, 0xdc, 0x25, 0xe3, 0x5d, 0x16, 0xda, 0xa6,
+ 0xde, 0xf5, 0x2c, 0xa6, 0x3d, 0x93, 0xb5, 0x1a, 0x11, 0x4f, 0x3d, 0x4b, 0x40, 0xbd, 0x64, 0xc8,
+ 0x0d, 0x93, 0x69, 0x45, 0x64, 0xca, 0x85, 0xc0, 0x10, 0xec, 0xd6, 0x67, 0x66, 0xc8, 0x2c, 0x55,
+ 0xe8, 0x58, 0x62, 0x81, 0xb6, 0x29, 0x31, 0x44, 0xc4, 0x94, 0x05, 0x2f, 0x8a, 0x2c, 0x20, 0xbf,
+ 0x82, 0xe5, 0x49, 0x1d, 0xcb, 0xe6, 0x81, 0xf6, 0x09, 0xaa, 0x90, 0x71, 0x95, 0x43, 0x9b, 0x07,
+ 0xe4, 0x25, 0x90, 0x30, 0xba, 0xcf, 0x23, 0x7c, 0x63, 0x69, 0xcf, 0xd1, 0x91, 0xa5, 0x01, 0x47,
+ 0x61, 0x1b, 0x8b, 0x94, 0x61, 0x71, 0x28, 0x2e, 0x8a, 0x37, 0xd0, 0x3e, 0xc5, 0x9c, 0x4f, 0x26,
+ 0x6c, 0x0c, 0x24, 0xd0, 0x7c, 0x38, 0xba, 0x0c, 0x56, 0xaf, 0x21, 0x89, 0x85, 0x76, 0xef, 0xcd,
+ 0x33, 0x0a, 0x94, 0xb3, 0x1b, 0xf1, 0x11, 0xa0, 0x4c, 0x5e, 0x0d, 0xa1, 0x77, 0xee, 0xfe, 0x7b,
+ 0x29, 0x92, 0x2b, 0xb6, 0x21, 0x33, 0x92, 0x79, 0x01, 0xaf, 0x27, 0x73, 0x5f, 0x88, 0x89, 0x59,
+ 0xa7, 0xa8, 0xf5, 0x5a, 0xf5, 0x67, 0x09, 0xab, 0x91, 0x27, 0xd7, 0x09, 0xf2, 0x04, 0xb4, 0x49,
+ 0x35, 0xbd, 0xd9, 0x2a, 0x95, 0xca, 0xcd, 0x66, 0x61, 0xee, 0xcd, 0x5c, 0xca, 0x2c, 0x58, 0xc5,
+ 0xbf, 0xc7, 0x20, 0x2f, 0xef, 0x39, 0xdb, 0x73, 0x05, 0xda, 0x08, 0xc8, 0x4f, 0xf0, 0x98, 0x45,
+ 0x14, 0x01, 0x4b, 0x78, 0xa8, 0x0f, 0x5e, 0x2c, 0x78, 0xdd, 0x89, 0xf6, 0x97, 0x6f, 0x9a, 0xed,
+ 0xe8, 0x4d, 0xb3, 0x7d, 0x16, 0x49, 0xd0, 0x15, 0x36, 0x62, 0x8e, 0x87, 0x03, 0x06, 0xf9, 0x03,
+ 0xac, 0x0f, 0xed, 0x8a, 0x2b, 0xcf, 0x61, 0x22, 0xf5, 0x43, 0xdb, 0xf1, 0x77, 0xda, 0x5e, 0x1b,
+ 0x18, 0x28, 0x45, 0xfa, 0x03, 0x66, 0xf1, 0xdf, 0x04, 0x52, 0x0a, 0x40, 0xf8, 0xe4, 0x15, 0xcc,
+ 0x73, 0x04, 0x12, 0xca, 0xe3, 0xc7, 0x33, 0x91, 0x06, 0x55, 0x82, 0xe4, 0x3b, 0x48, 0x32, 0xce,
+ 0x3d, 0x09, 0xae, 0xf2, 0x3b, 0xcf, 0xa6, 0x6b, 0xf8, 0xf8, 0x51, 0x16, 0x82, 0xbb, 0xf1, 0xfa,
+ 0x09, 0x95, 0x3a, 0xc4, 0x81, 0x95, 0xb6, 0x61, 0xe9, 0xaa, 0x9f, 0x74, 0xce, 0x8c, 0x00, 0x4f,
+ 0x69, 0x49, 0x10, 0x99, 0xdf, 0xf9, 0xff, 0x59, 0xe6, 0x0e, 0x0c, 0x4b, 0xf5, 0x24, 0x45, 0xa5,
+ 0x92, 0x67, 0xb1, 0xdd, 0x05, 0x75, 0xb1, 0xd1, 0xe5, 0xf6, 0x14, 0xb6, 0xe8, 0xd4, 0xae, 0x1d,
+ 0x04, 0xb6, 0x7b, 0x21, 0xe7, 0x39, 0xbe, 0x4a, 0xd2, 0x34, 0xab, 0x88, 0xb2, 0x5c, 0x9f, 0x43,
+ 0x3e, 0x12, 0x92, 0xee, 0xe0, 0xc4, 0x4b, 0xd3, 0x48, 0x55, 0xda, 0x13, 0xb6, 0xf0, 0x08, 0x7a,
+ 0x97, 0x05, 0x81, 0x71, 0xc1, 0x14, 0x88, 0xcf, 0x22, 0xf1, 0x54, 0xd2, 0x88, 0x0e, 0x2f, 0x3a,
+ 0x1e, 0x37, 0x99, 0x1e, 0x84, 0x1e, 0x67, 0xa3, 0x2d, 0xae, 0x77, 0x3c, 0xae, 0xf7, 0xdc, 0xae,
+ 0x11, 0x9a, 0x97, 0xcc, 0x92, 0xf0, 0x7c, 0x0c, 0x48, 0x7d, 0x82, 0x8a, 0x4d, 0xa1, 0x37, 0xec,
+ 0xfe, 0x23, 0x8f, 0xb7, 0x22, 0x1d, 0xc4, 0xef, 0x0c, 0x5e, 0xbe, 0xdf, 0x06, 0x11, 0x28, 0xca,
+ 0x8f, 0xee, 0xf1, 0xd9, 0xbb, 0xf6, 0x88, 0x40, 0x45, 0x17, 0x5e, 0xbd, 0xdf, 0x36, 0xc3, 0xb1,
+ 0x1f, 0x68, 0x85, 0xd1, 0xad, 0x3e, 0x7f, 0xd7, 0x56, 0xc3, 0x0b, 0x24, 0x10, 0x17, 0xbb, 0x1c,
+ 0xf6, 0xf2, 0xfd, 0x8c, 0x23, 0x03, 0x09, 0x62, 0x2e, 0xfc, 0x10, 0x31, 0x2f, 0xed, 0x50, 0x5b,
+ 0xc3, 0x22, 0xd9, 0x9c, 0x55, 0x24, 0x38, 0x0d, 0x9a, 0x5e, 0x8f, 0x9b, 0x4c, 0x59, 0x38, 0xb6,
+ 0x43, 0xb2, 0x07, 0x6b, 0xc3, 0x91, 0x7f, 0x17, 0x5d, 0xae, 0xe2, 0x86, 0xda, 0x40, 0xa4, 0x34,
+ 0x01, 0x33, 0xb7, 0xe1, 0xe1, 0x84, 0x92, 0xe8, 0x45, 0x44, 0x28, 0x31, 0xba, 0x34, 0x06, 0x4a,
+ 0x45, 0x97, 0x91, 0x2a, 0x6c, 0x4e, 0x6e, 0x12, 0xbd, 0x31, 0x39, 0xf3, 0xb9, 0x67, 0x4a, 0xfd,
+ 0x2f, 0x51, 0xff, 0xe9, 0x38, 0xa8, 0x55, 0xef, 0x4d, 0x29, 0x87, 0xd6, 0x6a, 0xf0, 0xc9, 0x0c,
+ 0x6b, 0x22, 0x1d, 0x8e, 0x67, 0xc8, 0xd1, 0x80, 0x98, 0x27, 0x46, 0x37, 0xa6, 0x99, 0x3b, 0x52,
+ 0x82, 0x68, 0xef, 0xb7, 0xf0, 0x64, 0xc2, 0x1e, 0xf7, 0x4d, 0xdd, 0x34, 0x1c, 0x47, 0xda, 0xf9,
+ 0x0a, 0xed, 0x68, 0x63, 0x76, 0xa8, 0x6f, 0x96, 0x0c, 0xc7, 0x41, 0xfd, 0x63, 0x78, 0x36, 0xa1,
+ 0x8f, 0x65, 0xc1, 0x59, 0xe0, 0x7b, 0x6e, 0xc0, 0xa4, 0x91, 0xdf, 0xa0, 0x91, 0xf5, 0x31, 0x23,
+ 0xc2, 0x0b, 0xaa, 0xa4, 0xd0, 0xd2, 0x0f, 0xb0, 0x3e, 0xc5, 0x13, 0x09, 0xb0, 0xd1, 0xca, 0xd7,
+ 0x68, 0xe5, 0xf1, 0xa4, 0x2b, 0x07, 0x42, 0xe2, 0x9e, 0xb3, 0x04, 0xcc, 0x55, 0x06, 0xbe, 0x99,
+ 0x7e, 0x96, 0x26, 0x73, 0xef, 0xd3, 0x7f, 0x6b, 0xd8, 0x72, 0x94, 0x6b, 0xdf, 0x4e, 0xd7, 0x3f,
+ 0x37, 0xec, 0xf0, 0x1e, 0x7d, 0xce, 0xcc, 0x1b, 0xa9, 0xbf, 0x3b, 0x5d, 0x9f, 0x32, 0xf3, 0xe6,
+ 0x9e, 0x08, 0xf8, 0x06, 0x8f, 0xe2, 0xf8, 0xdd, 0xf4, 0x08, 0x34, 0x84, 0xc4, 0x8c, 0x6c, 0x28,
+ 0xec, 0xcd, 0x5c, 0x4b, 0x4c, 0x34, 0xb4, 0xf2, 0xfd, 0x94, 0x6c, 0x20, 0x68, 0x6e, 0x48, 0xa9,
+ 0x19, 0xbe, 0x48, 0x4b, 0xbc, 0xe7, 0x4a, 0x2b, 0x7b, 0x53, 0x7c, 0x41, 0x2b, 0xb4, 0xe7, 0xa2,
+ 0x85, 0xdf, 0xdd, 0x89, 0x06, 0xbe, 0xa8, 0x3a, 0xb6, 0x6b, 0x07, 0x97, 0xcc, 0xd2, 0x8e, 0x10,
+ 0x80, 0x8c, 0x1b, 0x10, 0x8f, 0x99, 0x23, 0x25, 0x30, 0xc5, 0x05, 0x34, 0x30, 0xec, 0xfe, 0xd7,
+ 0x33, 0x2c, 0x94, 0x46, 0x3a, 0x7d, 0x9a, 0x05, 0xa3, 0xed, 0xf1, 0x90, 0x59, 0xda, 0x31, 0xea,
+ 0x6b, 0x77, 0xf4, 0xf7, 0x25, 0x9f, 0x7c, 0x0b, 0x8f, 0xa7, 0xa9, 0xcb, 0xeb, 0xae, 0x82, 0xca,
+ 0x8f, 0xee, 0x28, 0xe3, 0x1d, 0x47, 0xf6, 0x67, 0x84, 0x6f, 0x70, 0xfa, 0x37, 0xa8, 0xbe, 0x7a,
+ 0x37, 0x7c, 0x83, 0xe3, 0x7f, 0x0d, 0xda, 0xac, 0x0c, 0x68, 0x27, 0xa8, 0xfd, 0xd1, 0xd4, 0xe0,
+ 0x4f, 0x39, 0xb5, 0x54, 0xbc, 0xb2, 0x1d, 0x87, 0x59, 0x5a, 0x75, 0xca, 0xa9, 0x51, 0xf7, 0x04,
+ 0xf9, 0x53, 0x6a, 0x48, 0xc0, 0x87, 0x91, 0x3b, 0x5a, 0x3c, 0x96, 0x1b, 0xf8, 0x5a, 0x1b, 0xaf,
+ 0x21, 0x85, 0xbe, 0xe5, 0xa5, 0x2b, 0x1e, 0xd1, 0x47, 0xb0, 0x38, 0x06, 0x89, 0xc2, 0x40, 0xfb,
+ 0x11, 0x61, 0xc5, 0xfa, 0x94, 0x81, 0x3d, 0x84, 0x52, 0x34, 0xcf, 0xc6, 0xd6, 0xc5, 0x97, 0x90,
+ 0x1e, 0xa0, 0x07, 0x32, 0x0f, 0xf1, 0xfa, 0x49, 0xe1, 0x01, 0xd1, 0x20, 0x73, 0xb0, 0x7f, 0xa8,
+ 0xd3, 0xf2, 0x8f, 0xad, 0x72, 0xf3, 0xac, 0xf0, 0x9f, 0xe8, 0x2f, 0x56, 0x2c, 0xc1, 0xf2, 0x34,
+ 0x74, 0x40, 0x32, 0x10, 0xe1, 0x83, 0xc2, 0x03, 0xf2, 0x31, 0xac, 0xb6, 0x6a, 0xcd, 0x56, 0xa3,
+ 0x51, 0xa7, 0x67, 0xe5, 0x43, 0xbd, 0x54, 0x3f, 0x6d, 0x54, 0xaa, 0x65, 0xaa, 0x1f, 0x55, 0xf7,
+ 0x5f, 0x37, 0x0b, 0xb1, 0x62, 0x4b, 0x61, 0x49, 0x79, 0x7b, 0x90, 0x2c, 0xa4, 0x6a, 0x75, 0xbd,
+ 0xb4, 0x5f, 0x3a, 0x16, 0xef, 0xe5, 0x1c, 0xa4, 0x4f, 0xcb, 0xa7, 0x6a, 0x89, 0xaf, 0x65, 0x01,
+ 0x13, 0xf7, 0x5f, 0x97, 0x15, 0x29, 0x4e, 0x1e, 0x01, 0xa9, 0xd6, 0x4b, 0xfb, 0x55, 0xbd, 0xde,
+ 0x3a, 0x6b, 0xb4, 0xce, 0x14, 0x3d, 0xf1, 0x66, 0x2e, 0xf5, 0xa8, 0xb0, 0xf2, 0x66, 0x2e, 0xb5,
+ 0x52, 0xd0, 0x14, 0x94, 0xfc, 0x53, 0x0c, 0xb2, 0x78, 0x2f, 0xca, 0x81, 0x78, 0x3d, 0xf8, 0x35,
+ 0x26, 0x86, 0xa0, 0xfb, 0x1d, 0xbf, 0xc6, 0xdc, 0x7d, 0x5c, 0xc1, 0x07, 0x3f, 0xae, 0x8a, 0x2f,
+ 0x20, 0x37, 0xe2, 0x41, 0xe0, 0x8f, 0xc1, 0xf2, 0xd8, 0xf8, 0xef, 0xd7, 0x1e, 0xe4, 0xaa, 0x9e,
+ 0x77, 0xd5, 0xf3, 0x23, 0x77, 0x67, 0xcb, 0xfe, 0x6f, 0x9c, 0xdb, 0x83, 0xfc, 0xe8, 0x86, 0x81,
+ 0x3f, 0x08, 0x50, 0xfc, 0x3d, 0x02, 0x54, 0xcc, 0x43, 0xb6, 0xdc, 0xf5, 0xc3, 0xbe, 0x82, 0x64,
+ 0xc5, 0x4f, 0xa1, 0x70, 0x1c, 0x86, 0x7e, 0xc3, 0xe3, 0x61, 0x74, 0xfb, 0xe0, 0xef, 0x9b, 0x1e,
+ 0x0f, 0xe5, 0xaf, 0x88, 0x14, 0xbf, 0xff, 0x1b, 0x00, 0x00, 0xff, 0xff, 0x6a, 0xbb, 0xbc, 0x18,
+ 0xa3, 0x18, 0x00, 0x00,
}
diff --git a/proto/api/goma_data.proto b/proto/api/goma_data.proto
index d7e0345..296bd72 100644
--- a/proto/api/goma_data.proto
+++ b/proto/api/goma_data.proto
@@ -1,6 +1,4 @@
-// Copyright 2010 The Goma Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
+// Copyright 2010 Google LLC. All Rights Reserved.
//
// new proto definitions for goma v2
@@ -8,21 +6,29 @@
syntax = "proto2";
+import "google/protobuf/timestamp.proto";
+
package devtools_goma;
// persistent data
// hash_key = sha256(serialized FileBlob)
//
-// for small file (< 2MB)
+// for small file (< 2MB) embedded
// blob_type=FILE, !has_offset(), has_content()
// has_file_size(), hash_key_size() == 0
+//
// for large file
// blob_type=FILE_META, !has_offset(), !has_content()
// has_file_size(), hash_key_size() > 0
// for each hash_key(i)
// blob_type=FILE_CHUNK, has_offset(), has_content(),
// has_file_size(), hash_key_size() == 0
+//
+// for small file (< 2MB), stored as blob_type=FILE in file server with
+// key=hash_key[0]
+// blob_type=FILE_REF, !hash_offset(), !has_content()
+// has_file_size(), hash_key_size() == 1
message FileBlob {
enum BlobType {
FILE_UNSPECIFIED = 0;
@@ -30,6 +36,7 @@
FILE_META = 2;
FILE_CHUNK = 3;
// ARCHIVE = 4;
+ FILE_REF = 5;
}
required BlobType blob_type = 1;
@@ -183,7 +190,7 @@
// e.g. "os":"linux".
//
// Inspired from swarming bot dimensions.
- // https://cs.chromium.org/chromium/infra/luci/appengine/swarming/proto/bots.proto
+ // https://chromium.googlesource.com/infra/luci/luci-py/+/effbcfcafa96e0840189a98ac485586ba2c2ceb6/appengine/swarming/proto/config/bots.proto
repeated string dimensions = 10;
// Implementation Note: `WINDOWS` is defined on Win, so we have to add
@@ -295,12 +302,17 @@
reserved 99;
}
-message MultiExecReq {
- repeated ExecReq req = 1;
+// Stats of a single RBE execution. This is a subset of
+// https://github.com/bazelbuild/remote-apis/blob/178b756a22d441d8d06873a70bcd0ef01d876467/build/bazel/remote/execution/v2/remote_execution.proto#L789-L819
+message ExecutionStats {
+ // When the worker started executing the action command.
+ optional google.protobuf.Timestamp execution_start_timestamp = 1;
- optional RequesterInfo requester_info = 10;
+ // When the worker completed executing the action command.
+ optional google.protobuf.Timestamp execution_completed_timestamp = 2;
}
+// NEXT ID TO USE: 82
message ExecResp {
enum ExecError {
OK = 0;
@@ -368,18 +380,12 @@
optional bool compiler_proxy_local_killed = 76;
optional int32 compiler_proxy_exec_request_retry = 80;
-
+ // Execution stats collected from RBE
+ optional ExecutionStats execution_stats = 81;
// 99 was used in experimental phase.
reserved 99;
}
-message MultiExecResp {
- repeated group Response = 1 {
- optional int32 response_code = 2;
- optional ExecResp resp = 3;
- }
-}
-
// FileService Interface
message StoreFileReq {
diff --git a/proto/api/goma_log.pb.go b/proto/api/goma_log.pb.go
index 63981d9..b420986 100644
--- a/proto/api/goma_log.pb.go
+++ b/proto/api/goma_log.pb.go
@@ -1597,7 +1597,9 @@
proto.RegisterType((*OSInfo_MacInfo)(nil), "devtools_goma.OSInfo.MacInfo")
}
-func init() { proto.RegisterFile("api/goma_log.proto", fileDescriptor_663523c6ed595628) }
+func init() {
+ proto.RegisterFile("api/goma_log.proto", fileDescriptor_663523c6ed595628)
+}
var fileDescriptor_663523c6ed595628 = []byte{
// 2573 bytes of a gzipped FileDescriptorProto
diff --git a/proto/auth/acl.pb.go b/proto/auth/acl.pb.go
index 82706d1..f027435 100644
--- a/proto/auth/acl.pb.go
+++ b/proto/auth/acl.pb.go
@@ -164,7 +164,9 @@
proto.RegisterType((*ACL)(nil), "auth.ACL")
}
-func init() { proto.RegisterFile("auth/acl.proto", fileDescriptor_91b80568fa5a4647) }
+func init() {
+ proto.RegisterFile("auth/acl.proto", fileDescriptor_91b80568fa5a4647)
+}
var fileDescriptor_91b80568fa5a4647 = []byte{
// 220 bytes of a gzipped FileDescriptorProto
diff --git a/proto/auth/auth.pb.go b/proto/auth/auth.pb.go
index eeaf347..1bc2bd5 100644
--- a/proto/auth/auth.pb.go
+++ b/proto/auth/auth.pb.go
@@ -196,7 +196,9 @@
proto.RegisterType((*AuthResp)(nil), "auth.AuthResp")
}
-func init() { proto.RegisterFile("auth/auth.proto", fileDescriptor_712ec48c1eaf43a2) }
+func init() {
+ proto.RegisterFile("auth/auth.proto", fileDescriptor_712ec48c1eaf43a2)
+}
var fileDescriptor_712ec48c1eaf43a2 = []byte{
// 293 bytes of a gzipped FileDescriptorProto
diff --git a/proto/auth/auth_service.pb.go b/proto/auth/auth_service.pb.go
index 3adfe19..b7db01d 100644
--- a/proto/auth/auth_service.pb.go
+++ b/proto/auth/auth_service.pb.go
@@ -24,7 +24,9 @@
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
-func init() { proto.RegisterFile("auth/auth_service.proto", fileDescriptor_eea8da14b90d2639) }
+func init() {
+ proto.RegisterFile("auth/auth_service.proto", fileDescriptor_eea8da14b90d2639)
+}
var fileDescriptor_eea8da14b90d2639 = []byte{
// 96 bytes of a gzipped FileDescriptorProto
diff --git a/proto/auth/authdb.pb.go b/proto/auth/authdb.pb.go
index e75f053..1791606 100644
--- a/proto/auth/authdb.pb.go
+++ b/proto/auth/authdb.pb.go
@@ -111,7 +111,9 @@
proto.RegisterType((*CheckMembershipResp)(nil), "auth.CheckMembershipResp")
}
-func init() { proto.RegisterFile("auth/authdb.proto", fileDescriptor_555942e2cbf784d7) }
+func init() {
+ proto.RegisterFile("auth/authdb.proto", fileDescriptor_555942e2cbf784d7)
+}
var fileDescriptor_555942e2cbf784d7 = []byte{
// 129 bytes of a gzipped FileDescriptorProto
diff --git a/proto/auth/authdb_service.pb.go b/proto/auth/authdb_service.pb.go
index 30bb448..6d1a7a3 100644
--- a/proto/auth/authdb_service.pb.go
+++ b/proto/auth/authdb_service.pb.go
@@ -24,7 +24,9 @@
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
-func init() { proto.RegisterFile("auth/authdb_service.proto", fileDescriptor_2b553d7f571c1da7) }
+func init() {
+ proto.RegisterFile("auth/authdb_service.proto", fileDescriptor_2b553d7f571c1da7)
+}
var fileDescriptor_2b553d7f571c1da7 = []byte{
// 115 bytes of a gzipped FileDescriptorProto
diff --git a/proto/backend/backend.pb.go b/proto/backend/backend.pb.go
index a904729..5acfb10 100644
--- a/proto/backend/backend.pb.go
+++ b/proto/backend/backend.pb.go
@@ -503,7 +503,9 @@
proto.RegisterType((*BackendConfig)(nil), "backend.BackendConfig")
}
-func init() { proto.RegisterFile("backend/backend.proto", fileDescriptor_b81549028379a959) }
+func init() {
+ proto.RegisterFile("backend/backend.proto", fileDescriptor_b81549028379a959)
+}
var fileDescriptor_b81549028379a959 = []byte{
// 466 bytes of a gzipped FileDescriptorProto
diff --git a/proto/cache/cache.pb.go b/proto/cache/cache.pb.go
index 3f08425..4cdcc43 100644
--- a/proto/cache/cache.pb.go
+++ b/proto/cache/cache.pb.go
@@ -247,7 +247,9 @@
proto.RegisterType((*PutResp)(nil), "cache.PutResp")
}
-func init() { proto.RegisterFile("cache/cache.proto", fileDescriptor_dd209d76f5b70ea3) }
+func init() {
+ proto.RegisterFile("cache/cache.proto", fileDescriptor_dd209d76f5b70ea3)
+}
var fileDescriptor_dd209d76f5b70ea3 = []byte{
// 203 bytes of a gzipped FileDescriptorProto
diff --git a/proto/cache/cache_service.pb.go b/proto/cache/cache_service.pb.go
index 5987fcb..7ac0a25 100644
--- a/proto/cache/cache_service.pb.go
+++ b/proto/cache/cache_service.pb.go
@@ -24,7 +24,9 @@
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
-func init() { proto.RegisterFile("cache/cache_service.proto", fileDescriptor_7933906ce719ddba) }
+func init() {
+ proto.RegisterFile("cache/cache_service.proto", fileDescriptor_7933906ce719ddba)
+}
var fileDescriptor_7933906ce719ddba = []byte{
// 112 bytes of a gzipped FileDescriptorProto
diff --git a/proto/command/command.pb.go b/proto/command/command.pb.go
index 48447c6..d24030d 100644
--- a/proto/command/command.pb.go
+++ b/proto/command/command.pb.go
@@ -1127,7 +1127,9 @@
proto.RegisterType((*ConfigResp)(nil), "command.ConfigResp")
}
-func init() { proto.RegisterFile("command/command.proto", fileDescriptor_ce77433d29a243f3) }
+func init() {
+ proto.RegisterFile("command/command.proto", fileDescriptor_ce77433d29a243f3)
+}
var fileDescriptor_ce77433d29a243f3 = []byte{
// 1215 bytes of a gzipped FileDescriptorProto
diff --git a/proto/command/command_service.pb.go b/proto/command/command_service.pb.go
index 49b757c..41558fa 100644
--- a/proto/command/command_service.pb.go
+++ b/proto/command/command_service.pb.go
@@ -20,7 +20,9 @@
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
-func init() { proto.RegisterFile("command/command_service.proto", fileDescriptor_927f43bcc3a37434) }
+func init() {
+ proto.RegisterFile("command/command_service.proto", fileDescriptor_927f43bcc3a37434)
+}
var fileDescriptor_927f43bcc3a37434 = []byte{
// 58 bytes of a gzipped FileDescriptorProto
diff --git a/proto/command/package_opts.pb.go b/proto/command/package_opts.pb.go
index e921881..11ec41b 100644
--- a/proto/command/package_opts.pb.go
+++ b/proto/command/package_opts.pb.go
@@ -76,7 +76,9 @@
proto.RegisterType((*PackageOpts)(nil), "command.PackageOpts")
}
-func init() { proto.RegisterFile("command/package_opts.proto", fileDescriptor_290ec2fb95a58d08) }
+func init() {
+ proto.RegisterFile("command/package_opts.proto", fileDescriptor_290ec2fb95a58d08)
+}
var fileDescriptor_290ec2fb95a58d08 = []byte{
// 169 bytes of a gzipped FileDescriptorProto
diff --git a/proto/command/setup.pb.go b/proto/command/setup.pb.go
index 2be2276..2beac56 100644
--- a/proto/command/setup.pb.go
+++ b/proto/command/setup.pb.go
@@ -297,7 +297,9 @@
proto.RegisterType((*Setup)(nil), "command.Setup")
}
-func init() { proto.RegisterFile("command/setup.proto", fileDescriptor_b38a5ba41df99617) }
+func init() {
+ proto.RegisterFile("command/setup.proto", fileDescriptor_b38a5ba41df99617)
+}
var fileDescriptor_b38a5ba41df99617 = []byte{
// 374 bytes of a gzipped FileDescriptorProto
diff --git a/proto/exec/exec_service.pb.go b/proto/exec/exec_service.pb.go
index c1d507d..3bbac5a 100644
--- a/proto/exec/exec_service.pb.go
+++ b/proto/exec/exec_service.pb.go
@@ -85,7 +85,9 @@
proto.RegisterEnum("devtools_goma.ExecServiceApplicationError", ExecServiceApplicationError_name, ExecServiceApplicationError_value)
}
-func init() { proto.RegisterFile("exec/exec_service.proto", fileDescriptor_86aa5ae33157a9d0) }
+func init() {
+ proto.RegisterFile("exec/exec_service.proto", fileDescriptor_86aa5ae33157a9d0)
+}
var fileDescriptor_86aa5ae33157a9d0 = []byte{
// 281 bytes of a gzipped FileDescriptorProto
diff --git a/proto/execlog/log_service.pb.go b/proto/execlog/log_service.pb.go
index f10d5ac..d8aa29d 100644
--- a/proto/execlog/log_service.pb.go
+++ b/proto/execlog/log_service.pb.go
@@ -25,7 +25,9 @@
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
-func init() { proto.RegisterFile("execlog/log_service.proto", fileDescriptor_a47cf8dc6db25b78) }
+func init() {
+ proto.RegisterFile("execlog/log_service.proto", fileDescriptor_a47cf8dc6db25b78)
+}
var fileDescriptor_a47cf8dc6db25b78 = []byte{
// 131 bytes of a gzipped FileDescriptorProto
diff --git a/proto/file/file_service.pb.go b/proto/file/file_service.pb.go
index b2b7902..5d6410d 100644
--- a/proto/file/file_service.pb.go
+++ b/proto/file/file_service.pb.go
@@ -25,7 +25,9 @@
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
-func init() { proto.RegisterFile("file/file_service.proto", fileDescriptor_f713fbe7eb11f137) }
+func init() {
+ proto.RegisterFile("file/file_service.proto", fileDescriptor_f713fbe7eb11f137)
+}
var fileDescriptor_f713fbe7eb11f137 = []byte{
// 153 bytes of a gzipped FileDescriptorProto
diff --git a/proto/google/protobuf/timestamp.proto b/proto/google/protobuf/timestamp.proto
index eafb3fa..cd35786 100644
--- a/proto/google/protobuf/timestamp.proto
+++ b/proto/google/protobuf/timestamp.proto
@@ -40,17 +40,19 @@
option java_multiple_files = true;
option objc_class_prefix = "GPB";
-// A Timestamp represents a point in time independent of any time zone
-// or calendar, represented as seconds and fractions of seconds at
-// nanosecond resolution in UTC Epoch time. It is encoded using the
-// Proleptic Gregorian Calendar which extends the Gregorian calendar
-// backwards to year one. It is encoded assuming all minutes are 60
-// seconds long, i.e. leap seconds are "smeared" so that no leap second
-// table is needed for interpretation. Range is from
-// 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z.
-// By restricting to that range, we ensure that we can convert to
-// and from RFC 3339 date strings.
-// See [https://www.ietf.org/rfc/rfc3339.txt](https://www.ietf.org/rfc/rfc3339.txt).
+// A Timestamp represents a point in time independent of any time zone or local
+// calendar, encoded as a count of seconds and fractions of seconds at
+// nanosecond resolution. The count is relative to an epoch at UTC midnight on
+// January 1, 1970, in the proleptic Gregorian calendar which extends the
+// Gregorian calendar backwards to year one.
+//
+// All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap
+// second table is needed for interpretation, using a [24-hour linear
+// smear](https://developers.google.com/time/smear).
+//
+// The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By
+// restricting to that range, we ensure that we can convert to and from [RFC
+// 3339](https://www.ietf.org/rfc/rfc3339.txt) date strings.
//
// # Examples
//
@@ -111,17 +113,18 @@
// 01:30 UTC on January 15, 2017.
//
// In JavaScript, one can convert a Date object to this format using the
-// standard [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString]
+// standard
+// [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString)
// method. In Python, a standard `datetime.datetime` object can be converted
-// to this format using [`strftime`](https://docs.python.org/2/library/time.html#time.strftime)
-// with the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one
-// can use the Joda Time's [`ISODateTimeFormat.dateTime()`](
-// http://www.joda.org/joda-time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime--
+// to this format using
+// [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with
+// the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use
+// the Joda Time's [`ISODateTimeFormat.dateTime()`](
+// http://www.joda.org/joda-time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime%2D%2D
// ) to obtain a formatter capable of generating timestamps in this format.
//
//
message Timestamp {
-
// Represents seconds of UTC time since Unix epoch
// 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to
// 9999-12-31T23:59:59Z inclusive.
diff --git a/proto/nsjail/config.pb.go b/proto/nsjail/config.pb.go
index b7c0187..d2592c3 100644
--- a/proto/nsjail/config.pb.go
+++ b/proto/nsjail/config.pb.go
@@ -1270,7 +1270,9 @@
proto.RegisterType((*NsJailConfig)(nil), "nsjail.NsJailConfig")
}
-func init() { proto.RegisterFile("nsjail/config.proto", fileDescriptor_82b7e3129c410694) }
+func init() {
+ proto.RegisterFile("nsjail/config.proto", fileDescriptor_82b7e3129c410694)
+}
var fileDescriptor_82b7e3129c410694 = []byte{
// 2041 bytes of a gzipped FileDescriptorProto
diff --git a/proto/settings/settings.pb.go b/proto/settings/settings.pb.go
index bd07047..7abcf49 100644
--- a/proto/settings/settings.pb.go
+++ b/proto/settings/settings.pb.go
@@ -168,7 +168,9 @@
proto.RegisterType((*SettingsResp)(nil), "settings.SettingsResp")
}
-func init() { proto.RegisterFile("settings/settings.proto", fileDescriptor_a4bfd59e429426d0) }
+func init() {
+ proto.RegisterFile("settings/settings.proto", fileDescriptor_a4bfd59e429426d0)
+}
var fileDescriptor_a4bfd59e429426d0 = []byte{
// 208 bytes of a gzipped FileDescriptorProto
diff --git a/proto/settings/settings_service.pb.go b/proto/settings/settings_service.pb.go
index 38aa3f5..a51ab92 100644
--- a/proto/settings/settings_service.pb.go
+++ b/proto/settings/settings_service.pb.go
@@ -24,7 +24,9 @@
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
-func init() { proto.RegisterFile("settings/settings_service.proto", fileDescriptor_f19be0f58c8239c5) }
+func init() {
+ proto.RegisterFile("settings/settings_service.proto", fileDescriptor_f19be0f58c8239c5)
+}
var fileDescriptor_f19be0f58c8239c5 = []byte{
// 101 bytes of a gzipped FileDescriptorProto
diff --git a/remoteexec/adapter.go b/remoteexec/adapter.go
index e7d39d3..13280f9 100644
--- a/remoteexec/adapter.go
+++ b/remoteexec/adapter.go
@@ -74,6 +74,10 @@
// which calls Store.Get().
CASBlobLookupSema chan struct{}
+ // OutputFileSema specifies concurrency to download files from CAS and store in
+ // file server in gomaOutput.toFileBlob().
+ OutputFileSema chan struct{}
+
capMu sync.Mutex
capabilities *rpb.ServerCapabilities
}
diff --git a/remoteexec/adapter_test.go b/remoteexec/adapter_test.go
index 989d782..39bade9 100644
--- a/remoteexec/adapter_test.go
+++ b/remoteexec/adapter_test.go
@@ -949,12 +949,12 @@
}
}
- if got, want := files["out/Release/run.sh"].digest, digest.Bytes("cwd-agnostic-wrapper-script", []byte(cwdAgnosticWrapperScript)).Digest(); !proto.Equal(got, want) {
+ if got, want := files["out/Release/run.sh"].digest, digest.Bytes("relocatable-wrapper-script", []byte(relocatableWrapperScript)).Digest(); !proto.Equal(got, want) {
t.Errorf("digest of out/Release/run.sh: %s != %s", got, want)
}
}
-func TestAdaptorHandleArbitraryToolchainSupportNonCwdAgnostic(t *testing.T) {
+func TestAdaptorHandleArbitraryToolchainSupportNonRelocatable(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
@@ -1099,12 +1099,12 @@
want []*rpb.Platform_Property
}{
{
- desc: "cwd agnostic",
+ desc: "relocatable",
args: nil,
want: nil,
},
{
- desc: "non cwd agnostic",
+ desc: "non relocatable",
args: []string{"-g"},
want: []*rpb.Platform_Property{
{
diff --git a/remoteexec/cas/cas.go b/remoteexec/cas/cas.go
index e64cf46..fa3d91f 100644
--- a/remoteexec/cas/cas.go
+++ b/remoteexec/cas/cas.go
@@ -103,6 +103,43 @@
return fmt.Sprintf("missing %d blobs", len(e.Blobs))
}
+type batchUpdateBlobsRequestPool struct {
+ mu sync.Mutex
+ pool sync.Pool // New is protected by mu
+ byteLimit int64
+}
+
+var batchReqPool batchUpdateBlobsRequestPool
+
+// Get gets dummy BachUpdateBlobsRequest to check serialized size.
+// maxSizeBytes is max size needed for data buffer.
+// byteLimit is limit that RBE server sets for batch total size bytes.
+// maxSizeBytes must not exceeds byteLimit.
+func (p *batchUpdateBlobsRequestPool) Get(instance string, maxSizeBytes, byteLimit int64) *rpb.BatchUpdateBlobsRequest {
+ p.mu.Lock()
+ if p.byteLimit < byteLimit {
+ p.byteLimit = byteLimit
+ }
+ if p.pool.New == nil {
+ p.pool.New = func() interface{} {
+ return make([]byte, 0, p.byteLimit)
+ }
+ }
+ buf := p.pool.Get().([]byte)
+ if int64(cap(buf)) < maxSizeBytes {
+ buf = make([]byte, 0, maxSizeBytes)
+ }
+ p.mu.Unlock()
+ return &rpb.BatchUpdateBlobsRequest{
+ InstanceName: instance,
+ Requests: []*rpb.BatchUpdateBlobsRequest_Request{{Data: buf}},
+ }
+}
+
+func (p *batchUpdateBlobsRequestPool) Put(req *rpb.BatchUpdateBlobsRequest) {
+ p.pool.Put(req.Requests[0].Data)
+}
+
func separateBlobsByByteLimit(blobs []*rpb.Digest, instance string, byteLimit int64) ([]*rpb.Digest, []*rpb.Digest) {
if len(blobs) == 0 {
return nil, nil
@@ -113,22 +150,28 @@
})
// Create dummy data to check protobuf size. To avoid redundant allocations, find the largest digest size.
+ // string/bytes will be [encoded <wire-type><tag>] [length] [content...]
+ // so no need to allocate more than byteLimit here.
+ // https://developers.google.com/protocol-buffers/docs/encoding#structure
+ // https://developers.google.com/protocol-buffers/docs/encoding#strings
maxSizeBytes := blobs[len(blobs)-1].SizeBytes
- dummyReq := &rpb.BatchUpdateBlobsRequest{
- InstanceName: instance,
- Requests: []*rpb.BatchUpdateBlobsRequest_Request{{Data: make([]byte, 1, maxSizeBytes)}},
+ if maxSizeBytes >= byteLimit {
+ maxSizeBytes = byteLimit
}
-
- for i, blob := range blobs {
- // Create dummy data to check protobuf size.
- dummyReq.Requests[0].Digest = blob
- dummyReq.Requests[0].Data = dummyReq.Requests[0].Data[:blob.SizeBytes]
- if int64(proto.Size(dummyReq)) >= byteLimit {
- return blobs[:i], blobs[i:]
+ dummyReq := batchReqPool.Get(instance, maxSizeBytes, byteLimit)
+ defer batchReqPool.Put(dummyReq)
+ i := sort.Search(len(blobs), func(i int) bool {
+ if blobs[i].SizeBytes >= byteLimit {
+ return true
}
+ dummyReq.Requests[0].Digest = blobs[i]
+ dummyReq.Requests[0].Data = dummyReq.Requests[0].Data[:blobs[i].SizeBytes]
+ return int64(proto.Size(dummyReq)) >= byteLimit
+ })
+ if i < len(blobs) {
+ return blobs[:i], blobs[i:]
}
- // All blobs have protobuf size below `byteLimit`.
- return blobs, []*rpb.Digest{}
+ return blobs, nil
}
func lookupBlobsInStore(ctx context.Context, blobs []*rpb.Digest, store *digest.Store, sema chan struct{}) ([]*rpb.BatchUpdateBlobsRequest_Request, []MissingBlob) {
@@ -245,11 +288,11 @@
// up to max_batch_total_size_bytes, use BatchUpdateBlobs.
// more than this, use bytestream.Write.
- batchLimit := int64(DefaultBatchByteLimit)
+ byteLimit := int64(DefaultBatchByteLimit)
if c.CacheCapabilities != nil && c.CacheCapabilities.MaxBatchTotalSizeBytes > 0 {
- batchLimit = c.CacheCapabilities.MaxBatchTotalSizeBytes
+ byteLimit = c.CacheCapabilities.MaxBatchTotalSizeBytes
}
- smallBlobs, largeBlobs := separateBlobsByByteLimit(blobs, instance, batchLimit)
+ smallBlobs, largeBlobs := separateBlobsByByteLimit(blobs, instance, byteLimit)
logger.Infof("upload by batch %d out of %d", len(smallBlobs), len(blobs))
blobReqs, missingBlobs := lookupBlobsInStore(ctx, smallBlobs, c.Store, sema)
@@ -257,7 +300,7 @@
Blobs: missingBlobs,
}
- batchReqs := createBatchUpdateBlobsRequests(blobReqs, instance, batchLimit)
+ batchReqs := createBatchUpdateBlobsRequests(blobReqs, instance, byteLimit)
for _, batchReq := range batchReqs {
uploaded := false
for !uploaded {
diff --git a/remoteexec/cas/cas_test.go b/remoteexec/cas/cas_test.go
index 43dcc05..f4c8cff 100644
--- a/remoteexec/cas/cas_test.go
+++ b/remoteexec/cas/cas_test.go
@@ -15,6 +15,7 @@
rpb "github.com/bazelbuild/remote-apis/build/bazel/remote/execution/v2"
"github.com/golang/protobuf/proto"
"github.com/google/go-cmp/cmp"
+ "github.com/google/go-cmp/cmp/cmpopts"
"go.chromium.org/goma/server/remoteexec/digest"
)
@@ -59,7 +60,7 @@
}
func protoEqual(x, y interface{}) bool {
- return cmp.Equal(x, y, cmp.Comparer(proto.Equal))
+ return cmp.Equal(x, y, cmp.Comparer(proto.Equal), cmpopts.EquateEmpty())
}
func TestMissing(t *testing.T) {
diff --git a/remoteexec/clang-cl.go b/remoteexec/clang-cl.go
index c9c2ca9..5e4ac29 100644
--- a/remoteexec/clang-cl.go
+++ b/remoteexec/clang-cl.go
@@ -10,12 +10,14 @@
// TODO: share exec/clangcl.go ?
-// clangclCwdAgnostic checks if args will generate cwd-agnostic outputs
-// (files/stdout/stderr).
+// clangclRelocatableReq checks if the request (args, envs) uses relative
+// paths only and doesn't use flags that generates output including cwd,
+// so will generate cwd-agnostic outputs
+// (files/stdout/stderr will not include cwd dependent paths).
//
// TODO: implement it.
-func clangclCwdAgnostic(args, envs []string) error {
- return errors.New("no cwd agnostic check for clang-cl")
+func clangclRelocatableReq(args, envs []string) error {
+ return errors.New("no relocatable check for clang-cl")
}
// clangclOutputs returns output files from clang-cl command line.
diff --git a/remoteexec/exec.go b/remoteexec/exec.go
index 034a35d..cb4f9e0 100644
--- a/remoteexec/exec.go
+++ b/remoteexec/exec.go
@@ -28,6 +28,7 @@
"google.golang.org/grpc/status"
"go.chromium.org/goma/server/command/descriptor"
+ "go.chromium.org/goma/server/exec"
"go.chromium.org/goma/server/log"
gomapb "go.chromium.org/goma/server/proto/api"
cmdpb "go.chromium.org/goma/server/proto/command"
@@ -612,7 +613,7 @@
type wrapperType int
const (
- wrapperCwdAgnostic wrapperType = iota
+ wrapperRelocatable wrapperType = iota
wrapperBindMount
wrapperNsjailChroot
wrapperWin
@@ -620,8 +621,8 @@
func (w wrapperType) String() string {
switch w {
- case wrapperCwdAgnostic:
- return "wrapper-cwd-agnostic"
+ case wrapperRelocatable:
+ return "wrapper-relocatable"
case wrapperBindMount:
return "wrapper-bind-mount"
case wrapperNsjailChroot:
@@ -668,7 +669,7 @@
"$@"
`
- cwdAgnosticWrapperScript = `#!/bin/bash
+ relocatableWrapperScript = `#!/bin/bash
set -e
if [[ "$WORK_DIR" != "" ]]; then
cd "${WORK_DIR}"
@@ -708,17 +709,17 @@
args := buildArgs(ctx, cmdConfig, argv0, r.gomaReq)
// TODO: only allow whitelisted envs.
- wt := wrapperCwdAgnostic
+ wt := wrapperRelocatable
pathType := cmdConfig.GetCmdDescriptor().GetSetup().GetPathType()
switch pathType {
case cmdpb.CmdDescriptor_POSIX:
if r.needChroot {
wt = wrapperNsjailChroot
} else {
- err = cwdAgnosticReq(ctx, cmdConfig, r.filepath, r.gomaReq.Arg, r.gomaReq.Env)
+ err = relocatableReq(ctx, cmdConfig, r.filepath, r.gomaReq.Arg, r.gomaReq.Env)
if err != nil {
wt = wrapperBindMount
- logger.Infof("non cwd agnostic: %v", err)
+ logger.Infof("non relocatable: %v", err)
}
}
case cmdpb.CmdDescriptor_WINDOWS:
@@ -768,12 +769,12 @@
isExecutable: true,
},
}
- case wrapperCwdAgnostic:
- logger.Infof("run with chdir: cwd agnostic")
+ case wrapperRelocatable:
+ logger.Infof("run with chdir: relocatable")
for _, e := range r.gomaReq.Env {
if strings.HasPrefix(e, "PWD=") {
// PWD is usually absolute path.
- // if cwd agnostic, then we should remove
+ // if relocatable, then we should remove
// PWD environment variable.
continue
}
@@ -782,7 +783,7 @@
files = []fileDesc{
{
name: posixWrapperName,
- data: digest.Bytes("cwd-agnostic-wrapper-script", []byte(cwdAgnosticWrapperScript)),
+ data: digest.Bytes("relocatable-wrapper-script", []byte(relocatableWrapperScript)),
isExecutable: true,
},
}
@@ -866,21 +867,21 @@
return append(args, fmt.Sprintf("--target=%s", target))
}
-// cwdAgnosticReq checks args, envs is cwd agnostic, respecting cmdConfig.
-func cwdAgnosticReq(ctx context.Context, cmdConfig *cmdpb.Config, filepath clientFilePath, args, envs []string) error {
+// relocatableReq checks args, envs is relocatable, respecting cmdConfig.
+func relocatableReq(ctx context.Context, cmdConfig *cmdpb.Config, filepath clientFilePath, args, envs []string) error {
switch name := cmdConfig.GetCmdDescriptor().GetSelector().GetName(); name {
case "gcc", "g++", "clang", "clang++":
- return gccCwdAgnostic(filepath, args, envs)
+ return gccRelocatableReq(filepath, args, envs)
case "clang-cl":
- return clangclCwdAgnostic(args, envs)
+ return clangclRelocatableReq(args, envs)
case "javac":
- // Currently, javac in Chromium is fully cwd agnostic. Simpler just to
- // support only the cwd agnostic case and let it fail if the client passed
+ // Currently, javac in Chromium is fully relocatable. Simpler just to
+ // support only the relocatable case and let it fail if the client passed
// in invalid absolute paths.
return nil
default:
// "cl.exe", "clang-tidy"
- return fmt.Errorf("no cwd agnostic check for %s", name)
+ return fmt.Errorf("no relocatable check for %s", name)
}
}
@@ -1216,7 +1217,10 @@
timestampSub(ctx, md.GetInputFetchCompletedTimestamp(), md.GetInputFetchStartTimestamp()),
timestampSub(ctx, md.GetExecutionCompletedTimestamp(), md.GetExecutionStartTimestamp()),
timestampSub(ctx, md.GetOutputUploadCompletedTimestamp(), md.GetOutputUploadStartTimestamp()))
-
+ r.gomaResp.ExecutionStats = &gomapb.ExecutionStats{
+ ExecutionStartTimestamp: md.GetExecutionStartTimestamp(),
+ ExecutionCompletedTimestamp: md.GetExecutionCompletedTimestamp(),
+ }
gout := gomaOutput{
gomaResp: r.gomaResp,
bs: r.client.ByteStream(),
@@ -1278,11 +1282,23 @@
r.gomaResp.ErrorMessage = append(r.gomaResp.ErrorMessage, fmt.Sprintf("output path %s: %v", output.Path, err))
continue
}
- gout.outputDirectory(ctx, r.filepath, fname, output)
+ gout.outputDirectory(ctx, r.filepath, fname, output, r.f.OutputFileSema)
}
if len(r.gomaResp.ErrorMessage) == 0 {
r.gomaResp.Result.ExitStatus = proto.Int32(eresp.Result.ExitCode)
}
+
+ sizeLimit := exec.DefaultMaxRespMsgSize
+ respSize := proto.Size(r.gomaResp)
+ if respSize > sizeLimit {
+ logger.Infof("gomaResp size=%d, limit=%d, using FileService for larger blobs.", respSize, sizeLimit)
+ if err := gout.reduceRespSize(ctx, sizeLimit, r.f.OutputFileSema); err != nil {
+ // Don't need to append any error messages to `r.gomaResp` because it won't be sent.
+ return nil, fmt.Errorf("failed to reduce resp size below limit=%d, %d -> %d: %v", sizeLimit, respSize, proto.Size(gout.gomaResp), err)
+ }
+ logger.Infof("gomaResp size reduced %d -> %d", respSize, proto.Size(gout.gomaResp))
+ }
+
return r.gomaResp, r.Err()
}
diff --git a/remoteexec/gcc.go b/remoteexec/gcc.go
index 687b66d..4903faf 100644
--- a/remoteexec/gcc.go
+++ b/remoteexec/gcc.go
@@ -10,20 +10,37 @@
"strings"
)
+var pathFlags = []string{
+ "--sysroot=",
+ "-B",
+ "-I",
+ "-fcrash-diagnostics-dir=",
+ "-fprofile-instr-use=",
+ "-fprofile-sample-use=",
+ "-fsanitize-blacklist=",
+ "-include=",
+ "-isystem",
+ "-o",
+ "-resource-dir=",
+}
+
// TODO: share exec/gcc.go ?
-// gccCwdAgnostic checks if args will generate cwd-agnostic outputs
-// (files/stdout/stderr).
+// gccRelocatableReq checks if the request (args, envs) uses relative
+// paths only and doesn't use flags that generates output including cwd,
+// so will generate cwd-agnostic outputs
+// (files/stdout/stderr will not include cwd dependent paths).
//
-// args will be cwd-agnostic if path used in arg is cwd relative.
+// The request will be relocatable if path used in arg is cwd relative.
//
-// args will NOT generate cwd-agnostic output (.o), that is, generate
+// The request will NOT be relocatable, that is, generate
// outputs that would contain absolute path names (DW_AT_comp_dir etc),
// if
// debug build (-g* except -g0) -> DW_AT_comp_dir or other filepaths.
+// this will be canceled by -fdebug-compilation-dir
// --pnacl-allow-translate crbug.com/685461
//
-// these flags would emit non cwd-agnostic output.
+// The following flags would NOT be relocatable
// absolute input filename (debug build)
// *.d file output will not be cwd-agnostic.
// DW_AT_name (debug build)
@@ -37,32 +54,66 @@
//
// ref:
// https://docs.google.com/spreadsheets/d/1_-ZJhqy7WhSFYuZU2QkmQ4Ed9182bWfKg09EfBAkVf8/edit#gid=759603323
-func gccCwdAgnostic(filepath clientFilePath, args, envs []string) error {
+//
+// TODO: http://b/150662978 relocatableReq should check input and output file path too.
+func gccRelocatableReq(filepath clientFilePath, args, envs []string) error {
var debugFlags []string
+ debugCompilationDir := false
subArgs := map[string][]string{}
pathFlag := false
var subCmd string
+Loop:
for _, arg := range args {
- switch {
- case arg == "-fdebug-compilation-dir":
- // We can stop checking the rest of the flags. When seeing
- // "-fdebug-compilation-dir", we expect the result to be CWD agnostic.
- //
- // Note that this check applies to both GCC and Clang and returns nil
- // immediately for the following cases:
- // -xx -fdebug-compilation-dir . -yy ... <- GCC flag
- // -xx -XClang -fdebug-compilation-dir -XClang . -yy ... <- Clang flag
- //
- // As a result, clangArgCwdAgnostic() doesn't need to check this again.
- if subCmd == "" || subCmd == "clang" {
- return nil
- }
- return errors.New("fdebug-compilation-dir not supported for " + subCmd)
- case pathFlag:
+ if pathFlag {
if filepath.IsAbs(arg) {
return fmt.Errorf("abs path: %s", arg)
}
+ // TODO: When clang supports relative paths in hmap,
+ // instead check that hmap does not have abs paths.
+ if strings.HasSuffix(arg, ".hmap") {
+ return fmt.Errorf("hmap file: %s", arg)
+ }
pathFlag = false
+ continue
+ }
+ // TODO: When clang supports relative paths in hmap,
+ // instead check that hmap does not have abs paths.
+ if strings.HasPrefix(arg, "-I") && strings.HasSuffix(arg, ".hmap") {
+ return fmt.Errorf("hmap file: %s", arg)
+ }
+ for _, fp := range pathFlags {
+ if arg != fp && strings.HasPrefix(arg, fp) {
+
+ if filepath.IsAbs(arg[len(fp):]) {
+ return fmt.Errorf("abs path: %s", arg)
+ }
+ continue Loop
+ }
+ }
+ switch {
+ case arg == "-fdebug-compilation-dir":
+ // We can stop checking the rest of the flags.
+ // When seeing "-fdebug-compilation-dir",
+ // we could cancel non-cwd agnosticsness due to
+ // debug flags.
+ //
+ // Note that this check applies to both GCC and Clang
+ // -xx -fdebug-compilation-dir . -yy ... <- GCC flag
+ // -xx -Xclang -fdebug-compilation-dir -Xclang . -yy ... <- Clang flag
+ //
+ // As a result, clangArgRelocatableReq() doesn't need to check this again.
+ // The value of -fdebug-compilation-dir is used
+ // just for DW_AT_comp_dir, so no need to check it.
+ switch subCmd {
+ case "clang":
+ subArgs[subCmd] = append(subArgs[subCmd], arg)
+ fallthrough
+ case "":
+ debugCompilationDir = true
+
+ continue Loop
+ }
+ return errors.New("fdebug-compilation-dir not supported for " + subCmd)
case subCmd != "":
subArgs[subCmd] = append(subArgs[subCmd], arg)
@@ -120,34 +171,9 @@
pathFlag = true
case arg == "-MF":
pathFlag = true
- case strings.HasPrefix(arg, "-o"):
- if filepath.IsAbs(arg[len("-o"):]) {
- return fmt.Errorf("abs path: %s", arg)
- }
- case strings.HasPrefix(arg, "-I") || strings.HasPrefix(arg, "-B"):
- if filepath.IsAbs(arg[len("-I"):]) {
- return fmt.Errorf("abs path: %s", arg)
- }
- case strings.HasPrefix(arg, "-isystem"):
- if filepath.IsAbs(arg[len("-isystem"):]) {
- return fmt.Errorf("abs path: %s", arg)
- }
- case strings.HasPrefix(arg, "-include="):
- if filepath.IsAbs(arg[len("-include="):]) {
- return fmt.Errorf("abs path: %s", arg)
- }
- case strings.HasPrefix(arg, "--sysroot="):
- if filepath.IsAbs(arg[len("--sysroot="):]) {
- return fmt.Errorf("abs path: %s", arg)
- }
case arg == "-isysroot":
pathFlag = true
- case strings.HasPrefix(arg, "-resource-dir="):
- if filepath.IsAbs(arg[len("-resource-dir="):]) {
- return fmt.Errorf("abs path: %s", arg)
- }
-
case strings.HasPrefix(arg, "-"): // unknown flag?
return fmt.Errorf("unknown flag: %s", arg)
@@ -158,19 +184,19 @@
}
}
- if len(debugFlags) > 0 {
+ if len(debugFlags) > 0 && !debugCompilationDir {
return fmt.Errorf("debug build: %q", debugFlags)
}
if len(subArgs) > 0 {
for cmd, args := range subArgs {
switch cmd {
case "clang":
- err := clangArgCwdAgnostic(filepath, args)
+ err := clangArgRelocatable(filepath, args)
if err != nil {
return err
}
case "llvm":
- err := llvmArgCwdAgnostic(filepath, args)
+ err := llvmArgRelocatable(filepath, args)
if err != nil {
return err
}
@@ -195,7 +221,7 @@
return nil
}
-func clangArgCwdAgnostic(filepath clientFilePath, args []string) error {
+func clangArgRelocatable(filepath clientFilePath, args []string) error {
pathFlag := false
skipFlag := false
for _, arg := range args {
@@ -208,8 +234,8 @@
case skipFlag:
skipFlag = false
- case arg == "-mllvm" || arg == "-add-plugin":
- // TODO: pass llvmArgCwdAgnostic for -mllvm?
+ case arg == "-mllvm", arg == "-add-plugin", arg == "-fdebug-compilation-dir":
+ // TODO: pass llvmArgRelocatable for -mllvm?
skipFlag = true
case strings.HasPrefix(arg, "-plugin-arg-"):
skipFlag = true
@@ -222,7 +248,7 @@
return nil
}
-func llvmArgCwdAgnostic(filepath clientFilePath, args []string) error {
+func llvmArgRelocatable(filepath clientFilePath, args []string) error {
for _, arg := range args {
switch {
case strings.HasPrefix(arg, "-asan-"):
diff --git a/remoteexec/gcc_test.go b/remoteexec/gcc_test.go
index de600e1..86e902d 100644
--- a/remoteexec/gcc_test.go
+++ b/remoteexec/gcc_test.go
@@ -12,7 +12,7 @@
"go.chromium.org/goma/server/command/descriptor/posixpath"
)
-func TestGccCwdAgnostic(t *testing.T) {
+func TestGccRelocatableReq(t *testing.T) {
baseReleaseArgs := []string{
"../../third_party/llvm-build/Release+Asserts/bin/clang++",
@@ -78,7 +78,7 @@
desc string
args []string
envs []string
- cwdAgnostic bool
+ relocatable bool
}{
{
desc: "chromium base release",
@@ -86,7 +86,7 @@
envs: []string{
"PWD=/b/c/b/linux/src/out/Release",
},
- cwdAgnostic: true,
+ relocatable: true,
},
{
desc: "chromium base debug",
@@ -136,58 +136,58 @@
envs: []string{
"PWD=/b/c/b/linux/src/out/Debug",
},
- cwdAgnostic: false,
+ relocatable: false,
},
{
desc: "resource-dir relative",
args: append(append([]string{}, baseReleaseArgs...),
"-resource-dir=../../third_party/llvm-build-release+Asserts/bin/clang/10.0.0"),
- cwdAgnostic: true,
+ relocatable: true,
},
{
desc: "resource-dir absolute",
args: append(append([]string{}, baseReleaseArgs...),
"-resource-dir=/b/c/b/linux/src/third_party/llvm-build-release+Asserts/bin/clang/10.0.0"),
- cwdAgnostic: false,
+ relocatable: false,
},
{
desc: "isystem absolute",
args: append(append([]string{}, baseReleaseArgs...),
"-isystem/b/c/b/linux/src/build/linux/debian_sid_amd64-sysroot/usr/include/glib-2.0"),
- cwdAgnostic: false,
+ relocatable: false,
},
{
desc: "include absolute",
- args: append(append([]string {}, baseReleaseArgs...),
+ args: append(append([]string{}, baseReleaseArgs...),
"-include",
"/b/c/b/linux/header.h"),
- cwdAgnostic: false,
+ relocatable: false,
},
{
desc: "include relative",
- args: append(append([]string {}, baseReleaseArgs...),
+ args: append(append([]string{}, baseReleaseArgs...),
"-include",
"../b/c/b/linux/header.h"),
- cwdAgnostic: true,
+ relocatable: true,
},
{
desc: "include= absolute",
- args: append(append([]string {}, baseReleaseArgs...),
+ args: append(append([]string{}, baseReleaseArgs...),
"-include=/b/c/b/linux/header.h"),
- cwdAgnostic: false,
+ relocatable: false,
},
{
desc: "include= relative",
- args: append(append([]string {}, baseReleaseArgs...),
+ args: append(append([]string{}, baseReleaseArgs...),
"-include=../b/c/b/linux/header.h"),
- cwdAgnostic: true,
+ relocatable: true,
},
{
desc: "sysroot absolute",
args: modifyArgs(baseReleaseArgs,
"--sysroot",
"--sysroot=/b/c/b/linux/build/linux/debian_sid_amd64-sysroot"),
- cwdAgnostic: false,
+ relocatable: false,
},
{
desc: "llvm -asan option",
@@ -195,7 +195,7 @@
args: append(append([]string{}, baseReleaseArgs...),
"-mllvm",
"-asan-globals=0"),
- cwdAgnostic: true,
+ relocatable: true,
},
{
desc: "llvm -regalloc option",
@@ -205,27 +205,89 @@
"-regalloc=pbqp",
"-mllvm",
"-pbqp-coalescing"),
- cwdAgnostic: true,
+ relocatable: true,
+ },
+ {
+ desc: "headermap file",
+ // http://b/149448356#comment17
+ args: append(append([]string{}, baseReleaseArgs...),
+ "-Ifoo.hmap"),
+ relocatable: false,
+ },
+ {
+ desc: "headermap file 2",
+ // http://b/149448356#comment17
+ args: append(append([]string{}, baseReleaseArgs...),
+ []string{"-I", "foo.hmap"}...),
+ relocatable: false,
},
{
desc: "Qunused-arguments",
- args: append(append([]string {}, baseReleaseArgs...),
+ args: append(append([]string{}, baseReleaseArgs...),
"-Qunused-arguments"),
- cwdAgnostic: true,
+ relocatable: true,
+ },
+ {
+ desc: "fprofile-instr-use relative",
+ args: append(append([]string{}, baseReleaseArgs...),
+ "-fprofile-instr-use=../../out/data/default.profdata"),
+ relocatable: true,
+ },
+ {
+ desc: "fprofile-instr-use absolute",
+ args: append(append([]string{}, baseReleaseArgs...),
+ "-fprofile-instr-use=/b/c/b/linux/src/out/data/default.profdataa"),
+ relocatable: false,
+ },
+ {
+ desc: "fprofile-sample-use relative",
+ args: append(append([]string{}, baseReleaseArgs...),
+ "-fprofile-sample-use=../../chrome/android/profiles/afdo.prof"),
+ relocatable: true,
+ },
+ {
+ desc: "fprofile-sample-use absolute",
+ args: append(append([]string{}, baseReleaseArgs...),
+ "-fprofile-sample-use=/b/c/b/linux/src/android/profiles/afdo.prof"),
+ relocatable: false,
+ },
+ {
+ desc: "fsatinitize-blacklist relative",
+ args: append(append([]string{}, baseReleaseArgs...),
+ "-fsanitize-blacklist=../../tools/cfi/blacklist.txt"),
+ relocatable: true,
+ },
+ {
+ desc: "fsanitize-blacklist absolute",
+ args: append(append([]string{}, baseReleaseArgs...),
+ "-fsanitize-blacklist=/b/c/b/linux/src/tools/cfi/blacklist.txt"),
+ relocatable: false,
+ },
+ {
+ desc: "fcrash-diagnostics-dir absolute",
+ args: append(append([]string{}, baseReleaseArgs...),
+ "-fcrash-diagnostics-dir=../../tools/clang/crashreports"),
+ relocatable: true,
+ },
+ {
+ desc: "fcrash-diagnostics-dir absolute",
+ args: append(append([]string{}, baseReleaseArgs...),
+ "-fcrash-diagnostics-dir=/b/c/b/linux/src/tools/clang/crashreports"),
+ relocatable: false,
},
} {
t.Run(tc.desc, func(t *testing.T) {
- err := gccCwdAgnostic(posixpath.FilePath{}, tc.args, tc.envs)
- if (err == nil) != tc.cwdAgnostic {
- t.Errorf("gccCwdAgnostic(posixpath.FilePath, args, envs)=%v; cwdAgnostic=%t", err, tc.cwdAgnostic)
+ err := gccRelocatableReq(posixpath.FilePath{}, tc.args, tc.envs)
+ if (err == nil) != tc.relocatable {
+ t.Errorf("gccRelocatableReq(posixpath.FilePath, args, envs)=%v; relocatable=%t", err, tc.relocatable)
}
})
}
}
-func TestGccCwdAgnosticForDebugCompilationDir(t *testing.T) {
+func TestGccRelocatableReqForDebugCompilationDir(t *testing.T) {
// Tests for supporting "-fdebug-compilation-dir", see b/135719929.
- // We could have merged the cases here into TestGccCwdAgnostic, but decided
+ // We could have merged the cases here into TestGccRelocatableReq, but decided
// to separate them for clarity.
// Do not set "-g*" options in baseReleaseArgs!
@@ -240,14 +302,14 @@
desc string
args []string
envs []string
- cwdAgnostic bool
+ relocatable bool
}{
{
desc: "basic",
args: append(append([]string{}, baseReleaseArgs...),
"-fdebug-compilation-dir",
"."),
- cwdAgnostic: true,
+ relocatable: true,
},
{
desc: "-Xclang",
@@ -256,7 +318,7 @@
"-fdebug-compilation-dir",
"-Xclang",
"."),
- cwdAgnostic: true,
+ relocatable: true,
},
{
desc: "With -g* DBG options",
@@ -265,7 +327,7 @@
"-gsplit-dwarf",
"-fdebug-compilation-dir",
"."),
- cwdAgnostic: true,
+ relocatable: true,
},
{
desc: "-Xclang with -g* DBG option",
@@ -276,7 +338,7 @@
"-fdebug-compilation-dir",
"-Xclang",
"."),
- cwdAgnostic: true,
+ relocatable: true,
},
{
// Make sure the CWD agnostic still returns false if
@@ -285,7 +347,7 @@
args: append(append([]string{}, baseReleaseArgs...),
"-g2",
"-gsplit-dwarf"),
- cwdAgnostic: false,
+ relocatable: false,
},
{
// "-fdebug-compilation-dir" is not supported as LLVM flags.
@@ -295,13 +357,21 @@
"-fdebug-compilation-dir",
"-mllvm",
"."),
- cwdAgnostic: false,
+ relocatable: false,
+ },
+ {
+ desc: "input abs path",
+ args: append(append([]string{}, baseReleaseArgs...),
+ "-fdebug-compilation-dir",
+ ".",
+ "-isystem/b/c/b/linux/src/build/linux/debian_sid_amd64-sysroot/usr/include/glib-2.0"),
+ relocatable: false,
},
} {
t.Run(tc.desc, func(t *testing.T) {
- err := gccCwdAgnostic(posixpath.FilePath{}, tc.args, tc.envs)
- if (err == nil) != tc.cwdAgnostic {
- t.Errorf("gccCwdAgnostic(posixpath.FilePath, args, envs)=%v; cwdAgnostic=%t", err, tc.cwdAgnostic)
+ err := gccRelocatableReq(posixpath.FilePath{}, tc.args, tc.envs)
+ if (err == nil) != tc.relocatable {
+ t.Errorf("gccRelocatableReq(posixpath.FilePath, args, envs)=%v; relocatable=%t", err, tc.relocatable)
}
})
}
diff --git a/remoteexec/gomainput.go b/remoteexec/gomainput.go
index a2a2287..299767f 100644
--- a/remoteexec/gomainput.go
+++ b/remoteexec/gomainput.go
@@ -15,6 +15,7 @@
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
+ "go.chromium.org/goma/server/file"
"go.chromium.org/goma/server/hash"
gomapb "go.chromium.org/goma/server/proto/api"
fpb "go.chromium.org/goma/server/proto/file"
@@ -83,28 +84,39 @@
return resp.HashKey, nil
}
-func lookup(ctx context.Context, c lookupClient, hashKey string) (*gomapb.FileBlob, error) {
+func lookup(ctx context.Context, c lookupClient, hashKeys []string) ([]*gomapb.FileBlob, error) {
+ req := &gomapb.LookupFileReq{
+ HashKey: hashKeys,
+ RequesterInfo: requesterInfo(ctx),
+ }
var resp *gomapb.LookupFileResp
var err error
err = rpc.Retry{}.Do(ctx, func() error {
- resp, err = c.LookupFile(ctx, &gomapb.LookupFileReq{
- HashKey: []string{
- hashKey,
- },
- RequesterInfo: requesterInfo(ctx),
- })
+ resp, err = c.LookupFile(ctx, req)
return err
})
if err != nil {
return nil, err
}
if len(resp.Blob) == 0 {
- return nil, status.Errorf(codes.NotFound, "no blob for %s", hashKey)
+ return nil, status.Errorf(codes.NotFound, "no blob for %s", hashKeys)
}
- if resp.Blob[0].GetBlobType() == gomapb.FileBlob_FILE_UNSPECIFIED {
- return nil, status.Errorf(codes.NotFound, "missing blob for %s", hashKey)
+ if len(resp.Blob) != len(hashKeys) {
+ return nil, status.Errorf(codes.Internal, "request %d (%q), got %d", len(hashKeys), hashKeys, len(resp.Blob))
}
- return resp.Blob[0], nil
+ var unspecified []string
+ var blobs []*gomapb.FileBlob
+ for i, blob := range resp.Blob {
+ if blob.GetBlobType() == gomapb.FileBlob_FILE_UNSPECIFIED {
+ unspecified = append(unspecified, fmt.Sprintf("%d:%q", i, hashKeys[i]))
+ continue
+ }
+ blobs = append(blobs, blob)
+ }
+ if len(unspecified) > 0 {
+ return nil, status.Errorf(codes.NotFound, "missing blob for %s", unspecified)
+ }
+ return blobs, nil
}
type gomaInputSource struct {
@@ -122,10 +134,11 @@
// TODO: release g.blob
if blob == nil {
var err error
- blob, err = lookup(ctx, g.lookupClient, g.hashKey)
+ blobs, err := lookup(ctx, g.lookupClient, []string{g.hashKey})
if err != nil {
return nil, err
}
+ blob = blobs[0]
}
switch blob.GetBlobType() {
case gomapb.FileBlob_FILE:
@@ -138,6 +151,7 @@
lookupClient: g.lookupClient,
hashKey: g.hashKey,
meta: blob,
+ allocated: make([]byte, gomaInputBatchSize*file.FileChunkSize),
}, nil
case gomapb.FileBlob_FILE_UNSPECIFIED:
@@ -146,13 +160,16 @@
return nil, status.Errorf(codes.Internal, "bad file_blob type: %s: %v", g.hashKey, blob.GetBlobType())
}
+const gomaInputBatchSize = 5
+
type gomaInputReader struct {
ctx context.Context
lookupClient lookupClient
hashKey string
meta *gomapb.FileBlob
i int // next index of hash key in meta.
- buf []byte // points meta hash_key[i-1]'s Content.
+ buf []byte // points meta hash_key[prev_i:i]'s Content.
+ allocated []byte // allocated buffer for buf.
}
func (r *gomaInputReader) Read(buf []byte) (int, error) {
@@ -160,15 +177,31 @@
if len(r.meta.HashKey) == r.i {
return 0, io.EOF
}
- blob, err := lookup(r.ctx, r.lookupClient, r.meta.HashKey[r.i])
+ j := r.i + gomaInputBatchSize
+ if j > len(r.meta.HashKey) {
+ j = len(r.meta.HashKey)
+ }
+ blobs, err := lookup(r.ctx, r.lookupClient, r.meta.HashKey[r.i:j])
if err != nil {
- return 0, status.Errorf(status.Code(err), "lookup chunk in FILE_META %s %d %s: %v", r.hashKey, r.i, r.meta.HashKey[r.i], err)
+ return 0, status.Errorf(status.Code(err), "lookup chunk in FILE_META %s %d:%d %s: %v", r.hashKey, r.i, j, r.meta.HashKey[r.i:j], err)
}
- if blob.GetBlobType() != gomapb.FileBlob_FILE_CHUNK {
- return 0, status.Errorf(codes.Internal, "lookup chunk in FILE_META %s %d %s: not FILE_CHUNK %v", r.hashKey, r.i, r.meta.HashKey[r.i], blob.GetBlobType())
+ for i, blob := range blobs {
+ if blob.GetBlobType() != gomapb.FileBlob_FILE_CHUNK {
+ return 0, status.Errorf(codes.Internal, "lookup chunk in FILE_META %s %d %s: not FILE_CHUNK %v", r.hashKey, r.i+i, r.meta.HashKey[r.i+i], blob.GetBlobType())
+ }
}
- r.i++
- r.buf = blob.Content
+ b := r.allocated
+ pos := 0
+ i0 := r.i
+ r.i = j
+ for i, blob := range blobs {
+ n := copy(b[pos:], blob.Content)
+ if n < len(blob.Content) {
+ return 0, status.Errorf(codes.Internal, "goma input buffer shortage %d written, len(blob.Content)=%d for %s %d %s", n, len(blob.Content), r.hashKey, i0+i, r.meta.HashKey[i0+i])
+ }
+ pos += n
+ }
+ r.buf = b[:pos]
}
n := copy(buf, r.buf)
r.buf = r.buf[n:]
diff --git a/remoteexec/gomaoutput.go b/remoteexec/gomaoutput.go
index 4f687d7..bb5dd68 100644
--- a/remoteexec/gomaoutput.go
+++ b/remoteexec/gomaoutput.go
@@ -11,15 +11,18 @@
"fmt"
"io"
"os"
+ "sync"
rpb "github.com/bazelbuild/remote-apis/build/bazel/remote/execution/v2"
"github.com/golang/protobuf/proto"
"go.opencensus.io/trace"
+ "golang.org/x/sync/errgroup"
bpb "google.golang.org/genproto/googleapis/bytestream"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
+ "go.chromium.org/goma/server/file"
"go.chromium.org/goma/server/log"
gomapb "go.chromium.org/goma/server/proto/api"
fpb "go.chromium.org/goma/server/proto/file"
@@ -81,7 +84,7 @@
g.gomaResp.Result.StderrBuffer = buf.Bytes()
}
-func (g gomaOutput) outputFile(ctx context.Context, fname string, output *rpb.OutputFile) {
+func (g gomaOutput) outputFileHelper(ctx context.Context, fname string, output *rpb.OutputFile) (*gomapb.ExecResult_Output, error) {
var blob *gomapb.FileBlob
err := rpc.Retry{}.Do(ctx, func() error {
var err error
@@ -96,14 +99,115 @@
default:
logger.Errorf("goma blob for %s: %v", output.Path, err)
}
- g.gomaResp.ErrorMessage = append(g.gomaResp.ErrorMessage, fmt.Sprintf("goma blob for %s: %v", output.Path, status.Code(err)))
- return
+ return nil, fmt.Errorf("goma blob for %s: %v", output.Path, status.Code(err))
}
- g.gomaResp.Result.Output = append(g.gomaResp.Result.Output, &gomapb.ExecResult_Output{
+ return &gomapb.ExecResult_Output{
Filename: proto.String(fname),
Blob: blob,
IsExecutable: proto.Bool(output.IsExecutable),
- })
+ }, nil
+}
+
+func (g gomaOutput) outputFile(ctx context.Context, fname string, output *rpb.OutputFile) {
+ result, err := g.outputFileHelper(ctx, fname, output)
+ if err != nil {
+ g.gomaResp.ErrorMessage = append(g.gomaResp.ErrorMessage, err.Error())
+ return
+ }
+ g.gomaResp.Result.Output = append(g.gomaResp.Result.Output, result)
+}
+
+func (g gomaOutput) outputFilesConcurrent(ctx context.Context, outputs []*rpb.OutputFile, sema chan struct{}) {
+ var wg sync.WaitGroup
+ results := make([]*gomapb.ExecResult_Output, len(outputs))
+ errs := make([]error, len(outputs))
+
+ for i := range outputs {
+ wg.Add(1)
+ go func(i int) {
+ defer wg.Done()
+ sema <- struct{}{}
+ defer func() {
+ <-sema
+ }()
+
+ output := outputs[i]
+ results[i], errs[i] = g.outputFileHelper(ctx, output.Path, output)
+ }(i)
+ }
+ wg.Wait()
+
+ for _, result := range results {
+ if result != nil {
+ g.gomaResp.Result.Output = append(g.gomaResp.Result.Output, result)
+ }
+ }
+ for _, err := range errs {
+ if err != nil {
+ g.gomaResp.ErrorMessage = append(g.gomaResp.ErrorMessage, err.Error())
+ }
+ }
+}
+
+func toChunkedFileBlob(ctx context.Context, rd io.Reader, size int64, fs fpb.FileServiceClient) (*gomapb.FileBlob, error) {
+ const bufsize = file.LargeFileThreshold
+ in := bufio.NewReaderSize(rd, bufsize)
+ blob := &gomapb.FileBlob{
+ BlobType: gomapb.FileBlob_FILE_META.Enum(),
+ FileSize: proto.Int64(size),
+ }
+ buf := make([]byte, bufsize)
+ var offset int64
+ eof := false
+ for offset < size && !eof {
+ remain := size - offset
+ if remain < bufsize {
+ buf = buf[:remain]
+ }
+ n, err := io.ReadFull(in, buf)
+ if err != nil && err != io.EOF {
+ return nil, err
+ }
+ eof = err == io.EOF
+ var resp *gomapb.StoreFileResp
+ err = rpc.Retry{}.Do(ctx, func() error {
+ resp, err = fs.StoreFile(ctx, &gomapb.StoreFileReq{
+ Blob: []*gomapb.FileBlob{
+ {
+ BlobType: gomapb.FileBlob_FILE_CHUNK.Enum(),
+ Offset: proto.Int64(offset),
+ Content: buf[:n],
+ FileSize: proto.Int64(size),
+ },
+ },
+ RequesterInfo: requesterInfo(ctx),
+ })
+ return err
+ })
+ if err != nil {
+ return nil, fmt.Errorf("store blob failed offset=%d: %v", offset, err)
+ }
+ for _, hashKey := range resp.HashKey {
+ if hashKey == "" {
+ return nil, fmt.Errorf("store blob failed offset=%d", offset)
+ }
+ blob.HashKey = append(blob.HashKey, hashKey)
+ }
+ offset += int64(n)
+ }
+ if size != offset {
+ return nil, fmt.Errorf("size mismatch: digest size=%d, store size=%d", size, offset)
+ }
+ // EOF is only returned when there is no more input available at the beginning of a read,
+ // not at the end of the final non-empty read.
+ n, err := in.Read(make([]byte, 1))
+ if n != 0 {
+ return nil, fmt.Errorf("more bytes were read past end: %d", n)
+ }
+ if err != io.EOF {
+ return nil, fmt.Errorf("could not confirm EOF: %v", err)
+ }
+ return blob, nil
}
func (g gomaOutput) toFileBlob(ctx context.Context, output *rpb.OutputFile) (*gomapb.FileBlob, error) {
@@ -112,9 +216,7 @@
logger := log.FromContext(ctx)
- const fileBlobChunkThresholdBytes = 2 * 1024 * 1024
-
- if output.Digest.SizeBytes < fileBlobChunkThresholdBytes {
+ if output.Digest.SizeBytes < file.LargeFileThreshold {
// for single FileBlob.
var buf bytes.Buffer
err := cas.DownloadDigest(ctx, g.bs, &buf, g.instance, output.Digest)
@@ -128,9 +230,7 @@
}, nil
}
- var in io.Reader
casErrCh := make(chan error, 1)
- const bufsize = fileBlobChunkThresholdBytes
rd, wr, err := os.Pipe()
if err != nil {
return nil, err
@@ -149,63 +249,50 @@
wr.Close()
casErrCh <- err
}()
- in = bufio.NewReaderSize(rd, bufsize)
- blob := &gomapb.FileBlob{
- BlobType: gomapb.FileBlob_FILE_META.Enum(),
- FileSize: proto.Int64(output.Digest.SizeBytes),
+
+ blob, err := toChunkedFileBlob(ctx, rd, output.Digest.SizeBytes, g.gomaFile)
+ if err != nil {
+ return nil, fmt.Errorf("Failed to convert %v to chunked FileBlob: %v", output, err)
}
- buf := make([]byte, bufsize)
- var offset int64
- for offset < output.Digest.SizeBytes {
- remain := output.Digest.SizeBytes - offset
- if remain < bufsize {
- buf = buf[:remain]
- }
- n, err := io.ReadFull(in, buf)
- if err != nil && err != io.EOF {
- return nil, err
- }
- eof := err == io.EOF
- var resp *gomapb.StoreFileResp
- err = rpc.Retry{}.Do(ctx, func() error {
- resp, err = g.gomaFile.StoreFile(ctx, &gomapb.StoreFileReq{
- Blob: []*gomapb.FileBlob{
- {
- BlobType: gomapb.FileBlob_FILE_CHUNK.Enum(),
- Offset: proto.Int64(offset),
- Content: buf[:n],
- FileSize: proto.Int64(output.Digest.SizeBytes),
- },
- },
- RequesterInfo: requesterInfo(ctx),
- })
- return err
- })
- if err != nil {
- logger.Warnf("store blob failed offset=%d for %v: %v", offset, output, err)
- return nil, err
- }
- for _, hashKey := range resp.HashKey {
- if hashKey == "" {
- return nil, fmt.Errorf("store blob failed offset=%d for %v", offset, output)
- }
- blob.HashKey = append(blob.HashKey, hashKey)
- }
- offset += int64(n)
- if eof && offset < output.Digest.SizeBytes {
- break
- }
- }
- if err := <-casErrCh; err != nil {
+ if err = <-casErrCh; err != nil {
return nil, err
}
- if output.Digest.SizeBytes != offset {
- return nil, fmt.Errorf("size mismatch: digest size=%d, store size=%d", output.Digest.SizeBytes, offset)
- }
return blob, nil
}
-func (g gomaOutput) outputDirectory(ctx context.Context, filepath clientFilePath, dname string, output *rpb.OutputDirectory) {
+func traverseTree(ctx context.Context, filepath clientFilePath, dname string, dir *rpb.Directory, ds *digest.Store) []*rpb.OutputFile {
+ logger := log.FromContext(ctx)
+ var result []*rpb.OutputFile
+ for _, f := range dir.Files {
+ fname := filepath.Join(dname, f.Name)
+ result = append(result, &rpb.OutputFile{
+ Path: fname,
+ Digest: f.Digest,
+ IsExecutable: f.IsExecutable,
+ })
+ }
+ for _, d := range dir.Directories {
+ subdname := filepath.Join(dname, d.Name)
+ db, found := ds.Get(d.Digest)
+ if !found {
+ logger.Errorf("no dir for %s %s", subdname, d.Digest)
+ continue
+ }
+ subdir := &rpb.Directory{}
+ err := datasource.ReadProto(ctx, db, subdir)
+ if err != nil {
+ logger.Errorf("bad dir proto for %s %s: %v", subdname, d.Digest, err)
+ continue
+ }
+ result = append(result, traverseTree(ctx, filepath, subdname, subdir, ds)...)
+ }
+ if len(dir.Symlinks) > 0 {
+ logger.Errorf("symlinks exists in output dir %s: %q", dname, dir.Symlinks)
+ }
+ return result
+}
+
+func (g gomaOutput) outputDirectory(ctx context.Context, filepath clientFilePath, dname string, output *rpb.OutputDirectory, sema chan struct{}) {
logger := log.FromContext(ctx)
if output.TreeDigest == nil {
logger.Warnf("no tree digest in %s", dname)
@@ -238,35 +325,91 @@
}
ds.Set(d)
}
- g.traverseTree(ctx, filepath, dname, tree.Root, ds)
+ outputFiles := traverseTree(ctx, filepath, dname, tree.Root, ds)
+ g.outputFilesConcurrent(ctx, outputFiles, sema)
}
-func (g gomaOutput) traverseTree(ctx context.Context, filepath clientFilePath, dname string, dir *rpb.Directory, ds *digest.Store) {
- logger := log.FromContext(ctx)
- for _, f := range dir.Files {
- fname := filepath.Join(dname, f.Name)
- g.outputFile(ctx, fname, &rpb.OutputFile{
- Path: fname,
- Digest: f.Digest,
- IsExecutable: f.IsExecutable,
+// reduceRespSize attempts to reduce the encoded size of `g.gomaResp` to under `byteLimit`.
+// It replaces all file blobs with blob_type=FILE (embedded data) with blob_type=FILE_REF
+// (`content` in FileServer, `blob_type`=FILE). With each replaced blob, the response loses
+// `sizeof(content)` bytes and gains `sizeof(hash_key)` bytes. This will result in a net
+// reduction in encoded size as long as `sizeof(content)` > `sizeof(hash_key)`.
+//
+// See description of FileBlob for more information:
+// https://chromium.googlesource.com/infra/goma/client/+/bd9711495c9357eead845f0ae2d4eef92494c6d5/lib/goma_data.proto#17
+func (g gomaOutput) reduceRespSize(ctx context.Context, byteLimit int, sema chan struct{}) error {
+ origSize := proto.Size(g.gomaResp)
+ if origSize <= byteLimit {
+ return nil
+ }
+
+ toStoredFileBlob := func(ctx context.Context, input []byte, fs fpb.FileServiceClient) (*gomapb.FileBlob, error) {
+ size := int64(len(input))
+ blob := &gomapb.FileBlob{
+ BlobType: gomapb.FileBlob_FILE_REF.Enum(),
+ FileSize: proto.Int64(size),
+ }
+ var resp *gomapb.StoreFileResp
+ var err error
+ err = rpc.Retry{}.Do(ctx, func() error {
+ blob := &gomapb.FileBlob{
+ BlobType: gomapb.FileBlob_FILE.Enum(),
+ Content: input,
+ FileSize: proto.Int64(size),
+ }
+ resp, err = fs.StoreFile(ctx, &gomapb.StoreFileReq{
+ Blob: []*gomapb.FileBlob{blob},
+ RequesterInfo: requesterInfo(ctx),
+ })
+ return err
+ })
+ if err != nil {
+ return nil, fmt.Errorf("store blob failed: %v", err)
+ }
+ if len(resp.HashKey) != 1 {
+ return nil, fmt.Errorf("store blob got len(resp.HashKey)=%d, want=1", len(resp.HashKey))
+ }
+ if resp.HashKey[0] == "" {
+ return nil, fmt.Errorf("store blob failed with empty hash key")
+ }
+ blob.HashKey = resp.HashKey
+ return blob, nil
+ }
+
+ output := g.gomaResp.Result.Output
+ eg, ctx := errgroup.WithContext(ctx)
+ // For simplicity, store all blobs in FileServer rather than worrying about which ones to
+ // store.
+ // TODO: We can optimize this later.
+ for _, out := range output {
+ out := out
+ blob := out.Blob
+ if blob.GetBlobType() != gomapb.FileBlob_FILE {
+ continue
+ }
+ eg.Go(func() error {
+ sema <- struct{}{}
+ defer func() {
+ <-sema
+ }()
+
+ newBlob, err := toStoredFileBlob(ctx, blob.Content, g.gomaFile)
+ if err != nil {
+ return err
+ }
+ out.Blob = newBlob
+ return nil
})
}
- for _, d := range dir.Directories {
- subdname := filepath.Join(dname, d.Name)
- db, found := ds.Get(d.Digest)
- if !found {
- logger.Errorf("no dir for %s %s", subdname, d.Digest)
- continue
- }
- subdir := &rpb.Directory{}
- err := datasource.ReadProto(ctx, db, subdir)
- if err != nil {
- logger.Errorf("bad dir proto for %s %s: %v", subdname, d.Digest, err)
- continue
- }
- g.traverseTree(ctx, filepath, subdname, subdir, ds)
+
+ if err := eg.Wait(); err != nil {
+ return err
}
- if len(dir.Symlinks) > 0 {
- logger.Errorf("symlinks exists in output dir %s: %q", dname, dir.Symlinks)
+
+ // The result could still be too big if there are many FILE_REF blobs.
+ newSize := proto.Size(g.gomaResp)
+ if newSize > byteLimit {
+ return fmt.Errorf("new resp size: got=%d, want<=%d", newSize, byteLimit)
}
+ return nil
}
diff --git a/remoteexec/gomaoutput_test.go b/remoteexec/gomaoutput_test.go
index 4dc4681..10707a4 100644
--- a/remoteexec/gomaoutput_test.go
+++ b/remoteexec/gomaoutput_test.go
@@ -6,7 +6,9 @@
import (
"context"
+ "fmt"
"path"
+ "strings"
"testing"
rpb "github.com/bazelbuild/remote-apis/build/bazel/remote/execution/v2"
@@ -19,6 +21,284 @@
"go.chromium.org/goma/server/remoteexec/digest"
)
+type goutTestFile struct {
+ name string
+ data digest.Data
+ node *rpb.FileNode
+}
+
+func makeFileNode(name string) *goutTestFile {
+ // Contents are the same as the filename.
+ d := digest.Bytes(name, []byte(name))
+ return &goutTestFile{
+ name: name,
+ data: d,
+ node: &rpb.FileNode{
+ Name: name,
+ Digest: d.Digest(),
+ },
+ }
+}
+
+func makeDirNode(t *testing.T, name string, dir *rpb.Directory) *rpb.DirectoryNode {
+ dirdata, err := digest.Proto(dir)
+ if err != nil {
+ t.Fatalf("dir %s: %v", name, err)
+ }
+ return &rpb.DirectoryNode{
+ Name: name,
+ Digest: dirdata.Digest(),
+ }
+}
+
+func makeFileBlob(contents string) *gomapb.FileBlob {
+ return &gomapb.FileBlob{
+ BlobType: gomapb.FileBlob_FILE.Enum(),
+ Content: []byte(contents),
+ FileSize: proto.Int64(int64(len(contents))),
+ }
+}
+
+func TestToFileBlob(t *testing.T) {
+ ctx := context.Background()
+
+ cluster := &fakeCluster{
+ rbe: newFakeRBE(),
+ }
+ err := cluster.setup(ctx, cluster.rbe.instancePrefix)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer cluster.teardown()
+
+ f1 := makeFileNode("file1")
+ f2 := makeFileNode("file2")
+ f3 := makeFileNode("file3")
+ cluster.rbe.cas.Set(f1.data)
+ cluster.rbe.cas.Set(f2.data)
+ cluster.rbe.cas.Set(f3.data)
+
+ gout := gomaOutput{
+ bs: cluster.adapter.Client,
+ instance: path.Join(cluster.rbe.instancePrefix, "default_instance"),
+ gomaFile: cluster.adapter.GomaFile,
+ }
+
+ var blobs []*gomapb.FileBlob
+ for _, f := range []*goutTestFile{f1, f2, f3} {
+ blob, err := gout.toFileBlob(ctx, &rpb.OutputFile{
+ Path: f.name,
+ Digest: f.node.Digest,
+ })
+ if err != nil {
+ t.Errorf("toFileBlob returned err: %v", err)
+ continue
+ }
+ blobs = append(blobs, blob)
+ }
+
+ var want []*gomapb.FileBlob
+ for _, f := range []*goutTestFile{f1, f2, f3} {
+ want = append(want, makeFileBlob(f.name))
+ }
+ if diff := cmp.Diff(want, blobs, cmp.Comparer(proto.Equal)); diff != "" {
+ t.Errorf("output diff -want +got:\n%s", diff)
+ }
+}
+
+func TestToFileBlobLarge(t *testing.T) {
+ ctx := context.Background()
+
+ cluster := &fakeCluster{
+ rbe: newFakeRBE(),
+ }
+ err := cluster.setup(ctx, cluster.rbe.instancePrefix)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer cluster.teardown()
+
+ gout := gomaOutput{
+ bs: cluster.adapter.Client,
+ instance: path.Join(cluster.rbe.instancePrefix, "default_instance"),
+ gomaFile: cluster.adapter.GomaFile,
+ }
+
+ mibSize := int64(1024 * 1024)
+ bigData := digest.Bytes("fbig", make([]byte, 5*mibSize))
+ fBig := &goutTestFile{
+ name: "fbig",
+ data: bigData,
+ node: &rpb.FileNode{
+ Name: "fbig",
+ Digest: bigData.Digest(),
+ },
+ }
+ cluster.rbe.cas.Set(fBig.data)
+
+ blob, err := gout.toFileBlob(ctx, &rpb.OutputFile{
+ Path: fBig.name,
+ Digest: fBig.node.Digest,
+ })
+ if err != nil {
+ t.Errorf("toFileBlob returned err: %v", err)
+ }
+
+ var chunkBlobs []*gomapb.FileBlob
+ for i, hk := range blob.HashKey {
+ // Look up one blob at a time to avoid exceeding the 4MiB gRPC response size limit.
+ resp, err := gout.gomaFile.LookupFile(ctx, &gomapb.LookupFileReq{
+ HashKey: []string{hk},
+ })
+ if err != nil {
+ t.Errorf("gomaFile.LookupFile(HashKey[%d]=%s) returned err: %v", i, hk, err)
+ continue
+ }
+ if len(resp.Blob) != 1 {
+ t.Errorf("gomaFile.LookupFile(HashKey[%d]=%s) returned %d blobs, expected 1", i, hk, len(resp.Blob))
+ continue
+ }
+ chunkBlobs = append(chunkBlobs, resp.Blob[0])
+ }
+
+ wantBlob := &gomapb.FileBlob{
+ BlobType: gomapb.FileBlob_FILE_META.Enum(),
+ FileSize: proto.Int64(5 * mibSize),
+ HashKey: blob.HashKey, // These don't need to be checked.
+ }
+ if diff := cmp.Diff(wantBlob, blob, cmp.Comparer(proto.Equal)); diff != "" {
+ t.Errorf("output diff -want +got:\n%s", diff)
+ }
+
+ wantChunkBlobs := []*gomapb.FileBlob{
+ {
+ BlobType: gomapb.FileBlob_FILE_CHUNK.Enum(),
+ Offset: proto.Int64(0),
+ Content: make([]byte, 2*mibSize),
+ FileSize: proto.Int64(5 * mibSize),
+ }, {
+ BlobType: gomapb.FileBlob_FILE_CHUNK.Enum(),
+ Offset: proto.Int64(2 * mibSize),
+ Content: make([]byte, 2*mibSize),
+ FileSize: proto.Int64(5 * mibSize),
+ }, {
+ BlobType: gomapb.FileBlob_FILE_CHUNK.Enum(),
+ Offset: proto.Int64(4 * mibSize),
+ Content: make([]byte, mibSize),
+ FileSize: proto.Int64(5 * mibSize),
+ },
+ }
+ if diff := cmp.Diff(wantChunkBlobs, chunkBlobs, cmp.Comparer(proto.Equal)); diff != "" {
+ t.Errorf("output diff -want +got:\n%s", diff)
+ }
+}
+
+func TestOutputFile(t *testing.T) {
+ ctx := context.Background()
+
+ cluster := &fakeCluster{
+ rbe: newFakeRBE(),
+ }
+ err := cluster.setup(ctx, cluster.rbe.instancePrefix)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer cluster.teardown()
+
+ f1 := makeFileNode("file1")
+ f2 := makeFileNode("file2")
+ f3 := makeFileNode("file3")
+ cluster.rbe.cas.Set(f1.data)
+ cluster.rbe.cas.Set(f2.data)
+ cluster.rbe.cas.Set(f3.data)
+
+ gout := gomaOutput{
+ gomaResp: &gomapb.ExecResp{
+ Result: &gomapb.ExecResult{},
+ },
+ bs: cluster.adapter.Client,
+ instance: path.Join(cluster.rbe.instancePrefix, "default_instance"),
+ gomaFile: cluster.adapter.GomaFile,
+ }
+
+ for _, f := range []*goutTestFile{f1, f2, f3} {
+ gout.outputFile(ctx, f.name, &rpb.OutputFile{
+ Path: f.name,
+ Digest: f.node.Digest,
+ })
+ }
+
+ if len(gout.gomaResp.ErrorMessage) > 0 {
+ t.Errorf("resp errorMessage %q; want no error", gout.gomaResp.ErrorMessage)
+ }
+ var want []*gomapb.ExecResult_Output
+ for _, f := range []*goutTestFile{f1, f2, f3} {
+ want = append(want, &gomapb.ExecResult_Output{
+ Filename: proto.String(f.name),
+ Blob: makeFileBlob(f.name),
+ IsExecutable: proto.Bool(false),
+ })
+ }
+ if diff := cmp.Diff(want, gout.gomaResp.Result.Output, cmp.Comparer(proto.Equal)); diff != "" {
+ t.Errorf("output diff -want +got:\n%s", diff)
+ }
+}
+
+func TestOutputFilesConcurrent(t *testing.T) {
+ ctx := context.Background()
+
+ cluster := &fakeCluster{
+ rbe: newFakeRBE(),
+ }
+ err := cluster.setup(ctx, cluster.rbe.instancePrefix)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer cluster.teardown()
+
+ var files []*goutTestFile
+ for i := 0; i < 1000; i++ {
+ f := makeFileNode(fmt.Sprintf("file%d", i))
+ files = append(files, f)
+ cluster.rbe.cas.Set(f.data)
+ }
+
+ gout := gomaOutput{
+ gomaResp: &gomapb.ExecResp{
+ Result: &gomapb.ExecResult{},
+ },
+ bs: cluster.adapter.Client,
+ instance: path.Join(cluster.rbe.instancePrefix, "default_instance"),
+ gomaFile: cluster.adapter.GomaFile,
+ }
+
+ var outputFiles []*rpb.OutputFile
+ for _, f := range files {
+ outputFiles = append(outputFiles, &rpb.OutputFile{
+ Path: f.name,
+ Digest: f.node.Digest,
+ })
+ }
+
+ sema := make(chan struct{}, 20)
+ gout.outputFilesConcurrent(ctx, outputFiles, sema)
+
+ if len(gout.gomaResp.ErrorMessage) > 0 {
+ t.Errorf("resp errorMessage %q; want no error", gout.gomaResp.ErrorMessage)
+ }
+ var want []*gomapb.ExecResult_Output
+ for _, f := range files {
+ want = append(want, &gomapb.ExecResult_Output{
+ Filename: proto.String(f.name),
+ Blob: makeFileBlob(f.name),
+ IsExecutable: proto.Bool(false),
+ })
+ }
+ if diff := cmp.Diff(want, gout.gomaResp.Result.Output, cmp.Comparer(proto.Equal)); diff != "" {
+ t.Errorf("output diff -want +got:\n%s", diff)
+ }
+}
+
func TestOutputDirectory(t *testing.T) {
ctx := context.Background()
@@ -31,56 +311,27 @@
}
defer cluster.teardown()
- f1 := digest.Bytes("file1", []byte("file1"))
- f2 := digest.Bytes("file2", []byte("file2"))
- f3 := digest.Bytes("file3", []byte("file3"))
- cluster.rbe.cas.Set(f1)
- cluster.rbe.cas.Set(f2)
- cluster.rbe.cas.Set(f3)
+ f1 := makeFileNode("file1")
+ f2 := makeFileNode("file2")
+ f3 := makeFileNode("file3")
+ cluster.rbe.cas.Set(f1.data)
+ cluster.rbe.cas.Set(f2.data)
+ cluster.rbe.cas.Set(f3.data)
dir2 := &rpb.Directory{
- Files: []*rpb.FileNode{
- {
- Name: "file3",
- Digest: f3.Digest(),
- },
- },
- }
- dir2data, err := digest.Proto(dir2)
- if err != nil {
- t.Fatalf("dir2: %v", err)
+ Files: []*rpb.FileNode{f3.node},
}
dir1 := &rpb.Directory{
- Files: []*rpb.FileNode{
- {
- Name: "file2",
- Digest: f2.Digest(),
- },
- },
+ Files: []*rpb.FileNode{f2.node},
Directories: []*rpb.DirectoryNode{
- {
- Name: "dir2",
- Digest: dir2data.Digest(),
- },
+ makeDirNode(t, "dir2", dir2),
},
}
- dir1data, err := digest.Proto(dir1)
- if err != nil {
- t.Fatalf("dir1: %v", err)
- }
td, err := cluster.rbe.setProto(ctx, &rpb.Tree{
Root: &rpb.Directory{
- Files: []*rpb.FileNode{
- {
- Name: "file1",
- Digest: f1.Digest(),
- },
- },
+ Files: []*rpb.FileNode{f1.node},
Directories: []*rpb.DirectoryNode{
- {
- Name: "dir1",
- Digest: dir1data.Digest(),
- },
+ makeDirNode(t, "dir1", dir1),
},
},
Children: []*rpb.Directory{
@@ -102,40 +353,29 @@
gomaFile: cluster.adapter.GomaFile,
}
+ sema := make(chan struct{}, 2)
gout.outputDirectory(ctx, filepath, "out", &rpb.OutputDirectory{
Path: "out",
TreeDigest: td,
- })
+ }, sema)
if len(gout.gomaResp.ErrorMessage) > 0 {
t.Errorf("resp errorMessage %q; want no error", gout.gomaResp.ErrorMessage)
}
want := []*gomapb.ExecResult_Output{
{
- Filename: proto.String("out/file1"),
- Blob: &gomapb.FileBlob{
- BlobType: gomapb.FileBlob_FILE.Enum(),
- Content: []byte("file1"),
- FileSize: proto.Int64(int64(len("file1"))),
- },
+ Filename: proto.String("out/file1"),
+ Blob: makeFileBlob("file1"),
IsExecutable: proto.Bool(false),
},
{
- Filename: proto.String("out/dir1/file2"),
- Blob: &gomapb.FileBlob{
- BlobType: gomapb.FileBlob_FILE.Enum(),
- Content: []byte("file2"),
- FileSize: proto.Int64(int64(len("file2"))),
- },
+ Filename: proto.String("out/dir1/file2"),
+ Blob: makeFileBlob("file2"),
IsExecutable: proto.Bool(false),
},
{
- Filename: proto.String("out/dir1/dir2/file3"),
- Blob: &gomapb.FileBlob{
- BlobType: gomapb.FileBlob_FILE.Enum(),
- Content: []byte("file3"),
- FileSize: proto.Int64(int64(len("file3"))),
- },
+ Filename: proto.String("out/dir1/dir2/file3"),
+ Blob: makeFileBlob("file3"),
IsExecutable: proto.Bool(false),
},
}
@@ -143,3 +383,284 @@
t.Errorf("output diff -want +got:\n%s", diff)
}
}
+
+func TestTraverseTree(t *testing.T) {
+ ctx := context.Background()
+
+ cluster := &fakeCluster{
+ rbe: newFakeRBE(),
+ }
+ err := cluster.setup(ctx, cluster.rbe.instancePrefix)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer cluster.teardown()
+
+ f1 := makeFileNode("file1")
+ f2 := makeFileNode("file2")
+ f3 := makeFileNode("file3")
+ cluster.rbe.cas.Set(f1.data)
+ cluster.rbe.cas.Set(f2.data)
+ cluster.rbe.cas.Set(f3.data)
+ dir2 := &rpb.Directory{
+ Files: []*rpb.FileNode{f3.node},
+ }
+ dir1 := &rpb.Directory{
+ Files: []*rpb.FileNode{f2.node},
+ Directories: []*rpb.DirectoryNode{
+ makeDirNode(t, "dir2", dir2),
+ },
+ }
+ tree := &rpb.Tree{
+ Root: &rpb.Directory{
+ Files: []*rpb.FileNode{f1.node},
+ Directories: []*rpb.DirectoryNode{
+ makeDirNode(t, "dir1", dir1),
+ },
+ },
+ Children: []*rpb.Directory{
+ dir1,
+ dir2,
+ },
+ }
+
+ ds := digest.NewStore()
+ for _, c := range tree.Children {
+ d, err := digest.Proto(c)
+ if err != nil {
+ t.Errorf("digest for children %s: %v", c, err)
+ continue
+ }
+ ds.Set(d)
+ }
+
+ var filepath posixpath.FilePath
+
+ result := traverseTree(ctx, filepath, "out", tree.Root, ds)
+
+ want := []*rpb.OutputFile{
+ {
+ Path: "out/file1",
+ Digest: f1.node.Digest,
+ }, {
+ Path: "out/dir1/file2",
+ Digest: f2.node.Digest,
+ }, {
+ Path: "out/dir1/dir2/file3",
+ Digest: f3.node.Digest,
+ },
+ }
+ if diff := cmp.Diff(want, result, cmp.Comparer(proto.Equal)); diff != "" {
+ t.Errorf("output diff -want +got:\n%s", diff)
+ }
+}
+
+func TestReduceRespSize(t *testing.T) {
+ ctx := context.Background()
+
+ contents := []string{
+ strings.Repeat("###", 10),
+ strings.Repeat("###", 20),
+ strings.Repeat("###", 30),
+ strings.Repeat("###", 40),
+ strings.Repeat("###", 50),
+ strings.Repeat("###", 60),
+ strings.Repeat("###", 70),
+ strings.Repeat("###", 80),
+ strings.Repeat("###", 90),
+ strings.Repeat("###", 100),
+ }
+
+ var outputs []*gomapb.ExecResult_Output
+ for i, content := range contents {
+ outputs = append(outputs, &gomapb.ExecResult_Output{
+ Filename: proto.String(fmt.Sprintf("file%d", i)),
+ Blob: makeFileBlob(content),
+ IsExecutable: proto.Bool(false),
+ })
+ }
+ convertToStored := func(in *gomapb.ExecResult_Output) *gomapb.ExecResult_Output {
+ return &gomapb.ExecResult_Output{
+ Filename: in.Filename,
+ Blob: &gomapb.FileBlob{
+ BlobType: gomapb.FileBlob_FILE_REF.Enum(),
+ FileSize: proto.Int64(in.Blob.GetFileSize()),
+ // Hashkey will be checked separately.
+ },
+ IsExecutable: in.IsExecutable,
+ }
+ }
+
+ makeResp := func(outputs []*gomapb.ExecResult_Output) *gomapb.ExecResp {
+ return &gomapb.ExecResp{
+ Result: &gomapb.ExecResult{
+ ExitStatus: proto.Int32(1234),
+ StdoutBuffer: []byte("Hello"),
+ StderrBuffer: []byte("Goodbye"),
+ Output: outputs,
+ },
+ }
+ }
+ respSizeAllOutputs := proto.Size(makeResp(outputs))
+
+ metaOutput := &gomapb.ExecResult_Output{
+ Filename: proto.String("bigfile"),
+ Blob: &gomapb.FileBlob{
+ BlobType: gomapb.FileBlob_FILE_META.Enum(),
+ FileSize: proto.Int64(5 * 1024 * 1024),
+ HashKey: []string{"0123", "4567", "89ab"},
+ },
+ IsExecutable: proto.Bool(false),
+ }
+
+ for _, tc := range []struct {
+ desc string
+ output []*gomapb.ExecResult_Output
+ byteLimit int
+ wantOutput []*gomapb.ExecResult_Output
+ wantErr bool
+ }{
+ {
+ desc: "empty",
+ output: []*gomapb.ExecResult_Output{},
+ byteLimit: 1,
+ wantErr: true,
+ }, {
+ desc: "no change in size",
+ output: outputs,
+ byteLimit: respSizeAllOutputs,
+ wantOutput: outputs,
+ }, {
+ desc: "limit too high",
+ output: outputs,
+ byteLimit: respSizeAllOutputs * 2,
+ wantOutput: outputs,
+ }, {
+ desc: "limit too low",
+ output: outputs,
+ byteLimit: 1,
+ wantErr: true,
+ }, {
+ desc: "blob smaller than hashkey",
+ output: []*gomapb.ExecResult_Output{
+ outputs[0],
+ outputs[1], // The largest blob has size 3 * 20 = 60, < hashkey size=64
+ },
+ byteLimit: proto.Size(makeResp([]*gomapb.ExecResult_Output{
+ outputs[0],
+ outputs[1],
+ })) - 1,
+ wantErr: true,
+ }, {
+ desc: "remove all blobs",
+ byteLimit: respSizeAllOutputs - 600,
+ output: outputs,
+ wantOutput: []*gomapb.ExecResult_Output{
+ convertToStored(outputs[0]),
+ convertToStored(outputs[1]),
+ convertToStored(outputs[2]),
+ convertToStored(outputs[3]),
+ convertToStored(outputs[4]),
+ convertToStored(outputs[5]),
+ convertToStored(outputs[6]),
+ convertToStored(outputs[7]),
+ convertToStored(outputs[8]),
+ convertToStored(outputs[9]),
+ },
+ }, {
+ desc: "skip non-FILE",
+ byteLimit: respSizeAllOutputs - 600,
+ output: []*gomapb.ExecResult_Output{
+ outputs[0],
+ outputs[1],
+ outputs[2],
+ outputs[3],
+ outputs[4],
+ metaOutput,
+ outputs[5],
+ outputs[6],
+ outputs[7],
+ outputs[8],
+ outputs[9],
+ },
+ wantOutput: []*gomapb.ExecResult_Output{
+ convertToStored(outputs[0]),
+ convertToStored(outputs[1]),
+ convertToStored(outputs[2]),
+ convertToStored(outputs[3]),
+ convertToStored(outputs[4]),
+ metaOutput,
+ convertToStored(outputs[5]),
+ convertToStored(outputs[6]),
+ convertToStored(outputs[7]),
+ convertToStored(outputs[8]),
+ convertToStored(outputs[9]),
+ },
+ },
+ } {
+ t.Run(tc.desc, func(t *testing.T) {
+ cluster := &fakeCluster{}
+ err := cluster.setup(ctx, "")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer cluster.teardown()
+
+ gout := gomaOutput{
+ gomaFile: cluster.adapter.GomaFile,
+ // Avoid changing the original gomapb.ExecResult_Output values during test.
+ gomaResp: proto.Clone(makeResp(tc.output)).(*gomapb.ExecResp),
+ }
+ sema := make(chan struct{}, 3)
+ err = gout.reduceRespSize(ctx, tc.byteLimit, sema)
+ result := gout.gomaResp.Result
+
+ if err != nil && !tc.wantErr {
+ t.Errorf("got err=%v, want nil", err)
+ return
+ }
+ if tc.wantErr {
+ if err == nil {
+ t.Errorf("got err=nil, want !nil")
+ }
+ return
+ }
+
+ if len(result.Output) != len(tc.wantOutput) {
+ t.Errorf("got len(result.Output)=%d, want %d", len(result.Output), len(tc.wantOutput))
+ return
+ }
+ if proto.Size(result) > tc.byteLimit {
+ t.Errorf("got size=%d, want<=%d", proto.Size(result), tc.byteLimit)
+ }
+
+ // Since we don't have expected hash keys, check them separately and remove them from the result.
+ for i, resOut := range result.Output {
+ if resOut.Blob.GetBlobType() != gomapb.FileBlob_FILE_REF {
+ continue
+ }
+ hks := resOut.Blob.HashKey
+ if len(hks) != 1 {
+ t.Errorf("len(hks)=%d, want=1", len(hks))
+ }
+ resOut.Blob.HashKey = nil
+ resp, err := gout.gomaFile.LookupFile(ctx, &gomapb.LookupFileReq{HashKey: hks})
+ if err != nil {
+ t.Errorf("LookupFile failed: %v", err)
+ }
+ // Make sure the stored blob is the same as the original blob.
+ wantBlobs := []*gomapb.FileBlob{tc.output[i].Blob}
+ if !cmp.Equal(resp.Blob, wantBlobs, cmp.Comparer(proto.Equal)) {
+ t.Errorf("at i=%d, resp.Blob=%v, want=%v", i, resp.Blob[0], wantBlobs)
+ }
+ }
+
+ for i, resOut := range result.Output {
+ wantOut := tc.wantOutput[i]
+ if !cmp.Equal(resOut, wantOut, cmp.Comparer(proto.Equal)) {
+ t.Errorf("at i=%d, resOut=%v, want=%v", i, resOut, wantOut)
+ }
+ }
+ })
+ }
+}