delete all other than README.md

Bug: b/341211526
Change-Id: I5c6b849c78d01e3fbac4367412d6eb0ad479fd82
diff --git a/.gitallowed b/.gitallowed
deleted file mode 100644
index b399cae..0000000
--- a/.gitallowed
+++ /dev/null
@@ -1,2 +0,0 @@
-687418631491-r6m1c3pr0lth5atp4ie07f03ae8omefc.apps.googleusercontent.com
-187410957766-f28toeml294bk5mm7nr2jn2rup1rvtjj.apps.googleusercontent.com
diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index c4f5265..0000000
--- a/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-loadtest/testcases/gen
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
deleted file mode 100644
index 96d750d..0000000
--- a/CONTRIBUTING.md
+++ /dev/null
@@ -1,26 +0,0 @@
-# How to Contribute
-
-We'd love to accept your patches and contributions to this project. There are
-just a few small guidelines you need to follow.
-
-## Contributor License Agreement
-
-Contributions to this project must be accompanied by a Contributor License
-Agreement. You (or your employer) retain the copyright to your contribution,
-this simply gives us permission to use and redistribute your contributions as
-part of the project. Head over to <https://cla.developers.google.com/> to see
-your current agreements on file or to sign a new one.
-
-You generally only need to submit a CLA once, so if you've already submitted one
-(even if it was for a different project), you probably don't need to do it
-again.
-
-## Code reviews
-
-All submissions, including submissions by project members, require review. We
-use `git cl` (via `depot_tools`) for this process.
-
-# Community Guidelines
-
-This project follows
-[Google's Open Source Community Guidelines](https://opensource.google.com/conduct/).
diff --git a/LICENSE b/LICENSE
deleted file mode 100644
index 4d35958..0000000
--- a/LICENSE
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2019 The Goma Authors. All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-//    * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-//    * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-//    * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/OWNERS b/OWNERS
deleted file mode 100644
index f9b879b..0000000
--- a/OWNERS
+++ /dev/null
@@ -1,7 +0,0 @@
-jojwang@chromium.org
-jojwang@google.com
-jwata@google.com
-tikuta@chromium.org
-tikuta@google.com
-ukai@chromium.org
-ukai@google.com
diff --git a/README.md b/README.md
index beac39e..390abb0 100644
--- a/README.md
+++ b/README.md
@@ -2,114 +2,3 @@
 
 **Goma is not maintained any more, please use
 [reclient](https://github.com/bazelbuild/reclient) instead.**
-
-*Goma* is a distributed compiler service for open-source project such as
-Chromium and Android. It's some kind of replacement of distcc+ccache.
-
-This is reference implementation of server code to be used with
-[Goma client](https://chromium.googlesource.com/infra/goma/client).
-
-[TOC]
-
-## Dependencies
-
-The Goma server uses a backend service that implements the [Remote Execution
-API](https://github.com/bazelbuild/remote-apis)
-to distribute compile requests across a collection of worker machines and to
-cache the results of compilations. The Remote Execution API is an open-source
-standard, with multiple service implementations. The Goma server has been tested
-with Google's internal Remote Build Execution service, but could use other
-service implementations with some minor tweaks to the service code.
-
-## How to build
-
-Goma server can be built on Linux.
-
-```
-$ GO111MODULE=on go get go.chromium.org/goma/server/cmd/remoteexec_proxy
-```
-
-You will get the binary in `$(go env GOPATH)/bin`.
-
-## How to run
-
-`remoteexec_proxy` is a single server that acts as proxy server
-between Goma client and Remote Execution API.
-
-```
-$ remoteexec_proxy --port $PORT \
-   --platform-container-image "docker://...@sha256:..." \
-   --remoteexec-addr $REMOTEEXEC_ADDR \
-   --remote-instance-name $REMOTE_INSTANCE_NAME
-```
-
-for chromium, platform container image would be something like
-```
-FROM marketing.gcr.io/google/ubuntu1804:latest
-ENV DEBIAN_FRONTEND noninteractive
-
-RUN apt-get -y update \
-  && \
-  apt-get install -f -y build-essential lsb-release python \
-  && \
-  rm -rf /var/lib/apt/lists/*
-
-```
-
-If Remote Execution API requires service account,
-specify service account JSON file for Remote Execution API by
-`--service-account-json`.
-
-Running user is granted by default. If you need to allow other users, you
-need to specify them by `--allowed-users`.
-
-Log messages will be output to stderr.
-
-### How to use
-
-Install goma client. We provide prebuilt binary with `cipd`, which
-is available in [`depot_tools`](https://commondatastorage.googleapis.com/chrome-infra-docs/flat/depot_tools/docs/html/depot_tools.html).
-
-```
-$ cipd install infra/goma/client/linux-amd64 -root ${HOME}/goma
-```
-
-or follow the
-[build instructions](https://chromium.googlesource.com/infra/goma/client#build) to
-build your own local version of Goma client before running the server code,
-and install it in `$HOME/goma`.
-
-Need to authenticate Goma client before use.
-
-```
-$ $HOME/goma/goma_auth.py login
-```
-
-Specify hostname in `$GOMA_SERVER_HOST`
-and port in `$GOMA_SERVER_PORT`, along with a few other environment flags.
-
-```
-$ export GOMA_SERVER_HOST='host-of-remoteexec_proxy-running'
-$ export GOMA_SERVER_PORT='port-of-remoteexec_proxy-running'
-$ export GOMA_USE_SSL=false
-$ export GOMA_ARBITRARY_TOOLCHAIN_SUPPORT=true
-```
-
-For example, if you are running remoteexec_proxy locally with `--port 5050`, use:
-
-```
-$ export GOMA_SERVER_HOST=localhost
-$ export GOMA_SERVER_PORT=5050
-```
-
-Finally, start Goma client:
-
-```
-$ $HOME/goma/goma_ctl.py ensure_start
-```
-
-and in chromium tree.
-```
-$ rm -f out/Release/obj/base/base/base64.o
-$ GOMA_USE_LOCAL=false autoninja -C out/Release obj/base/base/base64.o
-```
diff --git a/auth/account/empty.go b/auth/account/empty.go
deleted file mode 100644
index f5e4f5f..0000000
--- a/auth/account/empty.go
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2018 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.
-
-package account
-
-import "errors"
-
-// Empty doesn't provide any service accounts.
-type Empty struct{}
-
-func (e Empty) New(name string) (Account, error) {
-	return nil, errors.New("empty has no service account")
-}
diff --git a/auth/account/mint.go b/auth/account/mint.go
deleted file mode 100644
index 87ed4f3..0000000
--- a/auth/account/mint.go
+++ /dev/null
@@ -1,141 +0,0 @@
-// Copyright 2018 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.
-
-// Package account manages service account.
-package account
-
-import (
-	"context"
-	"io/ioutil"
-	"path/filepath"
-	"reflect"
-	"sync"
-
-	"golang.org/x/oauth2"
-	"golang.org/x/oauth2/google"
-	"golang.org/x/oauth2/jwt"
-)
-
-// Account represents service account.
-type Account interface {
-	// Equals compare account with other.
-	Equals(other Account) bool
-
-	// Token generates new oauth2 token for the account.
-	Token(ctx context.Context) (*oauth2.Token, error)
-}
-
-// Pool manages service accounts.
-type Pool interface {
-	// New creates new account for name.
-	New(name string) (Account, error)
-}
-
-// JSONDir is a Pool with json files.
-// It should be used for experiments only.
-// we need to rotate keys.
-// It uses application default credential, if "default" is requested.
-// https://cloud.google.com/docs/authentication/production
-type JSONDir struct {
-	Dir    string
-	Scopes []string
-}
-
-type serviceAccount struct {
-	name   string
-	config *jwt.Config
-
-	mu sync.Mutex
-	t  *oauth2.Token
-}
-
-type defaultServiceAccount struct {
-	scopes []string
-	mu     sync.Mutex
-	cred   *google.Credentials
-	t      *oauth2.Token
-}
-
-// New creates new account by loading json file in the dir.
-// if name is "default", returns default service account instead.
-func (j JSONDir) New(name string) (Account, error) {
-	if name == "default" {
-		return &defaultServiceAccount{scopes: j.Scopes}, nil
-	}
-	keyFile := filepath.Join(j.Dir, name+".json")
-	jsonKey, err := ioutil.ReadFile(keyFile)
-	if err != nil {
-		return nil, err
-	}
-	config, err := google.JWTConfigFromJSON(jsonKey, j.Scopes...)
-	if err != nil {
-		return nil, err
-	}
-	return &serviceAccount{
-		name:   name,
-		config: config,
-	}, nil
-}
-
-// Equals checks other account has same name and config.
-func (sa *serviceAccount) Equals(other Account) bool {
-	if other == nil {
-		return false
-	}
-	osa, ok := other.(*serviceAccount)
-	if !ok {
-		return false
-	}
-	if sa.name != osa.name {
-		return false
-	}
-	return reflect.DeepEqual(sa.config, osa.config)
-}
-
-// Token generates new oauth2 token.
-func (sa *serviceAccount) Token(ctx context.Context) (*oauth2.Token, error) {
-	sa.mu.Lock()
-	defer sa.mu.Unlock()
-	if !sa.t.Valid() {
-		var err error
-		sa.t, err = sa.config.TokenSource(ctx).Token()
-		if err != nil {
-			return nil, err
-		}
-	}
-	return sa.t, nil
-}
-
-// Equals checks other account has same default service account.
-func (sa *defaultServiceAccount) Equals(other Account) bool {
-	if other == nil {
-		return false
-	}
-	_, ok := other.(*defaultServiceAccount)
-	return ok
-}
-
-// Token generates new oauth2 token.
-func (sa *defaultServiceAccount) Token(ctx context.Context) (*oauth2.Token, error) {
-	sa.mu.Lock()
-	defer sa.mu.Unlock()
-	if sa.cred == nil {
-		var err error
-		sa.cred, err = google.FindDefaultCredentials(ctx, sa.scopes...)
-		if err != nil {
-			return nil, err
-		}
-	}
-	if !sa.t.Valid() {
-		var err error
-		sa.t, err = sa.cred.TokenSource.Token()
-		if err != nil {
-			return nil, err
-		}
-	}
-	return sa.t, nil
-}
-
-// TODO: provide another account pool using SignJWT
-// TODO: provide another account pool using luci-token-server.
diff --git a/auth/acl/acl.go b/auth/acl/acl.go
deleted file mode 100644
index 848e8df..0000000
--- a/auth/acl/acl.go
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2018 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.
-
-// Package acl performs access control with ACL.
-package acl
-
-import "context"
-
-// ACL manages access control list.
-type ACL struct {
-	Loader
-	Checker
-}
-
-// Update loads acl by Loader and sets it to Checker.
-func (a *ACL) Update(ctx context.Context) error {
-	if a.Loader == nil {
-		a.Loader = DefaultAllowlist{}
-	}
-	config, err := a.Loader.Load(ctx)
-	if err != nil {
-		return err
-	}
-	return a.Checker.Set(ctx, config)
-}
diff --git a/auth/acl/allowlist.go b/auth/acl/allowlist.go
deleted file mode 100644
index dcc78bc..0000000
--- a/auth/acl/allowlist.go
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2018 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.
-
-package acl
-
-import (
-	"context"
-
-	pb "go.chromium.org/goma/server/proto/auth"
-)
-
-const (
-	// new goma client client_id
-	// https://chromium.googlesource.com/infra/goma/client/+/70685d6cbb19c108d8abf2235edd2d02bed8dded/client/oauth2.cc#72
-	GomaClientClientID = "687418631491-r6m1c3pr0lth5atp4ie07f03ae8omefc.apps.googleusercontent.com"
-)
-
-// DefaultAllowlist is a loader to provide default allow list, which pass through EUC.
-type DefaultAllowlist struct{}
-
-func (DefaultAllowlist) Load(ctx context.Context) (*pb.ACL, error) {
-	return &pb.ACL{
-		Groups: []*pb.Group{
-			{
-				Id:          "chrome-bot",
-				Description: "chromium buildbot service account",
-				Emails:      []string{"goma-client@chrome-infra-auth.iam.gserviceaccount.com"},
-			},
-			{
-				Id:          "chromium-swarm-dev",
-				Description: "staging chromium-swarm-dev bots. http://b/63818232 http://crbug.com/684735",
-				Emails:      []string{"pool-chrome@chromium-swarm-dev.iam.gserviceaccount.com"},
-			},
-			{
-				Id:       "googler",
-				Audience: GomaClientClientID,
-				Domains:  []string{"google.com"},
-			},
-		},
-	}, nil
-}
diff --git a/auth/acl/allowlist_test.go b/auth/acl/allowlist_test.go
deleted file mode 100644
index 4c76b8a..0000000
--- a/auth/acl/allowlist_test.go
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright 2018 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.
-
-package acl
-
-import (
-	"context"
-	"fmt"
-	"reflect"
-	"testing"
-
-	"golang.org/x/oauth2"
-
-	"go.chromium.org/goma/server/auth"
-)
-
-func TestDefaultAllowlist(t *testing.T) {
-	a := ACL{
-		Loader: DefaultAllowlist{},
-	}
-	ctx := context.Background()
-	err := a.Update(ctx)
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	testcases := []struct {
-		email    string
-		audience string
-		wantErr  bool
-	}{
-		{
-			email:    "example@google.com",
-			audience: GomaClientClientID,
-			wantErr:  false,
-		},
-		{
-			email:    "example@chromium.org",
-			audience: GomaClientClientID,
-			wantErr:  true,
-		},
-		{
-			email:    "goma-client@chrome-infra-auth.iam.gserviceaccount.com",
-			audience: "goma-client@chrome-infra-auth.iam.gserviceaccount.com",
-			wantErr:  false,
-		},
-		{
-			email:    "me@google.com@example.com",
-			audience: GomaClientClientID,
-			wantErr:  true,
-		},
-		{
-			email:    "example@google.com",
-			audience: "other-xxx.apps.googleusercontent.com",
-			wantErr:  true,
-		},
-	}
-
-	for _, tc := range testcases {
-		token := &oauth2.Token{
-			AccessToken: fmt.Sprintf("token-value-%s", tc.email),
-		}
-		_, got, err := a.CheckToken(ctx, token, &auth.TokenInfo{
-			Email:    tc.email,
-			Audience: tc.audience,
-		})
-		if tc.wantErr {
-			if err == nil {
-				t.Errorf("a.CheckToken(ctx, token, %q)=_, _, nil; want err", tc.email)
-			}
-			continue
-		}
-		if !reflect.DeepEqual(got, token) {
-			t.Errorf("a.CheckToken(ctx, %v, %q)=_, %v, nil; want _, %v, nil", token, tc.email, got, token)
-		}
-	}
-}
diff --git a/auth/acl/checker.go b/auth/acl/checker.go
deleted file mode 100644
index f225309..0000000
--- a/auth/acl/checker.go
+++ /dev/null
@@ -1,202 +0,0 @@
-// Copyright 2018 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.
-
-package acl
-
-import (
-	"context"
-	"errors"
-	"fmt"
-	"strings"
-	"sync"
-
-	"golang.org/x/oauth2"
-	"google.golang.org/grpc"
-	"google.golang.org/grpc/codes"
-	"google.golang.org/grpc/status"
-	"google.golang.org/protobuf/proto"
-
-	"go.chromium.org/goma/server/auth"
-	"go.chromium.org/goma/server/auth/account"
-	"go.chromium.org/goma/server/log"
-	pb "go.chromium.org/goma/server/proto/auth"
-)
-
-// AuthDB provides authentication database; user groups.
-type AuthDB interface {
-	IsMember(ctx context.Context, email, group string) (bool, error)
-}
-
-// Checker checks token.
-type Checker struct {
-	AuthDB
-	account.Pool
-
-	mu     sync.RWMutex
-	config *pb.ACL
-
-	accounts map[string]account.Account
-}
-
-// Set sets config in the checker.
-func (c *Checker) Set(ctx context.Context, config *pb.ACL) error {
-	c.mu.Lock()
-	defer c.mu.Unlock()
-
-	if c.Pool == nil {
-		c.Pool = account.Empty{}
-	}
-	if c.accounts == nil {
-		c.accounts = make(map[string]account.Account)
-	}
-
-	logger := log.FromContext(ctx)
-
-	seen := make(map[string]bool)
-
-	for _, g := range config.Groups {
-		if g.ServiceAccount == "" {
-			continue
-		}
-		if seen[g.ServiceAccount] {
-			continue
-		}
-		sa, err := c.Pool.New(g.ServiceAccount)
-		if err != nil {
-			return fmt.Errorf("service account %q: %v", g.ServiceAccount, err)
-		}
-		seen[g.ServiceAccount] = true
-		if sa.Equals(c.accounts[g.ServiceAccount]) {
-			// no diff
-			logger.Infof("service account %s: no change", g.ServiceAccount)
-			continue
-		}
-		logger.Infof("service account %s: update", g.ServiceAccount)
-		c.accounts[g.ServiceAccount] = sa
-	}
-	for sa := range c.accounts {
-		if !seen[sa] {
-			logger.Infof("service account %s: deleted", sa)
-			delete(c.accounts, sa)
-		}
-	}
-	logger.Infof("acl updated")
-	c.config = proto.Clone(config).(*pb.ACL)
-	return nil
-}
-
-var errNoMatchingGroup = errors.New("no matching group")
-
-// FindGroup finds a group for tokenInfo.
-func (c *Checker) FindGroup(ctx context.Context, tokenInfo *auth.TokenInfo) (*pb.Group, error) {
-	logger := log.FromContext(ctx)
-
-	c.mu.RLock()
-	defer c.mu.RUnlock()
-
-	for _, g := range c.config.GetGroups() {
-		ok, err := checkGroup(ctx, tokenInfo, g, c.AuthDB)
-		if err != nil {
-			logger.Errorf("filed to check group %s for %q %q: %v", g.Id, tokenInfo.Email, tokenInfo.Audience, err)
-			return nil, err
-		}
-		if !ok {
-			continue
-		}
-		return g, nil
-	}
-	return nil, fmt.Errorf("no group for %q %q: %w", tokenInfo.Email, tokenInfo.Audience, errNoMatchingGroup)
-}
-
-// CheckToken checks token and returns group id and token used for backend API.
-func (c *Checker) CheckToken(ctx context.Context, token *oauth2.Token, tokenInfo *auth.TokenInfo) (string, *oauth2.Token, error) {
-
-	logger := log.FromContext(ctx)
-
-	g, err := c.FindGroup(ctx, tokenInfo)
-	if err != nil {
-		if ctx.Err() != nil {
-			err := status.FromContextError(ctx.Err()).Err()
-			logger.Errorf("acl check context error: %v", err)
-			return "", nil, err
-		}
-		switch {
-		case errors.Is(err, context.DeadlineExceeded):
-			logger.Errorf("acl check deadline exceeded: %v", err)
-			return "", nil, status.Errorf(codes.DeadlineExceeded, "find group failed: %v", err)
-		case errors.Is(err, context.Canceled):
-			logger.Errorf("acl check canceled: %v", err)
-			return "", nil, status.Errorf(codes.Canceled, "find group canceled: %v", err)
-		case errors.Is(err, errNoMatchingGroup):
-			logger.Errorf("no acl match: %v", err)
-			return "", nil, status.Errorf(codes.PermissionDenied, "access rejected")
-		}
-		logger.Errorf("acl check backend err: %v", err)
-		return "", nil, err
-	}
-
-	logger.Debugf("in group:%s", g.Id)
-	if g.Reject {
-		logger.Errorf("group:%s rejected", g.Id)
-		return g.Id, nil, grpc.Errorf(codes.PermissionDenied, "access rejected")
-	}
-	if g.ServiceAccount == "" {
-		logger.Debugf("group:%s use EUC", g.Id)
-		return g.Id, token, nil
-	}
-
-	sa := c.accounts[g.ServiceAccount]
-	if sa == nil {
-		logger.Errorf("group:%s service account not found: %s", g.Id, g.ServiceAccount)
-		return g.Id, nil, grpc.Errorf(codes.Internal, "service account not found: %s", g.ServiceAccount)
-	}
-	saToken, err := sa.Token(ctx)
-	if err != nil {
-		logger.Errorf("group:%s service account:%s error:%v", g.Id, g.ServiceAccount, err)
-		return g.Id, nil, grpc.Errorf(codes.Internal, "service account:%s error:%v", g.ServiceAccount, err)
-	}
-	logger.Debugf("group:%s use service account:%s", g.Id, g.ServiceAccount)
-	return g.Id, saToken, nil
-}
-
-func checkGroup(ctx context.Context, tokenInfo *auth.TokenInfo, g *pb.Group, authDB AuthDB) (bool, error) {
-	logger := log.FromContext(ctx)
-	logger.Debugf("checking group:%s", g.Id)
-	if g.Audience != "" {
-		if tokenInfo.Audience != g.Audience {
-			logger.Debugf("audience mismatch: %s != %s", tokenInfo.Audience, g.Audience)
-			return false, nil
-		}
-	}
-	if len(g.Emails) == 0 && len(g.Domains) == 0 && authDB != nil {
-		ok, err := authDB.IsMember(ctx, tokenInfo.Email, g.Id)
-		if err != nil {
-			logger.Warnf("authdb lookup error:%s: %v", g.Id, err)
-			return false, err
-		}
-		if !ok {
-			logger.Debugf("not member in authdb group:%s", g.Id)
-		}
-		return ok, nil
-	}
-	if !match(tokenInfo.Email, g.Emails, g.Domains) {
-		logger.Debugf("emails/domains mismatch: client email not in group %s", g.Id)
-		return false, nil
-	}
-	return true, nil
-}
-
-func match(email string, emails, domains []string) bool {
-	for _, e := range emails {
-		if email == e {
-			return true
-		}
-	}
-	for _, d := range domains {
-		if strings.HasSuffix(email, "@"+d) {
-			return true
-		}
-	}
-	return false
-}
diff --git a/auth/acl/checker_test.go b/auth/acl/checker_test.go
deleted file mode 100644
index d17d497..0000000
--- a/auth/acl/checker_test.go
+++ /dev/null
@@ -1,361 +0,0 @@
-// Copyright 2018 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.
-
-package acl
-
-import (
-	"context"
-	"testing"
-
-	"golang.org/x/oauth2"
-	"google.golang.org/grpc/codes"
-	"google.golang.org/grpc/status"
-
-	"go.chromium.org/goma/server/auth"
-	"go.chromium.org/goma/server/auth/account"
-	pb "go.chromium.org/goma/server/proto/auth"
-)
-
-type fakePool struct{}
-
-func (fakePool) New(name string) (account.Account, error) {
-	return fakeAccount{name}, nil
-}
-
-type fakeAccount struct {
-	name string
-}
-
-func (f fakeAccount) Equals(other account.Account) bool {
-	o, ok := other.(fakeAccount)
-	if !ok {
-		return false
-	}
-	return f.name != o.name
-}
-
-func (f fakeAccount) Token(ctx context.Context) (*oauth2.Token, error) {
-	return &oauth2.Token{AccessToken: "token"}, nil
-}
-
-func TestChecker(t *testing.T) {
-	config := &pb.ACL{
-		Groups: []*pb.Group{
-			{
-				Id:     "service-account",
-				Emails: []string{"foo@project.iam.gserviceaccount.com"},
-			},
-			{
-				Id:             "chrome-bot",
-				Emails:         []string{"goma-client@chrome-infra-auth.iam.gserviceaccount.com"},
-				ServiceAccount: "chrome-bot-service-account",
-			},
-			{
-				Id:             "googler",
-				Audience:       "687418631491-r6m1c3pr0lth5atp4ie07f03ae8omefc.apps.googleusercontent.com",
-				Domains:        []string{"google.com"},
-				ServiceAccount: "googler-service-account",
-			},
-			{
-				Id:             "contributor",
-				Audience:       "687418631491-r6m1c3pr0lth5atp4ie07f03ae8omefc.apps.googleusercontent.com",
-				Emails:         []string{"foo@gmail.com"},
-				ServiceAccount: "contributor-service-account",
-			},
-		},
-	}
-
-	checker := &Checker{
-		Pool: fakePool{},
-	}
-
-	ctx := context.Background()
-	err := checker.Set(ctx, config)
-	if err != nil {
-		t.Errorf("checker.Set(ctx, config)=%v; want nil-error", err)
-	}
-
-	type testData struct {
-		tokenInfo *auth.TokenInfo
-		errCode   codes.Code
-	}
-
-	testCases := map[string]*testData{
-		"service-account": {
-			tokenInfo: &auth.TokenInfo{
-				Email:    "foo@project.iam.gserviceaccount.com",
-				Audience: "123456-xxxxxx.apps.googleusercontent.com",
-			},
-		},
-		"chrome-bot": {
-			tokenInfo: &auth.TokenInfo{
-				Email:    "goma-client@chrome-infra-auth.iam.gserviceaccount.com",
-				Audience: "7890-xxxxxx.apps.googleusercontent.com",
-			},
-		},
-		"googler": {
-			tokenInfo: &auth.TokenInfo{
-				Email:    "someone@google.com",
-				Audience: "687418631491-r6m1c3pr0lth5atp4ie07f03ae8omefc.apps.googleusercontent.com",
-			},
-		},
-		"malicious-googler": {
-			tokenInfo: &auth.TokenInfo{
-				Email:    "malicious@google.com",
-				Audience: "687418631491-r6m1c3pr0lth5atp4ie07f03ae8omefc.apps.googleusercontent.com",
-			},
-		},
-		"contributor": {
-			tokenInfo: &auth.TokenInfo{
-				Email:    "foo@gmail.com",
-				Audience: "687418631491-r6m1c3pr0lth5atp4ie07f03ae8omefc.apps.googleusercontent.com",
-			},
-		},
-		"unknown service account": {
-			tokenInfo: &auth.TokenInfo{
-				Email:    "unknown-service-account@chrome-infra-auth.iam.gserviceaccount.com",
-				Audience: "7890-xxxxxx.apps.googleusercontent.com",
-			},
-			errCode: codes.PermissionDenied,
-		},
-		"unknown audience": {
-			tokenInfo: &auth.TokenInfo{
-				Email:    "someone@google.com",
-				Audience: "187410957766-f28toeml294bk5mm7nr2jn2rup1rvtjj.apps.googleusercontent.com",
-			},
-			errCode: codes.PermissionDenied,
-		},
-		"unknown user": {
-			tokenInfo: &auth.TokenInfo{
-				Email:    "unknown.user@gmail.com",
-				Audience: "687418631491-r6m1c3pr0lth5atp4ie07f03ae8omefc.apps.googleusercontent.com",
-			},
-			errCode: codes.PermissionDenied,
-		},
-		"contributor2": {
-			tokenInfo: &auth.TokenInfo{
-				Email:    "contributor@gmail.com",
-				Audience: "687418631491-r6m1c3pr0lth5atp4ie07f03ae8omefc.apps.googleusercontent.com",
-			},
-			errCode: codes.PermissionDenied,
-		},
-	}
-
-	testCheck := func() {
-		for desc, tc := range testCases {
-			t.Logf("check %s %s", desc, tc.tokenInfo.Email)
-			_, _, err := checker.CheckToken(ctx, &oauth2.Token{AccessToken: "token"}, tc.tokenInfo)
-			if st, _ := status.FromError(err); st.Code() != tc.errCode {
-				t.Errorf("checker.CheckToken(ctx, token, tokenInfo %s)=_, %v, want err code %d", tc.tokenInfo.Email, err, tc.errCode)
-			}
-		}
-	}
-
-	testCheck()
-
-	t.Logf("allow new contributor")
-	g := config.Groups[len(config.Groups)-1]
-	g.Emails = append(g.Emails, "contributor@gmail.com")
-
-	err = checker.Set(ctx, config)
-	if err != nil {
-		t.Errorf("checker.Set(ctx, config)=%v; want nil-error", err)
-	}
-	testCases["contributor2"].errCode = codes.OK
-
-	testCheck()
-
-	t.Logf("remove contributor")
-	config.Groups = config.Groups[:len(config.Groups)-1]
-
-	err = checker.Set(ctx, config)
-	if err != nil {
-		t.Errorf("checker.Set(ctx, config)=%v; want nil-error", err)
-	}
-	testCases["contributor"].errCode = codes.PermissionDenied
-	testCases["contributor2"].errCode = codes.PermissionDenied
-
-	testCheck()
-
-	t.Logf("reject bad googler")
-	config.Groups = append([]*pb.Group{
-		{
-			Id:       "bad-googler",
-			Audience: "687418631491-r6m1c3pr0lth5atp4ie07f03ae8omefc.apps.googleusercontent.com",
-			Emails:   []string{"malicious@google.com"},
-			Reject:   true,
-		},
-	}, config.Groups...)
-
-	err = checker.Set(ctx, config)
-	if err != nil {
-		t.Errorf("checker.Set(ctx, config)=%v; want nil-error", err)
-	}
-	testCases["malicious-googler"].errCode = codes.PermissionDenied
-
-	testCheck()
-}
-
-type fakeAuthDB struct {
-	db map[string]bool
-}
-
-func (f fakeAuthDB) IsMember(ctx context.Context, email, group string) (bool, error) {
-	return f.db[email+":"+group], nil
-}
-
-func TestCheckGroup(t *testing.T) {
-	ctx := context.Background()
-
-	authDB := fakeAuthDB{
-		db: map[string]bool{
-			"someone@google.com:googler": true,
-		},
-	}
-
-	for _, tc := range []struct {
-		desc      string
-		tokenInfo *auth.TokenInfo
-		g         *pb.Group
-		want      bool
-	}{
-		{
-			desc: "empty group",
-			tokenInfo: &auth.TokenInfo{
-				Email:    "someone@google.com",
-				Audience: "687418631491-r6m1c3pr0lth5atp4ie07f03ae8omefc.apps.googleusercontent.com",
-			},
-			g: &pb.Group{
-				Id: "empty",
-			},
-		},
-		{
-			desc: "email match",
-			tokenInfo: &auth.TokenInfo{
-				Email:    "someone@google.com",
-				Audience: "687418631491-r6m1c3pr0lth5atp4ie07f03ae8omefc.apps.googleusercontent.com",
-			},
-			g: &pb.Group{
-				Id: "someone-in-google",
-				Emails: []string{
-					"someone@google.com",
-				},
-			},
-			want: true,
-		},
-		{
-			desc: "domain match",
-			tokenInfo: &auth.TokenInfo{
-				Email:    "someone@google.com",
-				Audience: "687418631491-r6m1c3pr0lth5atp4ie07f03ae8omefc.apps.googleusercontent.com",
-			},
-			g: &pb.Group{
-				Id: "googler",
-				Domains: []string{
-					"google.com",
-				},
-			},
-			want: true,
-		},
-		{
-			desc: "authdb match",
-			tokenInfo: &auth.TokenInfo{
-				Email:    "someone@google.com",
-				Audience: "687418631491-r6m1c3pr0lth5atp4ie07f03ae8omefc.apps.googleusercontent.com",
-			},
-			g: &pb.Group{
-				Id: "googler",
-			},
-			want: true,
-		},
-		{
-			desc: "email match with audience check",
-			tokenInfo: &auth.TokenInfo{
-				Email:    "someone@google.com",
-				Audience: "687418631491-r6m1c3pr0lth5atp4ie07f03ae8omefc.apps.googleusercontent.com",
-			},
-			g: &pb.Group{
-				Id:       "someone-in-google",
-				Audience: "687418631491-r6m1c3pr0lth5atp4ie07f03ae8omefc.apps.googleusercontent.com",
-				Emails: []string{
-					"someone@google.com",
-				},
-			},
-			want: true,
-		},
-		{
-			desc: "domain match with audience check",
-			tokenInfo: &auth.TokenInfo{
-				Email:    "someone@google.com",
-				Audience: "687418631491-r6m1c3pr0lth5atp4ie07f03ae8omefc.apps.googleusercontent.com",
-			},
-			g: &pb.Group{
-				Id:       "googler",
-				Audience: "687418631491-r6m1c3pr0lth5atp4ie07f03ae8omefc.apps.googleusercontent.com",
-				Domains: []string{
-					"google.com",
-				},
-			},
-			want: true,
-		},
-		{
-			desc: "authdb match with audience check",
-			tokenInfo: &auth.TokenInfo{
-				Email:    "someone@google.com",
-				Audience: "687418631491-r6m1c3pr0lth5atp4ie07f03ae8omefc.apps.googleusercontent.com",
-			},
-			g: &pb.Group{
-				Id:       "googler",
-				Audience: "687418631491-r6m1c3pr0lth5atp4ie07f03ae8omefc.apps.googleusercontent.com",
-			},
-			want: true,
-		},
-		{
-			desc: "email match but audience mismatch",
-			tokenInfo: &auth.TokenInfo{
-				Email:    "someone@google.com",
-				Audience: "123456-xxxxxx.apps.googleusercontent.com",
-			},
-			g: &pb.Group{
-				Id:       "someone-in-google",
-				Audience: "687418631491-r6m1c3pr0lth5atp4ie07f03ae8omefc.apps.googleusercontent.com",
-				Emails: []string{
-					"someone@google.com",
-				},
-			},
-		},
-		{
-			desc: "domain match but audience mismatch",
-			tokenInfo: &auth.TokenInfo{
-				Email:    "someone@google.com",
-				Audience: "123456-xxxxxx.apps.googleusercontent.com",
-			},
-			g: &pb.Group{
-				Id:       "googler",
-				Audience: "687418631491-r6m1c3pr0lth5atp4ie07f03ae8omefc.apps.googleusercontent.com",
-				Domains: []string{
-					"google.com",
-				},
-			},
-		},
-		{
-			desc: "authdb match but audience mismatch",
-			tokenInfo: &auth.TokenInfo{
-				Email:    "someone@google.com",
-				Audience: "123456-xxxxxx.apps.googleusercontent.com",
-			},
-			g: &pb.Group{
-				Id:       "googler",
-				Audience: "687418631491-r6m1c3pr0lth5atp4ie07f03ae8omefc.apps.googleusercontent.com",
-			},
-		},
-	} {
-		t.Run(tc.desc, func(t *testing.T) {
-			got, err := checkGroup(ctx, tc.tokenInfo, tc.g, authDB)
-			if got != tc.want || err != nil {
-				t.Errorf("checkGroup(ctx, tokenInfo, group, authDB)=%t, %v; want=%t, nil", got, err, tc.want)
-			}
-		})
-	}
-}
diff --git a/auth/acl/loader.go b/auth/acl/loader.go
deleted file mode 100644
index d16b57a..0000000
--- a/auth/acl/loader.go
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2018 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.
-
-package acl
-
-import (
-	"context"
-	"fmt"
-	"io/ioutil"
-
-	"google.golang.org/protobuf/encoding/prototext"
-
-	pb "go.chromium.org/goma/server/proto/auth"
-)
-
-// Loader loads acl data.
-type Loader interface {
-	Load(ctx context.Context) (*pb.ACL, error)
-}
-
-// StaticLoader loads static acl data.
-type StaticLoader struct {
-	*pb.ACL
-}
-
-func (l StaticLoader) Load(ctx context.Context) (*pb.ACL, error) {
-	return l.ACL, nil
-}
-
-// FileLoader loads acl data from Filename.
-type FileLoader struct {
-	Filename string
-}
-
-// Loads loads acl stored as text proto in file.
-func (l FileLoader) Load(ctx context.Context) (*pb.ACL, error) {
-	b, err := ioutil.ReadFile(l.Filename)
-	if err != nil {
-		return nil, err
-	}
-	a := &pb.ACL{}
-	err = prototext.Unmarshal(b, a)
-	if err != nil {
-		return nil, fmt.Errorf("load error %s: %v", l.Filename, err)
-	}
-	return a, nil
-}
diff --git a/auth/authdb/client.go b/auth/authdb/client.go
deleted file mode 100644
index 983c4f1..0000000
--- a/auth/authdb/client.go
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2018 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.
-
-package authdb
-
-import (
-	"context"
-	"time"
-
-	"go.chromium.org/goma/server/httprpc"
-	"go.chromium.org/goma/server/log"
-	pb "go.chromium.org/goma/server/proto/auth"
-	"go.chromium.org/goma/server/rpc"
-)
-
-// Client is authdb client.
-type Client struct {
-	*httprpc.Client
-}
-
-// IsMember checks email is in group.
-func (c Client) IsMember(ctx context.Context, email, group string) (bool, error) {
-	logger := log.FromContext(ctx)
-
-	req := &pb.CheckMembershipReq{
-		Email: email,
-		Group: group,
-	}
-	resp := &pb.CheckMembershipResp{}
-	err := rpc.Retry{}.Do(ctx, func() error {
-		ctx, cancel := context.WithTimeout(ctx, 3*time.Second)
-		defer cancel()
-		return c.Client.Call(ctx, req, resp)
-	})
-	if err != nil {
-		logger.Errorf("check membership: %v", err)
-		return false, err
-	}
-	return resp.IsMember, nil
-}
diff --git a/auth/authdb/client_test.go b/auth/authdb/client_test.go
deleted file mode 100644
index 2db257b..0000000
--- a/auth/authdb/client_test.go
+++ /dev/null
@@ -1,127 +0,0 @@
-// Copyright 2018 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.
-
-package authdb
-
-import (
-	"context"
-	"errors"
-	"net/http/httptest"
-	"testing"
-
-	"google.golang.org/grpc/codes"
-	"google.golang.org/grpc/status"
-	"google.golang.org/protobuf/proto"
-
-	"go.chromium.org/goma/server/httprpc"
-	authdbrpc "go.chromium.org/goma/server/httprpc/authdb"
-	pb "go.chromium.org/goma/server/proto/auth"
-)
-
-type fakeAuthDBServer struct {
-	pb.UnimplementedAuthDBServiceServer
-	t        *testing.T
-	want     *pb.CheckMembershipReq
-	resp     *pb.CheckMembershipResp
-	respErrs []error
-}
-
-func (a *fakeAuthDBServer) CheckMembership(ctx context.Context, req *pb.CheckMembershipReq) (*pb.CheckMembershipResp, error) {
-	if len(a.respErrs) > 0 {
-		var err error
-		err, a.respErrs = a.respErrs[0], a.respErrs[1:]
-		return nil, err
-	}
-	if !proto.Equal(req, a.want) {
-		a.t.Errorf("CheckMembership: req=%#v; want=%#v", req, a.want)
-		return nil, errors.New("unexpected request")
-	}
-	return a.resp, nil
-}
-
-func TestClient(t *testing.T) {
-	ctx := context.Background()
-	fakeserver := &fakeAuthDBServer{}
-	s := httptest.NewServer(authdbrpc.Handler(fakeserver))
-	defer s.Close()
-
-	for _, tc := range []struct {
-		desc         string
-		email, group string
-		resp         bool
-		respErrs     []error
-		want         bool
-		wantErr      bool
-	}{
-		{
-			desc:  "ok",
-			email: "someone@google.com",
-			group: "goma-googlers",
-			resp:  true,
-			want:  true,
-		},
-		{
-			desc:  "not member",
-			email: "someone@example.com",
-			group: "goma-googlers",
-			resp:  false,
-			want:  false,
-		},
-		{
-			desc:     "temp failure",
-			email:    "someone@google.com",
-			group:    "goma-googlers",
-			resp:     true,
-			respErrs: []error{status.Errorf(codes.Unavailable, "unavailable")},
-			want:     true,
-		},
-		{
-			desc:     "temp failure false",
-			email:    "someone@google.com",
-			group:    "goma-googlers",
-			resp:     false,
-			respErrs: []error{status.Errorf(codes.Unavailable, "unavailable")},
-			want:     false,
-		},
-		{
-			desc:     "server error",
-			email:    "someone@google.com",
-			group:    "goma-googlers",
-			resp:     true,
-			respErrs: []error{status.Errorf(codes.InvalidArgument, "bad request")},
-			wantErr:  true,
-		},
-	} {
-		t.Run(tc.desc, func(t *testing.T) {
-			// can't start server in each test case,
-			// due to duplicate metrics collector registration.
-			fakeserver.t = t
-			fakeserver.want = &pb.CheckMembershipReq{
-				Email: tc.email,
-				Group: tc.group,
-			}
-			fakeserver.resp = &pb.CheckMembershipResp{
-				IsMember: tc.resp,
-			}
-			fakeserver.respErrs = tc.respErrs
-			c := Client{
-				Client: &httprpc.Client{
-					Client: s.Client(),
-					URL:    s.URL + "/authdb/checkMembership",
-				},
-			}
-			got, err := c.IsMember(ctx, tc.email, tc.group)
-			if tc.wantErr {
-				if err == nil {
-					t.Errorf("IsMember(ctx, %q, %q)=%v, nil; want=false, err", tc.email, tc.group, got)
-					return
-				}
-				return
-			}
-			if err != nil || got != tc.want {
-				t.Errorf("IsMember(ctx, %q, %q)=%v, %v; want=%v, false", tc.email, tc.group, got, err, tc.want)
-			}
-		})
-	}
-}
diff --git a/auth/authdb/doc.go b/auth/authdb/doc.go
deleted file mode 100644
index f079511..0000000
--- a/auth/authdb/doc.go
+++ /dev/null
@@ -1,6 +0,0 @@
-// Copyright 2018 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.
-
-// Package authdb provides access to authentication database.
-package authdb
diff --git a/auth/authdb/handler.go b/auth/authdb/handler.go
deleted file mode 100644
index 646d9f2..0000000
--- a/auth/authdb/handler.go
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2018 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.
-
-package authdb
-
-import (
-	"context"
-
-	pb "go.chromium.org/goma/server/proto/auth"
-)
-
-// AuthDB is authentication database.
-type AuthDB interface {
-	IsMember(ctx context.Context, email, group string) (bool, error)
-}
-
-// Handler handles request to AuthDB.
-type Handler struct {
-	pb.UnimplementedAuthDBServiceServer
-	AuthDB AuthDB
-}
-
-func (h Handler) CheckMembership(ctx context.Context, req *pb.CheckMembershipReq) (*pb.CheckMembershipResp, error) {
-	ok, err := h.AuthDB.IsMember(ctx, req.Email, req.Group)
-	return &pb.CheckMembershipResp{
-		IsMember: ok,
-	}, err
-}
diff --git a/auth/client.go b/auth/client.go
deleted file mode 100644
index 0a3b1fa..0000000
--- a/auth/client.go
+++ /dev/null
@@ -1,249 +0,0 @@
-// Copyright 2017 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.
-
-package auth
-
-import (
-	"context"
-	"errors"
-	"fmt"
-	"net/http"
-	"strings"
-	"sync"
-	"time"
-
-	"go.opencensus.io/stats"
-	"go.opencensus.io/stats/view"
-	"go.opencensus.io/tag"
-	"go.opencensus.io/trace"
-	"golang.org/x/oauth2"
-	"golang.org/x/sync/singleflight"
-
-	"go.chromium.org/goma/server/auth/enduser"
-	"go.chromium.org/goma/server/log"
-	authpb "go.chromium.org/goma/server/proto/auth"
-	"go.chromium.org/goma/server/rpc"
-)
-
-var (
-	authRequests = stats.Int64(
-		"go.chromium.org/goma/server/auth.auth",
-		"Number of auth requests",
-		stats.UnitDimensionless)
-
-	accountKey = tag.MustNewKey("account")
-	authErrKey = tag.MustNewKey("auth_error")
-
-	// DefaultViews are the default views provided by this package.
-	// You need to register the view for data to actually be collected.
-	DefaultViews = []*view.View{
-		{
-			Name:        "go.chromium.org/goma/server/auth.auth_by_account",
-			Description: "auth request count by account",
-			TagKeys: []tag.Key{
-				accountKey,
-				authErrKey,
-			},
-			Measure:     authRequests,
-			Aggregation: view.Count(),
-		},
-	}
-)
-
-// ErrNoAuthHeader represents authentication failure due to lack of Authorization header in an HTTP request.
-var ErrNoAuthHeader = errors.New("no Authorization header")
-
-// ErrInternal represents internal error.
-var ErrInternal = errors.New("internal error")
-
-// ErrExpired represents expiration of access token.
-var ErrExpired = errors.New("expired")
-
-// ErrOverQuota represents the user used up the quota.
-var ErrOverQuota = errors.New("over quota")
-
-type authInfo struct {
-	err error
-	mu  sync.Mutex // protect resp.Quota
-
-	// TODO: define type to avoid email leak in logging.
-	resp *authpb.AuthResp
-}
-
-func (ai *authInfo) expiresAt() time.Time {
-	if ai.err != nil || ai.resp == nil || ai.resp.ExpiresAt == nil {
-		return time.Now().Add(1 * time.Second)
-	}
-	return time.Unix(ai.resp.ExpiresAt.Seconds, int64(ai.resp.ExpiresAt.Nanos))
-}
-
-func (ai *authInfo) String() string {
-	return fmt.Sprintf("authInfo:%s %s %s err:%v", ai.resp.GetGroupId(), ai.expiresAt(), ai.resp.GetErrorDescription(), ai.err)
-}
-
-// Check returns error if authInfo does not have valid AuthResp.
-// This code also checks quota, and decrease remaining quota for accessing
-// the service.
-func (ai *authInfo) Check(ctx context.Context) error {
-	logger := log.FromContext(ctx)
-	// check order:
-	// 1. authInfo.err == nil?
-	// 2. ErrorDescription
-	// 3. Email exists.
-	// 4. token expiration.
-	// 5. Quota > 0 || Quota < 0 (unlimited)
-	if ai.err != nil {
-		logger.Warnf("auth.Check %v due to %v", ErrInternal, ai.err)
-		return ErrInternal
-	}
-	if ai.resp.ErrorDescription != "" {
-		logger.Warnf("permission denied: %s", ai.resp.ErrorDescription)
-		return errors.New(ai.resp.ErrorDescription)
-	}
-	// Valid AuthResp should not make Email empty but it is.
-	if ai.resp.Email == "" {
-		logger.Warnf("auth.Check %v", ErrInternal)
-		return ErrInternal
-	}
-
-	expiresAt := ai.expiresAt()
-	now := time.Now()
-	if expiresAt.Before(now) {
-		logger.Warnf("auth.Check %v because token was expired at %v", ErrExpired, expiresAt)
-		return ErrExpired
-	}
-
-	ai.mu.Lock()
-	defer ai.mu.Unlock()
-	if ai.resp.Quota < 0 { // unlimited.
-		return nil
-	}
-	if ai.resp.Quota == 0 {
-		return ErrOverQuota
-	}
-	ai.resp.Quota--
-	return nil
-}
-
-type Auth struct {
-	Client authpb.AuthServiceClient
-	Retry  rpc.Retry
-
-	sg    singleflight.Group
-	mu    sync.Mutex
-	cache map[string]*authInfo
-
-	runAt func(time.Time, func())
-}
-
-func (a *Auth) scheduledRun(t time.Time, f func()) {
-	scheduledRun := a.runAt
-	if scheduledRun == nil {
-		scheduledRun = runAt
-	}
-	scheduledRun(t, f)
-}
-
-// Check checks authorization header in an HTTP request.
-// The function returns error if authentication failed.
-// ErrNoAuthHeader is returned if no authorization header is in the request.
-func (a *Auth) Check(ctx context.Context, req *http.Request) (*enduser.EndUser, error) {
-	ctx, span := trace.StartSpan(ctx, "go.chromium.org/goma/server/auth.Auth.Check")
-	defer span.End()
-	logger := log.FromContext(ctx)
-
-	authorization := req.Header.Get("Authorization")
-	if authorization == "" {
-		logger.Warnf("no authorization header")
-		return nil, ErrNoAuthHeader
-	}
-	a.mu.Lock()
-	if a.cache == nil {
-		a.cache = make(map[string]*authInfo)
-	}
-	ai, ok := a.cache[authorization]
-	a.mu.Unlock()
-	if !ok {
-		v, err, _ := a.sg.Do(authorization, func() (interface{}, error) {
-			logger.Debugf("first call for %s...", authorization[:len(authorization)/3])
-			ai := &authInfo{}
-			err := a.Retry.Do(ctx, func() error {
-				var err error
-				ai.resp, err = a.Client.Auth(ctx, &authpb.AuthReq{
-					Authorization: authorization,
-				})
-				return err
-			})
-			if err != nil {
-				logger.Errorf("auth failed: %v", err)
-				ai.err = err
-			}
-			go a.scheduledRun(expiryTime(ai.expiresAt()), func() {
-				a.mu.Lock()
-				delete(a.cache, authorization)
-				a.mu.Unlock()
-			})
-			a.mu.Lock()
-			a.cache[authorization] = ai
-			a.mu.Unlock()
-			return ai, nil
-		})
-		if err != nil {
-			logger.Errorf("auth error: %v", err)
-			return nil, err
-		}
-		ai = v.(*authInfo)
-	}
-	err := ai.Check(ctx)
-	if err != nil {
-		logger.Warnf("auth check err: %v", err)
-		return nil, err
-	}
-	token := &oauth2.Token{
-		AccessToken: ai.resp.Token.GetAccessToken(),
-		TokenType:   ai.resp.Token.GetTokenType(),
-	}
-	return enduser.New(ai.resp.Email, ai.resp.GroupId, token), nil
-}
-
-// Auth authenticates the requests and returns new context with enduser info.
-func (a *Auth) Auth(ctx context.Context, req *http.Request) (context.Context, error) {
-	u, err := a.Check(ctx, req)
-	recordAuth(ctx, u, err)
-	if err != nil {
-		return ctx, err
-	}
-	return enduser.NewContext(ctx, u), nil
-}
-
-func recordAuth(ctx context.Context, u *enduser.EndUser, err error) {
-	logger := log.FromContext(ctx)
-	var tags []tag.Mutator
-	switch {
-	case errors.Is(err, ErrNoAuthHeader):
-		tags = append(tags, tag.Upsert(authErrKey, "no-auth-header"))
-	case errors.Is(err, ErrInternal):
-		tags = append(tags, tag.Upsert(authErrKey, "internal"))
-	case errors.Is(err, ErrExpired):
-		tags = append(tags, tag.Upsert(authErrKey, "expired"))
-	case errors.Is(err, ErrOverQuota):
-		tags = append(tags, tag.Upsert(authErrKey, "over-quota"))
-	case err == nil:
-		tags = append(tags, tag.Upsert(authErrKey, "ok"))
-	default:
-		tags = append(tags, tag.Upsert(authErrKey, "unknown"))
-	}
-	if u == nil || string(u.Email) == "" {
-		tags = append(tags, tag.Upsert(accountKey, "unauthenticated"))
-	} else if strings.HasSuffix(string(u.Email), ".iam.gserviceaccount.com") {
-		tags = append(tags, tag.Upsert(accountKey, string(u.Email)))
-	} else {
-		tags = append(tags, tag.Upsert(accountKey, "not-service-account"))
-	}
-
-
-	if err := stats.RecordWithTags(ctx, tags, authRequests.M(1)); err != nil {
-		logger.Errorf("failed to record metrics: %v", err)
-	}
-}
diff --git a/auth/client_test.go b/auth/client_test.go
deleted file mode 100644
index 366b7e2..0000000
--- a/auth/client_test.go
+++ /dev/null
@@ -1,438 +0,0 @@
-// Copyright 2017 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.
-
-package auth
-
-import (
-	"context"
-	"errors"
-	"fmt"
-	"net/http"
-	"net/url"
-	"reflect"
-	"strings"
-	"testing"
-	"time"
-
-	"golang.org/x/oauth2"
-	"google.golang.org/grpc"
-	"google.golang.org/grpc/codes"
-	"google.golang.org/protobuf/types/known/timestamppb"
-
-	"go.chromium.org/goma/server/auth/enduser"
-	authpb "go.chromium.org/goma/server/proto/auth"
-)
-
-func TestAuthInfoExpiresAt(t *testing.T) {
-	t.Log("expired in 1 second for authInfo.err")
-	ai := authInfo{
-		err: errors.New("dummy"),
-	}
-	exp := ai.expiresAt()
-	if time.Now().Add(2 * time.Second).Before(exp) {
-		t.Errorf("returned ai.expiresAt()=%v; want less than 2 seconds", exp)
-	}
-
-	t.Log("expired in 1 second for nil resp")
-	ai = authInfo{}
-	exp = ai.expiresAt()
-	if time.Now().Add(2 * time.Second).Before(exp) {
-		t.Errorf("returned ai.expiresAt()=%v; want less than 2 seconds", exp)
-	}
-
-	t.Log("expired in 1 second for nil ExpiresAt.")
-	ai = authInfo{
-		resp: &authpb.AuthResp{},
-	}
-	exp = ai.expiresAt()
-	if time.Now().Add(2 * time.Second).Before(exp) {
-		t.Errorf("returned ai.expiresAt()=%v; want less than 2 seconds", exp)
-	}
-
-	t.Log("should return the same with ExpiresAt.")
-	tm := time.Now().Add(time.Hour)
-	expires := timestamppb.New(tm)
-	ai = authInfo{
-		resp: &authpb.AuthResp{
-			ExpiresAt: expires,
-		},
-	}
-	exp = ai.expiresAt()
-	if !tm.Equal(exp) {
-		t.Errorf("returned ai.expiresAt()=%v; want %v", exp, tm)
-	}
-}
-
-func TestAuthInfoCheck(t *testing.T) {
-	hourAgo := time.Now().Add(-1 * time.Hour)
-	expiredHourAgo := timestamppb.New(hourAgo)
-	hour := time.Now().Add(time.Hour)
-	willExpireInHour := timestamppb.New(hour)
-
-	for _, tc := range []struct {
-		desc  string
-		resp  *authpb.AuthResp
-		err   error
-		retry int
-	}{
-		{
-			desc: "token has already been expired",
-			resp: &authpb.AuthResp{
-				ExpiresAt: expiredHourAgo,
-				Email:     "example@google.com",
-			},
-			err:   ErrExpired,
-			retry: 1,
-		},
-		{
-			desc: "resp does not have email",
-			resp: &authpb.AuthResp{
-				ExpiresAt: willExpireInHour,
-			},
-			err:   ErrInternal,
-			retry: 1,
-		},
-		{
-			desc: "quota = 0",
-			resp: &authpb.AuthResp{
-				ExpiresAt: willExpireInHour,
-				Email:     "example@google.com",
-				Quota:     0,
-			},
-			err:   ErrOverQuota,
-			retry: 1,
-		},
-		{
-			desc: "unlimited access allowed",
-			resp: &authpb.AuthResp{
-				ExpiresAt: willExpireInHour,
-				Email:     "example@google.com",
-				Quota:     -1,
-			},
-			err:   nil,
-			retry: 1,
-		},
-		{
-			desc: "access fail the user used up quota",
-			resp: &authpb.AuthResp{
-				ExpiresAt: willExpireInHour,
-				Email:     "example@google.com",
-				Quota:     1,
-			},
-			err:   ErrOverQuota,
-			retry: 2,
-		},
-		{
-			desc: "can access because the user still have enough quota",
-			resp: &authpb.AuthResp{
-				ExpiresAt: willExpireInHour,
-				Email:     "example@google.com",
-				Quota:     2,
-			},
-			err:   nil,
-			retry: 1,
-		},
-	} {
-		t.Run(tc.desc, func(t *testing.T) {
-			var err error
-			ai := authInfo{
-				resp: tc.resp,
-			}
-			for i := 0; i < tc.retry; i++ {
-				err = ai.Check(context.Background())
-			}
-			if err != tc.err {
-				t.Errorf("ai(%q).Check() retry=%d return error %v; want %v", tc.resp, tc.retry, err, tc.err)
-			}
-		})
-	}
-}
-
-func TestAuthExpire(t *testing.T) {
-	ctx := context.Background()
-	const authorization = "Bearer token-value"
-	req := &http.Request{
-		URL: &url.URL{
-			Path: "/path",
-		},
-		Header: map[string][]string{
-			"Authorization": {authorization},
-		},
-	}
-	ch := make(chan chan bool)
-	now := time.Now()
-	expiresAt := func() time.Time {
-		return now.Add(1 * time.Hour)
-	}
-	expires := expiresAt()
-	expiresProto := timestamppb.New(expires)
-	var deadline time.Time
-	var callCount int
-	a := &Auth{
-		Client: dummyClient{
-			auth: func(ctx context.Context, req *authpb.AuthReq) (*authpb.AuthResp, error) {
-				callCount++
-				if req.Authorization != authorization {
-					return nil, fmt.Errorf("req.Authorization=%q; want=%q", req.Authorization, authorization)
-				}
-				return &authpb.AuthResp{
-					Email:     "foo@example.com",
-					ExpiresAt: expiresProto,
-					Quota:     -1,
-					GroupId:   "foo",
-					Token: &authpb.Token{
-						AccessToken: "token-value",
-						TokenType:   "Bearer",
-					},
-				}, nil
-			},
-		},
-		runAt: func(t time.Time, f func()) {
-			deadline = t
-			rch := <-ch
-			f()
-			close(rch)
-		},
-	}
-
-	t.Logf("initial check")
-	user, err := a.Check(ctx, req)
-	if err != nil {
-		t.Fatalf("Check failed: %v", err)
-	}
-	want := enduser.New("foo@example.com", "foo", &oauth2.Token{
-		AccessToken: "token-value",
-		TokenType:   "Bearer",
-	})
-	if !reflect.DeepEqual(user, want) {
-		t.Errorf("a.Check(ctx, req)=%#v; want=%#v", user, want)
-	}
-
-	a.mu.Lock()
-	if _, ok := a.cache[authorization]; !ok {
-		t.Errorf("%q must exist in cache", authorization)
-	}
-	a.mu.Unlock()
-
-	if callCount != 1 {
-		t.Errorf("call count=%d; want=1", callCount)
-	}
-
-	t.Logf("30 minutes later")
-	now = now.Add(30 * time.Minute)
-	user, err = a.Check(ctx, req)
-	if err != nil {
-		t.Fatalf("Check failed: %v", err)
-	}
-	if !reflect.DeepEqual(user, want) {
-		t.Errorf("a.Check(ctx, req)=%#v; want=%#v", user, want)
-	}
-
-	if callCount != 1 {
-		t.Errorf("call count=%d; want=1", callCount)
-	}
-
-	t.Logf("1 hours later")
-	now = now.Add(30 * time.Minute)
-	rch := make(chan bool)
-	// fire runAt
-	ch <- rch
-	if !deadline.Equal(expiryTime(expires)) {
-		t.Errorf("deadline %s != %s (expiresAt %s)", deadline, expiryTime(expires), expires)
-	}
-	// wait runAt finish.
-	<-rch
-
-	a.mu.Lock()
-	if _, ok := a.cache[authorization]; ok {
-		t.Errorf("%q must be removed in cache", authorization)
-	}
-	a.mu.Unlock()
-
-	expires2 := expiresAt()
-	if otime, ntime := expires, expires2; otime.Equal(ntime) {
-		t.Fatalf("expiresAt: %s == %s", otime, ntime)
-	}
-	user, err = a.Check(ctx, req)
-	if err != nil {
-		t.Fatalf("Check failed: %v", err)
-	}
-	if !reflect.DeepEqual(user, want) {
-		t.Errorf("a.Check(ctx, req)=%#v; want=%#v", user, want)
-	}
-	a.mu.Lock()
-	if _, ok := a.cache[authorization]; !ok {
-		t.Errorf("%q must exist in cache", authorization)
-	}
-	a.mu.Unlock()
-
-	if callCount != 2 {
-		t.Errorf("call count=%d; want=2", callCount)
-	}
-}
-
-type dummyClient struct {
-	auth func(context.Context, *authpb.AuthReq) (*authpb.AuthResp, error)
-}
-
-func (d dummyClient) Auth(ctx context.Context, req *authpb.AuthReq, opts ...grpc.CallOption) (*authpb.AuthResp, error) {
-	return d.auth(ctx, req)
-}
-
-func TestAuthCheck(t *testing.T) {
-	// TODO: better to check the error code?
-	// Currently, the test does not check Check returns what error code,
-	// but to confirm it actually failed with the expected error, we might
-	// need to check the error code?  I am afraid it would be change
-	// checker test, though.
-
-	t.Log("0. no Authorization header.")
-	a := &Auth{}
-	emptyReq := &http.Request{
-		// this is needed to make trace work without nil access.
-		URL: &url.URL{
-			Path: "dummy",
-		},
-	}
-	_, err := a.Check(context.Background(), emptyReq)
-	if err != ErrNoAuthHeader {
-		t.Errorf("Check(%v) error %v; want %v", emptyReq, err, ErrNoAuthHeader)
-	}
-
-	t.Log("1. access succeed (using cache)")
-	hour := time.Now().Add(time.Hour)
-	willExpireInHour := timestamppb.New(hour)
-	email := "example@google.com"
-	a1 := &Auth{
-		cache: map[string]*authInfo{
-			"Bearer test": {
-				resp: &authpb.AuthResp{
-					Email:     email,
-					ExpiresAt: willExpireInHour,
-					Quota:     -1,
-					Token: &authpb.Token{
-						AccessToken: "test",
-						TokenType:   "Bearer",
-					},
-				},
-			},
-		},
-	}
-	testReq := &http.Request{
-		Header: map[string][]string{
-			"Authorization": {"Bearer test"},
-		},
-		// this is needed to make trace work without nil access.
-		URL: &url.URL{
-			Path: "dummy",
-		},
-	}
-	eu, err := a1.Check(context.Background(), testReq)
-	if err != nil {
-		t.Errorf("Check(%v) error %v; want nil", testReq, err)
-	}
-	expectedEu := enduser.New(email, "", &oauth2.Token{
-		AccessToken: "test",
-		TokenType:   "Bearer",
-	})
-	if !reflect.DeepEqual(eu, expectedEu) {
-		t.Errorf("Check(%v)=%v; want %v", testReq, eu, expectedEu)
-	}
-
-	t.Log("2. access succeed (using Auth client)")
-	a2 := &Auth{
-		Client: dummyClient{
-			auth: func(ctx context.Context, req *authpb.AuthReq) (*authpb.AuthResp, error) {
-				return &authpb.AuthResp{
-					Email:     email,
-					ExpiresAt: willExpireInHour,
-					Quota:     -1,
-					Token: &authpb.Token{
-						AccessToken: "test",
-						TokenType:   "Bearer",
-					},
-				}, nil
-			},
-		},
-	}
-	eu, err = a2.Check(context.Background(), testReq)
-	if err != nil {
-		t.Errorf("Check(%v) error %v; want nil", testReq, err)
-	}
-	if !reflect.DeepEqual(eu, expectedEu) {
-		t.Errorf("Check(%v)=%v; want %v", testReq, eu, expectedEu)
-	}
-
-	t.Log("3. access fail due to fail to fetch from Auth client.")
-	a3 := &Auth{
-		Client: dummyClient{
-			auth: func(ctx context.Context, req *authpb.AuthReq) (*authpb.AuthResp, error) {
-				return nil, grpc.Errorf(codes.Internal, "auth server error")
-			},
-		},
-	}
-	_, err = a3.Check(context.Background(), testReq)
-	if err == nil {
-		t.Errorf("Check(%v) nil error; want error", testReq)
-	}
-
-	t.Log("4. access fail due to Quota = 0.")
-	a4 := &Auth{
-		Client: dummyClient{
-			auth: func(ctx context.Context, req *authpb.AuthReq) (*authpb.AuthResp, error) {
-				return &authpb.AuthResp{
-					Email:     email,
-					ExpiresAt: willExpireInHour,
-					Quota:     0,
-					Token: &authpb.Token{
-						AccessToken: "test",
-						TokenType:   "Bearer",
-					},
-				}, nil
-			},
-		},
-	}
-	_, err = a4.Check(context.Background(), testReq)
-	if err == nil {
-		t.Errorf("Check(%v) nil error; want error", testReq)
-	}
-
-	t.Log("5. access fail due to expired token.")
-	hourAgo := time.Now().Add(-1 * time.Hour)
-	expiredHourAgo := timestamppb.New(hourAgo)
-	a5 := &Auth{
-		Client: dummyClient{
-			auth: func(ctx context.Context, req *authpb.AuthReq) (*authpb.AuthResp, error) {
-				return &authpb.AuthResp{
-					Email:     email,
-					ExpiresAt: expiredHourAgo,
-					Quota:     -1,
-					Token: &authpb.Token{
-						AccessToken: "test",
-						TokenType:   "Bearer",
-					},
-				}, nil
-			},
-		},
-	}
-	_, err = a5.Check(context.Background(), testReq)
-	if err == nil {
-		t.Errorf("Check(%v) nil error; want error", testReq)
-	}
-}
-
-func TestAuthInfoString(t *testing.T) {
-	ai := &authInfo{
-		resp: &authpb.AuthResp{
-			Email:   "someone@google.com",
-			GroupId: "somegroup",
-		},
-	}
-	if got := fmt.Sprint(ai); strings.Contains(got, "someone@google.com") {
-		t.Errorf("fmt.Sprint(ai)=%s; leak email address", got)
-	}
-	if got := fmt.Sprintf("%[1]s %[1]v %+[1]v %#[1]v %[1]q", ai); strings.Contains(got, "someone@google.com") {
-		t.Errorf(`fmt.Sprintf("...", ai)=%s; leak email address`, got)
-	}
-}
diff --git a/auth/doc.go b/auth/doc.go
deleted file mode 100644
index 52bae15..0000000
--- a/auth/doc.go
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright 2017 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.
-
-/*
-Package auth provides authentication service.
-*/
-package auth
diff --git a/auth/enduser/enduser.go b/auth/enduser/enduser.go
deleted file mode 100644
index a434466..0000000
--- a/auth/enduser/enduser.go
+++ /dev/null
@@ -1,115 +0,0 @@
-// Copyright 2017 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.
-
-/*
-Package enduser manages end user information with context.
-*/
-package enduser
-
-import (
-	"context"
-	"fmt"
-	"reflect"
-
-	"golang.org/x/oauth2"
-	"google.golang.org/grpc/metadata"
-)
-
-// EmailString holds email string.  It will not output empty string in format.
-// Don't use this type in unexported field.  fmt won't invoke formatting method
-// on unexported fields. see https://golang.org/pkg/fmt/.
-type EmailString string
-
-func (e EmailString) Formatter(fmt.State, rune) {}
-func (e EmailString) GoString() string          { return "" }
-func (e EmailString) String() string            { return "" }
-
-// EndUser represents end user of httprpc calls.
-type EndUser struct {
-	Email EmailString
-	Group string
-	token *oauth2.Token
-}
-
-type key int
-
-var userKey key = 0
-
-const (
-	// metadata key: allowed 0-9 a-z -_.
-	// upper case converted to lower case on wire.
-	// we should use lowercase only here to use it as md[key].
-	// https://godoc.org/google.golang.org/grpc/metadata#Pairs
-	//
-	// endpoint proxy will not pass some metadata such as
-	// "goma.auth.enduser.email".
-	// confirmed "x-[-a-z0-9]+" will go through endpoint proxy.
-	emailKey       = "x-goma-enduser-email"
-	groupKey       = "x-goma-enduser-group"
-	accessTokenKey = "x-goma-enduser-accesstoken"
-	tokenTypeKey   = "x-goma-enduser-tokentype"
-)
-
-// New creates new EndUser from email, group and oauth2 access token.
-func New(email, group string, token *oauth2.Token) *EndUser {
-	user := &EndUser{
-		Email: EmailString(email),
-		Group: group,
-		token: token,
-	}
-	return user
-}
-
-// NewContext returns a new Context that carries value u in metadata.
-func NewContext(ctx context.Context, u *EndUser) context.Context {
-	return context.WithValue(metadata.AppendToOutgoingContext(ctx,
-		emailKey, string(u.Email),
-		groupKey, u.Group,
-		accessTokenKey, u.Token().AccessToken,
-		tokenTypeKey, u.Token().TokenType),
-		userKey, u)
-}
-
-// FromContext returns the EndUser value stored in ctx, if any.
-func FromContext(ctx context.Context) (*EndUser, bool) {
-	u, ok := ctx.Value(userKey).(*EndUser)
-	if ok {
-		return u, ok
-	}
-	u = &EndUser{
-		token: &oauth2.Token{},
-	}
-	md, ok := metadata.FromIncomingContext(ctx)
-	if !ok {
-		return u, ok
-	}
-	v := md[emailKey]
-	if len(v) > 0 {
-		u.Email = EmailString(v[0])
-	}
-	v = md[groupKey]
-	if len(v) > 0 {
-		u.Group = v[0]
-	}
-	v = md[accessTokenKey]
-	if len(v) > 0 {
-		u.token.AccessToken = v[0]
-	}
-	v = md[tokenTypeKey]
-	if len(v) > 0 {
-		u.token.TokenType = v[0]
-	}
-	ok = !reflect.DeepEqual(u, &EndUser{
-		token: &oauth2.Token{},
-	})
-	return u, ok
-}
-
-// Token returns end user's access token.
-func (u *EndUser) Token() *oauth2.Token {
-	if u == nil || u.token == nil {
-		return &oauth2.Token{}
-	}
-	return u.token
-}
diff --git a/auth/enduser/enduser_test.go b/auth/enduser/enduser_test.go
deleted file mode 100644
index bdac28e..0000000
--- a/auth/enduser/enduser_test.go
+++ /dev/null
@@ -1,124 +0,0 @@
-// Copyright 2017 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.
-
-package enduser
-
-import (
-	"context"
-	"fmt"
-	"reflect"
-	"strings"
-	"testing"
-
-	"golang.org/x/oauth2"
-	"google.golang.org/grpc/metadata"
-)
-
-func TestEmailString(t *testing.T) {
-	v := EmailString("someone@google.com")
-	if got := fmt.Sprint(v); strings.Contains(got, "someone@google.com") {
-		t.Errorf("fmt.Sprint(v)=%s; leak email address", got)
-	}
-	if got := fmt.Sprintf("%[1]s %[1]v %+[1]v %#[1]v %[1]q", v); strings.Contains(got, "someone@google.com") {
-		t.Errorf(`fmt.Sprintf("...", v)=%s; leak email address`, got)
-	}
-
-	sv := struct {
-		Email EmailString
-	}{
-		Email: v,
-	}
-	if got := fmt.Sprint(sv); strings.Contains(got, "someone@google.com") {
-		t.Errorf("fmt.Sprint(sv)=%s; leak email address", got)
-	}
-	if got := fmt.Sprintf("%[1]s %[1]v %+[1]v %#[1]v %[1]q", sv); strings.Contains(got, "someone@google.com") {
-		t.Errorf(`fmt.Sprintf("...", sv)=%s; leak email address`, got)
-	}
-
-}
-
-func TestNewContext(t *testing.T) {
-	ctx := context.Background()
-	u := New("someone@google.com", "googler", &oauth2.Token{
-		AccessToken: "token-value",
-		TokenType:   "Bearer",
-	})
-
-	ctx = metadata.NewOutgoingContext(ctx, metadata.Pairs("x-api-key", "xxx"))
-	ctx = NewContext(ctx, u)
-
-	md, ok := metadata.FromOutgoingContext(ctx)
-	if !ok {
-		t.Fatal("NewContext failed to set md")
-	}
-	wantMD := metadata.Pairs(
-		"x-api-key", "xxx",
-		emailKey, "someone@google.com",
-		groupKey, "googler",
-		accessTokenKey, "token-value",
-		tokenTypeKey, "Bearer")
-	if !reflect.DeepEqual(md, wantMD) {
-		t.Errorf("NewContext set metadata %#v; want %#v", md, wantMD)
-	}
-}
-
-func TestFromContextEmpty(t *testing.T) {
-	ctx := context.Background()
-
-	t.Logf("empty context")
-	_, ok := FromContext(ctx)
-	if ok {
-		t.Errorf("FromContext(ctx)=_, %t; want=_, false", ok)
-	}
-}
-
-func TestFromContext(t *testing.T) {
-	ctx := context.Background()
-	u := New("someone@google.com", "googler", &oauth2.Token{
-		AccessToken: "token-value",
-		TokenType:   "Bearer",
-	})
-	ctx = NewContext(ctx, u)
-
-	got, ok := FromContext(ctx)
-	if !ok {
-		t.Errorf("FromContext(ctx)=_, %t; want=_, true", ok)
-	}
-	if !reflect.DeepEqual(got, u) {
-		t.Errorf("FromContext(ctx)=%#v; want=%#v", got, u)
-	}
-}
-
-func TestFromContextFromMetadata(t *testing.T) {
-	ctx := context.Background()
-	md := metadata.Pairs(
-		emailKey, "someone@google.com",
-		groupKey, "googler",
-		accessTokenKey, "token-value",
-		tokenTypeKey, "Bearer")
-	ctx = metadata.NewIncomingContext(ctx, md)
-
-	got, ok := FromContext(ctx)
-	if !ok {
-		t.Errorf("FromContext(ctx)=_, %t; want=_, true", ok)
-	}
-	u := New("someone@google.com", "googler", &oauth2.Token{
-		AccessToken: "token-value",
-		TokenType:   "Bearer",
-	})
-	if !reflect.DeepEqual(got, u) {
-		t.Errorf("FromContext(ctx)=%#v; want=%#v", got, u)
-	}
-}
-
-func TestFromContextFromOtherMetadata(t *testing.T) {
-	ctx := context.Background()
-	md := metadata.Pairs("x-api-key", "xxx")
-	ctx = metadata.NewIncomingContext(ctx, md)
-
-	got, ok := FromContext(ctx)
-	if ok {
-		t.Errorf("FromContext(ctx)=%#v, %t; want=_, false", got, ok)
-	}
-}
diff --git a/auth/local.go b/auth/local.go
deleted file mode 100644
index 6e91e08..0000000
--- a/auth/local.go
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2017 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.
-
-package auth
-
-import (
-	"context"
-
-	"google.golang.org/grpc"
-
-	pb "go.chromium.org/goma/server/proto/auth"
-)
-
-type LocalClient struct {
-	*Service
-}
-
-func (c LocalClient) Auth(ctx context.Context, in *pb.AuthReq, opts ...grpc.CallOption) (*pb.AuthResp, error) {
-	resp, err := c.Service.Auth(ctx, in)
-	return resp, err
-}
diff --git a/auth/service.go b/auth/service.go
deleted file mode 100644
index 7f4c000..0000000
--- a/auth/service.go
+++ /dev/null
@@ -1,201 +0,0 @@
-// Copyright 2017 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.
-
-package auth
-
-import (
-	"context"
-	"sync"
-	"time"
-
-	"go.opencensus.io/trace"
-	"golang.org/x/oauth2"
-	"golang.org/x/sync/singleflight"
-	"google.golang.org/grpc"
-	"google.golang.org/grpc/codes"
-	"google.golang.org/grpc/status"
-	"google.golang.org/protobuf/types/known/timestamppb"
-
-	"go.chromium.org/goma/server/log"
-	authpb "go.chromium.org/goma/server/proto/auth"
-)
-
-type tokenCacheEntry struct {
-	*TokenInfo
-
-	Group string
-	Token *oauth2.Token
-}
-
-func (te *tokenCacheEntry) TokenProto() *authpb.Token {
-	if te.Token == nil {
-		return &authpb.Token{}
-	}
-	return &authpb.Token{
-		AccessToken: te.Token.AccessToken,
-		TokenType:   te.Token.TokenType,
-	}
-}
-
-// Service implements goma auth service.
-type Service struct {
-	authpb.UnimplementedAuthServiceServer
-	// CheckToken optionally checks access token with token info.
-	// If it is not set, all access will be rejected.
-	// If it returns grpc's codes.PermissionDenied error,
-	// error message will be used as ErrorDescription for user.
-	CheckToken func(context.Context, *oauth2.Token, *TokenInfo) (string, *oauth2.Token, error)
-
-	sg         singleflight.Group
-	mu         sync.Mutex
-	tokenCache map[string]*tokenCacheEntry
-	fetchInfo  func(context.Context, *oauth2.Token) (*TokenInfo, error)
-	runAt      func(time.Time, func())
-}
-
-func (s *Service) fetch(ctx context.Context, token *oauth2.Token) (*TokenInfo, error) {
-	ctx, span := trace.StartSpan(ctx, "go.chromium.org/goma/server/auth.fetch")
-	defer span.End()
-	fetchInfo := s.fetchInfo
-	if fetchInfo == nil {
-		fetchInfo = fetch
-	}
-	return fetchInfo(ctx, token)
-}
-
-func (s *Service) checkToken(ctx context.Context, token *oauth2.Token, tokenInfo *TokenInfo) (string, *oauth2.Token, error) {
-	if s.CheckToken == nil {
-		return "", nil, grpc.Errorf(codes.Internal, "CheckToken is not configured")
-	}
-	return s.CheckToken(ctx, token, tokenInfo)
-}
-
-func (s *Service) scheduledRun(t time.Time, f func()) {
-	scheduledRun := s.runAt
-	if scheduledRun == nil {
-		scheduledRun = runAt
-	}
-	scheduledRun(t, f)
-}
-
-// Auth checks authorization header of incoming request, and
-// replies end user information.
-//
-// TODO: find answers to following questions.
-//  1. can auth server return expired token? (currently yes)
-//  2. should auth server refresh expired token? (currently no)
-//  3. should grpc status code represent status of request or access token?
-//  4. how error description should be handled?
-//     currently, it is stored in cache but not used by anybody.
-//  5. should auth server create go routine for each token to expire the entry?
-//     (currently yes)
-//  6. how do we implement quota?
-//  7. how do we integrate auth server with chrome-infra-auth?
-func (s *Service) Auth(ctx context.Context, req *authpb.AuthReq) (*authpb.AuthResp, error) {
-	logger := log.FromContext(ctx)
-	token, err := parseToken(req.Authorization)
-	if err != nil {
-		logger.Errorf("parse token failure %s: %v", req.Authorization, err)
-		return nil, grpc.Errorf(codes.InvalidArgument, "wrong authorization: %v", err)
-	}
-
-	// TODO: factor out singleflight timed cache.
-	s.mu.Lock()
-	if s.tokenCache == nil {
-		s.tokenCache = make(map[string]*tokenCacheEntry)
-	}
-	k := tokenKey(token)
-	te, ok := s.tokenCache[k]
-	s.mu.Unlock()
-	if !ok {
-		v, err, _ := s.sg.Do(k, func() (interface{}, error) {
-			te := &tokenCacheEntry{}
-			var err error
-			te.TokenInfo, err = s.fetch(ctx, token)
-			if err != nil {
-				te.TokenInfo = &TokenInfo{
-					Err: err,
-					// set 1 second negative cache.
-					ExpiresAt: time.Now().Add(1 * time.Second),
-				}
-				err = nil
-			}
-			if te.TokenInfo.Err == nil {
-				te.Group, te.Token, err = s.checkToken(ctx, token, te.TokenInfo)
-				if err != nil {
-					te.TokenInfo.Err = err
-					// set 30 seconds negative cache (rejected user / wrong config).
-					// more than exiryDelta (10 secs)
-					// less than client ping timeout.
-					te.TokenInfo.ExpiresAt = time.Now().Add(30 * time.Second)
-				}
-				if te.Token != nil && !te.Token.Expiry.IsZero() && te.Token.Expiry.Before(te.TokenInfo.ExpiresAt) {
-					te.TokenInfo.ExpiresAt = te.Token.Expiry
-				}
-			}
-			switch status.Code(te.TokenInfo.Err) {
-			case codes.OK, codes.PermissionDenied, codes.Internal:
-				go s.scheduledRun(expiryTime(te.TokenInfo.ExpiresAt), func() {
-					s.mu.Lock()
-					delete(s.tokenCache, k)
-					s.mu.Unlock()
-				})
-				s.mu.Lock()
-				s.tokenCache[k] = te
-				s.mu.Unlock()
-			default:
-				// don't cache other error data.
-				return te, err
-			}
-			return te, nil
-		})
-		if err != nil {
-			logger.Errorf("auth error: %v", err)
-			switch c := status.Code(err); c {
-			case codes.DeadlineExceeded, codes.Unavailable, codes.Canceled:
-				return nil, grpc.Errorf(c, "auth error: %v", err)
-			}
-			return nil, grpc.Errorf(codes.Internal, "auth error: %v", err)
-		}
-		te = v.(*tokenCacheEntry)
-	}
-	if te.TokenInfo == nil {
-		return nil, grpc.Errorf(codes.Internal, "nil TokenInfo is given for %q", te.Group)
-	}
-
-	expires := timestamppb.New(te.TokenInfo.ExpiresAt)
-	var errorDescription string
-	var quota int32
-	if te.TokenInfo.Err == nil {
-		quota = -1 // TODO: -1 is unlimited.
-	} else if st, ok := status.FromError(te.TokenInfo.Err); ok {
-		switch st.Code() {
-		case codes.OK:
-			quota = -1 // TODO: -1 is unlimited.
-			logger.Infof("token info %q error non-nil, but ok?: %v", te.Group, st.Message())
-		case codes.PermissionDenied:
-			errorDescription = st.Message()
-			logger.Errorf("token info %q permission denied: %v", te.Group, te.TokenInfo.Err)
-		default:
-			// no need to record error state
-			logger.Errorf("token info %q error: %v", te.Group, te.TokenInfo.Err)
-			return nil, grpc.Errorf(st.Code(), "token info %q error: %v", te.Group, te.TokenInfo.Err)
-		}
-	} else {
-		// non grpc error.
-		logger.Errorf("token info %q error: %v", te.Group, te.TokenInfo.Err)
-		return nil, grpc.Errorf(codes.Internal, "token info %q error: %v", te.Group, te.TokenInfo.Err)
-	}
-
-	resp := &authpb.AuthResp{
-		Email:            te.TokenInfo.Email,
-		ExpiresAt:        expires,
-		Quota:            quota,
-		ErrorDescription: errorDescription,
-		GroupId:          te.Group,
-		Token:            te.TokenProto(),
-	}
-
-	return resp, nil
-}
diff --git a/auth/service_test.go b/auth/service_test.go
deleted file mode 100644
index 4e56a8a..0000000
--- a/auth/service_test.go
+++ /dev/null
@@ -1,282 +0,0 @@
-// Copyright 2017 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.
-
-package auth
-
-import (
-	"context"
-	"errors"
-	"fmt"
-	"reflect"
-	"strings"
-	"testing"
-	"time"
-
-	"golang.org/x/oauth2"
-	"google.golang.org/grpc/codes"
-	"google.golang.org/grpc/status"
-	"google.golang.org/protobuf/types/known/timestamppb"
-
-	authpb "go.chromium.org/goma/server/proto/auth"
-)
-
-func TestServiceExpire(t *testing.T) {
-	ctx := context.Background()
-	token := &oauth2.Token{
-		AccessToken: "token-value",
-		TokenType:   "Bearer",
-	}
-	ch := make(chan chan bool)
-	now := time.Now()
-	expiresAt := func() time.Time {
-		return now.Add(1 * time.Hour)
-	}
-	var deadline time.Time
-	var fetchCount int
-	s := &Service{
-		CheckToken: func(ctx context.Context, token *oauth2.Token, tokenInfo *TokenInfo) (string, *oauth2.Token, error) {
-			return "", token, nil
-		},
-		fetchInfo: func(ctx context.Context, t *oauth2.Token) (*TokenInfo, error) {
-			fetchCount++
-			if !reflect.DeepEqual(t, token) {
-				return nil, errors.New("wrong token")
-			}
-			return &TokenInfo{
-				Email:     "foo@example.com",
-				ExpiresAt: expiresAt(),
-			}, nil
-		},
-		runAt: func(t time.Time, f func()) {
-			deadline = t
-			rch := <-ch
-			f()
-			close(rch)
-		},
-	}
-
-	t.Logf("initial fetch")
-	resp, err := s.Auth(ctx, &authpb.AuthReq{
-		Authorization: "Bearer token-value",
-	})
-	if err != nil {
-		t.Fatalf("Auth failed: %v", err)
-	}
-	expires := expiresAt()
-	expiresProto := timestamppb.New(expires)
-	want := &authpb.AuthResp{
-		Email:     "foo@example.com",
-		ExpiresAt: expiresProto,
-		Quota:     -1,
-		Token: &authpb.Token{
-			AccessToken: "token-value",
-			TokenType:   "Bearer",
-		},
-	}
-	if !reflect.DeepEqual(resp, want) {
-		t.Errorf("s.Auth(ctx, req)=%v; want=%v", resp, want)
-	}
-	key := tokenKey(token)
-	s.mu.Lock()
-	if _, ok := s.tokenCache[key]; !ok {
-		t.Errorf("%q must exist in tokenCache", key)
-	}
-	s.mu.Unlock()
-
-	if fetchCount != 1 {
-		t.Errorf("fetch count=%d; want=1", fetchCount)
-	}
-
-	t.Logf("30 minutes later")
-	now = now.Add(30 * time.Minute)
-	resp, err = s.Auth(ctx, &authpb.AuthReq{
-		Authorization: "Bearer token-value",
-	})
-	if err != nil {
-		t.Fatalf("Auth failed: %v", err)
-	}
-	if !reflect.DeepEqual(resp, want) {
-		t.Errorf("s.Auth(ctx, req)=%v; want=%v", resp, want)
-	}
-	if fetchCount != 1 {
-		t.Errorf("fetch count=%d; want=1", fetchCount)
-	}
-
-	t.Logf("1 hours later")
-	now = now.Add(30 * time.Minute)
-	rch := make(chan bool)
-	// fire runAt
-	ch <- rch
-	if !deadline.Equal(expiryTime(expires)) {
-		t.Errorf("deadline %s != %s (expiresAt %s)", deadline, expiryTime(expires), expires)
-	}
-	// wait runAt finish.
-	<-rch
-
-	s.mu.Lock()
-	if _, ok := s.tokenCache[key]; ok {
-		t.Errorf("%q must be removed from tokenCache", key)
-	}
-	s.mu.Unlock()
-
-	expires2 := expiresAt()
-	if otime, ntime := expires, expires2; otime.Equal(ntime) {
-		t.Fatalf("expiresAt: %s == %s", otime, ntime)
-	}
-	want.ExpiresAt = timestamppb.New(expires2)
-	resp, err = s.Auth(ctx, &authpb.AuthReq{
-		Authorization: "Bearer token-value",
-	})
-	if err != nil {
-		t.Fatalf("Auth failed: %v", err)
-	}
-	if !reflect.DeepEqual(resp, want) {
-		t.Errorf("s.Auth(ctx, req)=%v; want=%v", resp, want)
-	}
-	s.mu.Lock()
-	if _, ok := s.tokenCache[key]; !ok {
-		t.Errorf("%q must exist in tokenCache", key)
-	}
-	s.mu.Unlock()
-
-	if fetchCount != 2 {
-		t.Errorf("fetch count=%d; want=2", fetchCount)
-	}
-}
-
-// TODO: revise this when we implement ACL
-func TestAuth(t *testing.T) {
-	t.Logf("0. access succeeds by cache")
-	email := "example@google.com"
-	expiresAt := time.Now().Add(10 * time.Second)
-	ptypeExpiresAt := timestamppb.New(expiresAt)
-	ti := &TokenInfo{
-		Email:     email,
-		Audience:  "test-audience.apps.googleusercontent.com",
-		ExpiresAt: expiresAt,
-	}
-	checkToken := func(ctx context.Context, token *oauth2.Token, tokenInfo *TokenInfo) (string, *oauth2.Token, error) {
-		if !reflect.DeepEqual(tokenInfo, ti) {
-			return "", nil, fmt.Errorf("wrong token info: got=%v; want=%v", tokenInfo, ti)
-		}
-		return "", token, nil
-	}
-
-	want := &authpb.AuthResp{
-		Email:     email,
-		ExpiresAt: ptypeExpiresAt,
-		Quota:     -1,
-		Token: &authpb.Token{
-			AccessToken: "test",
-			TokenType:   "Bearer",
-		},
-	}
-	token := &oauth2.Token{
-		AccessToken: "test",
-		TokenType:   "Bearer",
-	}
-	key := tokenKey(token)
-	s := &Service{
-		CheckToken: checkToken,
-		tokenCache: map[string]*tokenCacheEntry{
-			key: {
-				TokenInfo: ti,
-				Token:     token,
-			},
-		},
-	}
-
-	req := &authpb.AuthReq{
-		Authorization: "Bearer test",
-	}
-	resp, err := s.Auth(context.Background(), req)
-	if err != nil {
-		t.Errorf("Auth(%q) error %v; want nil error", req, err)
-	}
-	if !reflect.DeepEqual(resp, want) {
-		t.Errorf("Auth(%q)=%q; want %q", req, resp, want)
-	}
-
-	t.Logf("1. access succeeds by fetching token info.")
-	s1 := &Service{
-		CheckToken: checkToken,
-		fetchInfo: func(ctx context.Context, token *oauth2.Token) (*TokenInfo, error) {
-			return ti, nil
-		},
-	}
-	resp, err = s1.Auth(context.Background(), req)
-	if err != nil {
-		t.Errorf("Auth(%q) error %v; want nil error", req, err)
-	}
-	if !reflect.DeepEqual(resp, want) {
-		t.Errorf("Auth(%q)=%q; want %q", req, resp, want)
-	}
-	// and confirm it is stored in cache.
-	if entry, ok := s.tokenCache[key]; !ok || !reflect.DeepEqual(entry.TokenInfo, ti) {
-		t.Errorf(`tokenCache[%q].TokenInfo=%q; want %q`, key, entry.TokenInfo, ti)
-	}
-
-	t.Logf("2. failed to fetch token info.")
-	s2 := &Service{
-		CheckToken: checkToken,
-		fetchInfo: func(ctx context.Context, token *oauth2.Token) (*TokenInfo, error) {
-			return nil, errors.New("fetch failed")
-		},
-	}
-	resp, err = s2.Auth(context.Background(), req)
-	if status.Code(err) != codes.Internal {
-		t.Errorf("Auth(%q) error %v; want internal error", req, err)
-	}
-
-	t.Logf("3. non-allowed user access")
-	s3 := &Service{
-		CheckToken: func(ctx context.Context, token *oauth2.Token, tokenInfo *TokenInfo) (string, *oauth2.Token, error) {
-			if !strings.HasSuffix(tokenInfo.Email, "@google.com") {
-				return "", token, status.Errorf(codes.PermissionDenied, "not allowed")
-			}
-			return "", token, nil
-
-		},
-		fetchInfo: func(ctx context.Context, token *oauth2.Token) (*TokenInfo, error) {
-			return &TokenInfo{
-				Email:     "not_allowed@example.com",
-				ExpiresAt: expiresAt,
-			}, nil
-		},
-	}
-	resp, err = s3.Auth(context.Background(), req)
-	if err != nil {
-		t.Errorf("Auth(%q) error %v; want nil error", req, err)
-	}
-	if resp == nil {
-		t.Errorf("Auth(%q)=nil, want non-nil resp", req)
-	} else {
-		if resp.Quota != 0 {
-			t.Errorf("Auth(%q).Quota=%d; want 0", req, resp.Quota)
-		}
-		if resp.ErrorDescription == "" {
-			t.Errorf("Auth(%q).ErrorDescription=%q; want non empty", req, resp.ErrorDescription)
-		}
-	}
-	t.Logf("4. failed membership check")
-	s4 := &Service{
-		CheckToken: func(ctx context.Context, token *oauth2.Token, tokenInfo *TokenInfo) (string, *oauth2.Token, error) {
-			if tokenInfo.Email == "misfortune@example.com" {
-				return "", token, status.Errorf(codes.DeadlineExceeded, "deadline exceeded")
-			}
-			return "", token, nil
-
-		},
-		fetchInfo: func(ctx context.Context, token *oauth2.Token) (*TokenInfo, error) {
-			return &TokenInfo{
-				Email:     "misfortune@example.com",
-				ExpiresAt: expiresAt,
-			}, nil
-		},
-	}
-	resp, err = s4.Auth(context.Background(), req)
-	if err == nil {
-		t.Errorf("Auth(%q)=%v, %v; want error", req, resp, err)
-	}
-}
diff --git a/auth/token.go b/auth/token.go
deleted file mode 100644
index ff30263..0000000
--- a/auth/token.go
+++ /dev/null
@@ -1,148 +0,0 @@
-// Copyright 2018 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.
-
-package auth
-
-import (
-	"bytes"
-	"context"
-	"encoding/json"
-	"errors"
-	"fmt"
-	"io/ioutil"
-	"net/http"
-	"net/url"
-	"strconv"
-	"strings"
-	"time"
-
-	"golang.org/x/oauth2"
-	"google.golang.org/grpc/codes"
-	"google.golang.org/grpc/status"
-
-	"go.chromium.org/goma/server/rpc"
-)
-
-// expiryDelta is how earlier a token is considered expired than
-// its actual expiration time.  same as
-// https://github.com/golang/oauth2/blob/master/token.go
-const expiryDelta = 10 * time.Second
-
-func expiryTime(t time.Time) time.Time {
-	return t.Round(0).Add(-expiryDelta)
-}
-
-// TokenInfo represents access token's info.
-type TokenInfo struct {
-	// Email is email address associated with the access token.
-	Email string
-
-	// Audience is OAuth2 client_id of the access token.
-	Audience string
-
-	// ExpiresAt is expirary timestamp of the access token.
-	ExpiresAt time.Time
-
-	// Err represents error of access token.
-	Err error
-}
-
-// parseToken parses authorization header and extracts oauth2 token.
-func parseToken(auth string) (*oauth2.Token, error) {
-	if !strings.HasPrefix(auth, "Bearer ") {
-		return nil, fmt.Errorf("not bearer authorization header: %q", auth)
-	}
-	return &oauth2.Token{
-		AccessToken: strings.TrimSpace(strings.TrimPrefix(auth, "Bearer ")),
-		TokenType:   "Bearer",
-	}, nil
-}
-
-func tokenKey(token *oauth2.Token) string {
-	return fmt.Sprintf("%s %s", token.TokenType, token.AccessToken)
-}
-
-const tokeninfoURL = "https://oauth2.googleapis.com/tokeninfo?access_token="
-
-func trimAccessTokenInErr(err error, accessToken string) error {
-	if err == nil {
-		return nil
-	}
-	if strings.Contains(err.Error(), accessToken) {
-		return errors.New(strings.Replace(err.Error(), accessToken, accessToken[:len(accessToken)/3], -1) + "...")
-	}
-	return err
-}
-
-// Fetch fetches access token info.
-func fetch(ctx context.Context, token *oauth2.Token) (*TokenInfo, error) {
-	req, err := http.NewRequest("GET", tokeninfoURL+url.QueryEscape(token.AccessToken), nil)
-	if err != nil {
-		return nil, trimAccessTokenInErr(err, token.AccessToken)
-	}
-
-	var tokenInfo *TokenInfo
-	err = rpc.Retry{}.Do(ctx, func() error {
-		// always use 1 min timeout regardless of incoming context.
-		// retry is controlled by incoming context.
-		ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute)
-		defer cancel()
-		req = req.WithContext(ctx)
-		resp, err := http.DefaultClient.Do(req)
-		if err != nil {
-			return status.Errorf(codes.Unavailable, "fetch error: %v", trimAccessTokenInErr(err, token.AccessToken))
-		}
-		defer resp.Body.Close()
-		data, err := ioutil.ReadAll(resp.Body)
-		if err != nil {
-			return status.Errorf(codes.Internal, "read tokeninfo response: code=%d %v", resp.StatusCode, err)
-		}
-		tokenInfo, err = parseResp(data)
-		if err != nil {
-			return status.Errorf(codes.Internal, "parse tokeninfo response: code=%d %v", resp.StatusCode, err)
-		}
-		return nil
-	})
-	return tokenInfo, err
-}
-
-func parseResp(data []byte) (*TokenInfo, error) {
-	var js struct {
-		Email            string `json:"email"`
-		Audience         string `json:"aud"`
-		ExpiresAt        string `json:"exp"`
-		Error            string `json:"error"`
-		ErrorDescription string `json:"error_description"`
-	}
-	d := json.NewDecoder(bytes.NewReader(data))
-	err := d.Decode(&js)
-	if err != nil {
-		return nil, err
-	}
-	if js.Email == "" && js.ExpiresAt == "" && js.Error == "" && js.ErrorDescription == "" {
-		return nil, status.Errorf(codes.Internal, "unexpected token info: %v", js)
-	}
-	ti := &TokenInfo{
-		Email:    js.Email,
-		Audience: js.Audience,
-	}
-	if js.Error != "" || js.ErrorDescription != "" {
-		ti.Err = status.Errorf(codes.PermissionDenied, "%s: %q", js.Error, js.ErrorDescription)
-	} else {
-		// parse exp iff no error is set.
-		// when error exists, maybe no exp.
-		ts, err := strconv.ParseInt(js.ExpiresAt, 10, 64)
-		if err != nil {
-			ti.Err = status.Errorf(codes.Internal, "parse exp: %v", err)
-		} else {
-			ti.ExpiresAt = time.Unix(ts, 0)
-		}
-	}
-	return ti, nil
-}
-
-func runAt(t time.Time, f func()) {
-	time.Sleep(t.Sub(time.Now()))
-	f()
-}
diff --git a/auth/token_test.go b/auth/token_test.go
deleted file mode 100644
index 8d2caad..0000000
--- a/auth/token_test.go
+++ /dev/null
@@ -1,116 +0,0 @@
-// Copyright 2018 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.
-
-package auth
-
-import (
-	"reflect"
-	"testing"
-	"time"
-
-	"golang.org/x/oauth2"
-	"google.golang.org/grpc/codes"
-	"google.golang.org/grpc/status"
-)
-
-func TestParseToken(t *testing.T) {
-	testcases := []struct {
-		input     string
-		want      *oauth2.Token
-		wantError bool
-	}{
-		{
-			input: "Bearer mF_9.B5f-4.1JqM",
-			want: &oauth2.Token{
-				AccessToken: "mF_9.B5f-4.1JqM",
-				TokenType:   "Bearer",
-			},
-		},
-		{
-			input:     "invalid",
-			wantError: true,
-		},
-	}
-
-	for _, tc := range testcases {
-		got, err := parseToken(tc.input)
-		if tc.wantError && err == nil {
-			t.Errorf("Parse(%s)=_, nil; want error", tc.input)
-		}
-		if !tc.wantError && err != nil {
-			t.Errorf("Parse(%s)=_, %v; want nil", tc.input, err)
-		}
-		if !reflect.DeepEqual(got, tc.want) {
-			t.Errorf("Parse(%s)=%v; want %v", tc.input, got, tc.want)
-		}
-	}
-}
-
-func TestParseResponse(t *testing.T) {
-	testcases := []struct {
-		input              []byte
-		want               *TokenInfo
-		wantError          bool
-		wantTokenInfoError codes.Code
-	}{
-		{
-			input:     nil,
-			wantError: true,
-		},
-		{
-			input:     []byte(""),
-			wantError: true,
-		},
-		{
-			input:     []byte("{}"),
-			wantError: true,
-		},
-		{
-			input: []byte(`{
-				"email": "user@example.org",
-				"exp": "not_a_number"
-			}`),
-			wantTokenInfoError: codes.Internal,
-		},
-		{
-			input: []byte(`{
-				"email": "user@example.org",
-				"exp": "12345"
-			}`),
-			want: &TokenInfo{
-				Email:     "user@example.org",
-				ExpiresAt: time.Unix(12345, 0),
-			},
-		},
-		{
-			input: []byte(`{
-				"email": "user@example.org",
-				"error_description": "this is error."
-			}`),
-			wantTokenInfoError: codes.PermissionDenied,
-		},
-	}
-	for _, tc := range testcases {
-		ti, err := parseResp(tc.input)
-		if tc.wantError {
-			if err == nil {
-				t.Errorf("parseResp(%s)=_, nil; want error", tc.input)
-			}
-			continue
-		}
-		if err != nil {
-			t.Errorf("parseResp(%s)=_, %v; want nil", tc.input, err)
-		}
-		if tc.wantTokenInfoError != codes.OK {
-			st, _ := status.FromError(ti.Err)
-			if st.Code() != tc.wantTokenInfoError {
-				t.Errorf("parseResp(%s) .Err=%v; want=%v", tc.input, ti.Err, tc.wantTokenInfoError)
-			}
-			continue
-		}
-		if !reflect.DeepEqual(ti, tc.want) {
-			t.Errorf("parseResp(%s)=%s; want %s", tc.input, ti, tc.want)
-		}
-	}
-}
diff --git a/backend/backend.go b/backend/backend.go
deleted file mode 100644
index e99984a..0000000
--- a/backend/backend.go
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2018 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.
-
-package backend
-
-import (
-	"context"
-	"errors"
-	"fmt"
-	"net/http"
-
-	pb "go.chromium.org/goma/server/proto/backend"
-)
-
-// Backend is interface of backend for frontend.
-// TODO: provides switching backend,
-// httprpc backend (e.g. to clients5.google.com/cxx-compiler-service/*) and
-// remote backend (e.g. to <cluster>.endpoints.<project>.cloud.goog)
-type Backend interface {
-	Ping() http.Handler
-	Exec() http.Handler
-	ByteStream() http.Handler
-	StoreFile() http.Handler
-	LookupFile() http.Handler
-	Execlog() http.Handler
-}
-
-// Option is backend option.
-type Option struct {
-	Auth      Auth
-	APIKeyDir string
-}
-
-// FromProto creates Backend based on cfg.
-// returned func will release resources associated with Backend.
-func FromProto(ctx context.Context, cfg *pb.BackendConfig, opt Option) (Backend, func(), error) {
-	switch be := cfg.Backend.(type) {
-	case *pb.BackendConfig_Local:
-		return FromLocalBackend(ctx, be.Local, opt)
-	case *pb.BackendConfig_HttpRpc:
-		return FromHTTPRPCBackend(ctx, be.HttpRpc)
-	case *pb.BackendConfig_Remote:
-		return FromRemoteBackend(ctx, be.Remote, opt)
-	case *pb.BackendConfig_Rule:
-		return FromBackendRule(ctx, be.Rule, opt)
-	case nil:
-		return nil, func() {}, errors.New("no backend in config")
-	default:
-		return nil, func() {}, fmt.Errorf("unknown type in backend: %T", cfg.Backend)
-	}
-}
diff --git a/backend/ctx.go b/backend/ctx.go
deleted file mode 100644
index cb145da..0000000
--- a/backend/ctx.go
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2018 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.
-
-package backend
-
-import (
-	"context"
-
-	"go.chromium.org/goma/server/auth/enduser"
-)
-
-// passThroughContext passes through enuser credentials from incoming to outgoing.
-func passThroughContext(ctx context.Context) context.Context {
-	if user, ok := enduser.FromContext(ctx); ok {
-		ctx = enduser.NewContext(ctx, user)
-	}
-	return ctx
-}
diff --git a/backend/doc.go b/backend/doc.go
deleted file mode 100644
index 4fc3a5a..0000000
--- a/backend/doc.go
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright 2018 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.
-
-/*
-Package backend is goma service backend of /cxx-compiler-service/*.
-*/
-package backend
diff --git a/backend/error.go b/backend/error.go
deleted file mode 100644
index 43c2a8e..0000000
--- a/backend/error.go
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2018 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.
-
-package backend
-
-import (
-	"context"
-
-	netctx "golang.org/x/net/context"
-	"google.golang.org/grpc"
-	"google.golang.org/grpc/codes"
-	"google.golang.org/grpc/status"
-
-	"go.chromium.org/goma/server/log"
-)
-
-func wrapError(ctx context.Context, service string, err error) error {
-	if err == nil {
-		return nil
-	}
-	logger := log.FromContext(ctx)
-	st, _ := status.FromError(err)
-	code := st.Code()
-	if code == codes.Unknown {
-		switch ctx.Err() {
-		case context.Canceled, netctx.Canceled:
-			code = codes.Canceled
-		case context.DeadlineExceeded, netctx.DeadlineExceeded:
-			code = codes.DeadlineExceeded
-		}
-	}
-	err = grpc.Errorf(code, "failed to call %s: %s", service, st.Message())
-	switch code {
-	case codes.Unavailable, codes.Canceled, codes.Aborted:
-		logger.Warnf("call %s err: %v", service, err)
-	default:
-		logger.Errorf("call %s err: %v", service, err)
-	}
-	return err
-}
diff --git a/backend/exec.go b/backend/exec.go
deleted file mode 100644
index 1058f2f..0000000
--- a/backend/exec.go
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2017 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.
-
-package backend
-
-import (
-	"context"
-
-	"go.opencensus.io/trace"
-	"google.golang.org/grpc"
-
-	"go.chromium.org/goma/server/exec"
-	"go.chromium.org/goma/server/log"
-	gomapb "go.chromium.org/goma/server/proto/api"
-	execpb "go.chromium.org/goma/server/proto/exec"
-	"go.chromium.org/goma/server/rpc"
-)
-
-// ExecServer handles /e.
-type ExecServer struct {
-	execpb.UnimplementedExecServiceServer
-	Client execpb.ExecServiceClient
-}
-
-// Exec handles /e.
-func (s ExecServer) Exec(ctx context.Context, req *gomapb.ExecReq) (*gomapb.ExecResp, error) {
-	ctx, span := trace.StartSpan(ctx, "go.chromium.org/goma/server/backend.ExecServer.Exec")
-	defer span.End()
-	ctx = passThroughContext(ctx)
-	ctx, id := rpc.TagID(ctx, req.GetRequesterInfo())
-	logger := log.FromContext(ctx)
-	logger.Infof("call exec %s", id)
-	resp, err := s.Client.Exec(ctx, req, grpc.MaxCallSendMsgSize(exec.DefaultMaxReqMsgSize), grpc.MaxCallRecvMsgSize(exec.DefaultMaxRespMsgSize))
-	return resp, wrapError(ctx, "exec", err)
-}
diff --git a/backend/execlog.go b/backend/execlog.go
deleted file mode 100644
index 174f28d..0000000
--- a/backend/execlog.go
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2017 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.
-
-package backend
-
-import (
-	"context"
-
-	"go.opencensus.io/trace"
-	"google.golang.org/grpc"
-
-	"go.chromium.org/goma/server/execlog"
-	gomapb "go.chromium.org/goma/server/proto/api"
-	execlogpb "go.chromium.org/goma/server/proto/execlog"
-)
-
-// ExeclogServer handles /sl.
-type ExeclogServer struct {
-	execlogpb.UnimplementedLogServiceServer
-	Client execlogpb.LogServiceClient
-}
-
-// SaveLog handles /sl.
-func (s ExeclogServer) SaveLog(ctx context.Context, req *gomapb.SaveLogReq) (*gomapb.SaveLogResp, error) {
-	ctx, span := trace.StartSpan(ctx, "go.chromium.org/goma/server/backend.ExeclogServer.SaveLog")
-	defer span.End()
-	ctx = passThroughContext(ctx)
-	resp, err := s.Client.SaveLog(ctx, req, grpc.MaxCallSendMsgSize(execlog.DefaultMaxReqMsgSize))
-	return resp, wrapError(ctx, "execlog", err)
-}
diff --git a/backend/file.go b/backend/file.go
deleted file mode 100644
index 79ae1d4..0000000
--- a/backend/file.go
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2017 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.
-
-package backend
-
-import (
-	"context"
-
-	"go.opencensus.io/trace"
-	"google.golang.org/grpc"
-
-	"go.chromium.org/goma/server/file"
-	"go.chromium.org/goma/server/log"
-	gomapb "go.chromium.org/goma/server/proto/api"
-	filepb "go.chromium.org/goma/server/proto/file"
-	"go.chromium.org/goma/server/rpc"
-)
-
-// FileServer handles /s and /l.
-type FileServer struct {
-	filepb.UnimplementedFileServiceServer
-	Client filepb.FileServiceClient
-}
-
-// StoreFile handles /s.
-func (s FileServer) StoreFile(ctx context.Context, req *gomapb.StoreFileReq) (*gomapb.StoreFileResp, error) {
-	ctx, span := trace.StartSpan(ctx, "go.chromium.org/goma/server/backend.FileServer.StoreFile")
-	defer span.End()
-	ctx = passThroughContext(ctx)
-	ctx, id := rpc.TagID(ctx, req.GetRequesterInfo())
-	logger := log.FromContext(ctx)
-	logger.Infof("call storefile %s", id)
-	resp, err := s.Client.StoreFile(ctx, req, grpc.MaxCallSendMsgSize(file.DefaultMaxMsgSize))
-	return resp, wrapError(ctx, "storefile", err)
-}
-
-// LookupFile handles /l.
-func (s FileServer) LookupFile(ctx context.Context, req *gomapb.LookupFileReq) (*gomapb.LookupFileResp, error) {
-	ctx, span := trace.StartSpan(ctx, "go.chromium.org/goma/server/backend.FileServer.StoreFile")
-	defer span.End()
-	ctx = passThroughContext(ctx)
-	ctx, id := rpc.TagID(ctx, req.GetRequesterInfo())
-	logger := log.FromContext(ctx)
-	logger.Infof("call lookupfile %s", id)
-	resp, err := s.Client.LookupFile(ctx, req, grpc.MaxCallRecvMsgSize(file.DefaultMaxMsgSize))
-	return resp, wrapError(ctx, "lookupfile", err)
-}
diff --git a/backend/grpc.go b/backend/grpc.go
deleted file mode 100644
index 19260af..0000000
--- a/backend/grpc.go
+++ /dev/null
@@ -1,113 +0,0 @@
-// Copyright 2018 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.
-
-package backend
-
-import (
-	"context"
-	"fmt"
-	"net/http"
-	"strings"
-	"time"
-
-	bspb "google.golang.org/genproto/googleapis/bytestream"
-
-	"go.chromium.org/goma/server/httprpc"
-	bytestreamrpc "go.chromium.org/goma/server/httprpc/bytestream"
-	execrpc "go.chromium.org/goma/server/httprpc/exec"
-	execlogrpc "go.chromium.org/goma/server/httprpc/execlog"
-	filerpc "go.chromium.org/goma/server/httprpc/file"
-	"go.chromium.org/goma/server/log"
-	"go.chromium.org/goma/server/rpc"
-)
-
-// Auth authenticates the request.
-type Auth interface {
-	// Auth checks HTTP access, and returns new context with enduser info.
-	Auth(context.Context, *http.Request) (context.Context, error)
-}
-
-// GRPC is grpc backend in the same cluster (local) or other cluter (remote).
-type GRPC struct {
-	ExecServer
-	FileServer
-	ExeclogServer
-
-	ByteStreamClient bspb.ByteStreamClient
-
-	Auth Auth
-	// api key. used for remote backend.
-	APIKey string
-
-	// trace prefix and label. used for local backend.
-	Namespace string
-	Cluster   string
-}
-
-func (g GRPC) httprpcOpts(timeout time.Duration) []httprpc.HandlerOption {
-	return []httprpc.HandlerOption{
-		httprpc.Timeout(timeout),
-		httprpc.WithRetry(rpc.Retry{}),
-		httprpc.WithAuth(g.Auth),
-		httprpc.WithAPIKey(g.APIKey),
-		httprpc.WithNamespace(g.Namespace),
-		httprpc.WithCluster(g.Cluster),
-	}
-}
-
-// Ping returns http handler for ping.
-func (g GRPC) Ping() http.Handler {
-	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
-		// TODO: hard fail if g.Auth == nil?
-		if g.Auth != nil {
-			ctx, err := g.Auth.Auth(req.Context(), req)
-			if err != nil {
-				code := http.StatusUnauthorized
-				logger := log.FromContext(ctx)
-				logger.Errorf("server error %s: %d %s: %v", req.URL.Path, code, http.StatusText(code), err)
-				http.Error(w, http.StatusText(code), code)
-				return
-			}
-			req = req.WithContext(ctx)
-		}
-		// Accept-Encoding: deflate only if client didn't say gzip,
-		// since old goma client only recognizes "Accept-Encoding: deflate".
-		// TODO: always accept gzip, deflate once new goma client released.
-		if strings.Contains(req.Header.Get("Accept-Encoding"), "gzip") {
-			w.Header().Set("Accept-Encoding", "gzip, deflate")
-		} else {
-			w.Header().Set("Accept-Encoding", "deflate")
-		}
-		// TODO: health status of backend servers?
-		fmt.Fprintln(w, "ok")
-	})
-}
-
-// Exec returns http handler for exec request.
-func (g GRPC) Exec() http.Handler {
-	return execrpc.Handler(g.ExecServer, g.httprpcOpts(9*time.Minute+50*time.Second)...)
-}
-
-// ByteStream returns http handler for bytestream.
-func (g GRPC) ByteStream() http.Handler {
-	if g.ByteStreamClient == nil {
-		return http.HandlerFunc(http.NotFound)
-	}
-	return bytestreamrpc.Handler(g.ByteStreamClient, g.httprpcOpts(1*time.Minute)...)
-}
-
-// StoreFile returns http handler for store file request.
-func (g GRPC) StoreFile() http.Handler {
-	return filerpc.StoreHandler(g.FileServer, g.httprpcOpts(1*time.Minute)...)
-}
-
-// LookupFile returns http handler for lookup file request.
-func (g GRPC) LookupFile() http.Handler {
-	return filerpc.LookupHandler(g.FileServer, g.httprpcOpts(1*time.Minute)...)
-}
-
-// Execlog returns http handler for execlog request.
-func (g GRPC) Execlog() http.Handler {
-	return execlogrpc.Handler(g.ExeclogServer, g.httprpcOpts(1*time.Minute)...)
-}
diff --git a/backend/httprpc.go b/backend/httprpc.go
deleted file mode 100644
index 7a1f7dc..0000000
--- a/backend/httprpc.go
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright 2018 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.
-
-package backend
-
-import (
-	"context"
-	"net/http"
-	"net/http/httputil"
-	"net/url"
-	"path"
-
-	pb "go.chromium.org/goma/server/proto/backend"
-)
-
-// FromHTTPRPCBackend creates new httprpc backend from cfg.
-func FromHTTPRPCBackend(ctx context.Context, cfg *pb.HttpRpcBackend) (HTTPRPC, func(), error) {
-	target, err := url.Parse(cfg.Target)
-	if err != nil {
-		return HTTPRPC{}, func() {}, err
-	}
-	return NewHTTPRPC(target), func() {}, nil
-}
-
-// HTTPRPC is httprpc backend.
-type HTTPRPC struct {
-	proxy *httputil.ReverseProxy
-}
-
-// NewHTTPRPC creates httprpc backend proxies to target (scheme + host).
-// target's path etc will be ignored.
-func NewHTTPRPC(target *url.URL) HTTPRPC {
-	return HTTPRPC{
-		proxy: reverseProxy(target),
-	}
-}
-
-func reverseProxy(target *url.URL) *httputil.ReverseProxy {
-	// almost same as httputil.NewSingleHostReverseProxy, but
-	//  - don't modify query.
-	//  - rewrite Host header.
-	return &httputil.ReverseProxy{
-		Director: func(req *http.Request) {
-			req.URL.Scheme = target.Scheme
-			req.URL.Host = target.Host
-			req.URL.Path = path.Join(target.Path, req.URL.Path)
-			if _, ok := req.Header["User-Agent"]; !ok {
-				// explicitly disable User-Agent so it's not set to default value
-				req.Header.Set("User-Agent", "")
-			}
-			req.Host = target.Host
-			req.Header.Set("Host", target.Host)
-		},
-		Transport: http.DefaultClient.Transport,
-	}
-}
-
-// Ping forwards requests to target.
-func (h HTTPRPC) Ping() http.Handler {
-	return h.proxy
-}
-
-// Exec forwards requests to target.
-func (h HTTPRPC) Exec() http.Handler {
-	return h.proxy
-}
-
-// ByteStream forwards requests to target.
-func (h HTTPRPC) ByteStream() http.Handler {
-	return h.proxy
-}
-
-// StoreFile forwards requests to target.
-func (h HTTPRPC) StoreFile() http.Handler {
-	return h.proxy
-}
-
-// LookupFile forwards requests to target.
-func (h HTTPRPC) LookupFile() http.Handler {
-	return h.proxy
-}
-
-// Execlog forwards requests to target.
-func (h HTTPRPC) Execlog() http.Handler {
-	return h.proxy
-}
diff --git a/backend/httprpc_test.go b/backend/httprpc_test.go
deleted file mode 100644
index 897d22c..0000000
--- a/backend/httprpc_test.go
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright 2018 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.
-
-package backend
-
-import (
-	"fmt"
-	"go.chromium.org/goma/server/server"
-	"io/ioutil"
-	"net/http"
-	"net/http/httptest"
-	"net/url"
-	"testing"
-)
-
-func TestHTTPRPCPing(t *testing.T) {
-	defaultTransport := http.DefaultTransport
-	defaultClient := http.DefaultClient
-	defer func() {
-		http.DefaultTransport = defaultTransport
-		http.DefaultClient = defaultClient
-	}()
-	server.SetupHTTPClient()
-
-	const pingResponse = "ok - ping response"
-	s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
-		fmt.Fprint(w, pingResponse)
-	}))
-	defer s.Close()
-
-	u, err := url.Parse(s.URL)
-	if err != nil {
-		t.Fatal(err)
-	}
-	p := NewHTTPRPC(u)
-	ps := httptest.NewServer(p.Ping())
-	defer ps.Close()
-
-	t.Logf("httprpc: %s -> %s", ps.URL, s.URL)
-	resp, err := http.Get(ps.URL)
-	if err != nil {
-		t.Fatal(err)
-	}
-	defer resp.Body.Close()
-	if resp.StatusCode != 200 {
-		t.Errorf("resp=%d; want=200", resp.StatusCode)
-	}
-	buf, err := ioutil.ReadAll(resp.Body)
-	if err != nil {
-		t.Error(err)
-	}
-	if string(buf) != pingResponse {
-		t.Errorf("response=%q; want=%q", string(buf), pingResponse)
-	}
-}
diff --git a/backend/local.go b/backend/local.go
deleted file mode 100644
index 1206c21..0000000
--- a/backend/local.go
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright 2018 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.
-
-package backend
-
-import (
-	"context"
-	"fmt"
-
-	bspb "google.golang.org/genproto/googleapis/bytestream"
-	"google.golang.org/grpc"
-
-	"go.chromium.org/goma/server/exec"
-	"go.chromium.org/goma/server/execlog"
-	"go.chromium.org/goma/server/file"
-	pb "go.chromium.org/goma/server/proto/backend"
-	filepb "go.chromium.org/goma/server/proto/file"
-	"go.chromium.org/goma/server/server"
-)
-
-// FromLocalBackend creates new GRPC from cfg.
-// returned func would release resources associated with GRPC.
-func FromLocalBackend(ctx context.Context, cfg *pb.LocalBackend, opt Option) (GRPC, func(), error) {
-	fileAddr := cfg.FileAddr
-	if fileAddr == "" {
-		fileAddr = "file-server:5050"
-	}
-	fileConn, err := server.DialContext(ctx, fileAddr,
-		grpc.WithDefaultCallOptions(
-			grpc.MaxCallSendMsgSize(file.DefaultMaxMsgSize),
-			grpc.MaxCallRecvMsgSize(file.DefaultMaxMsgSize),
-			grpc.FailFast(false)))
-	if err != nil {
-		return GRPC{}, func() {}, fmt.Errorf("dial %s: %v", fileAddr, err)
-	}
-	execAddr := cfg.ExecAddr
-	if execAddr == "" {
-		execAddr = "exec-server:5050"
-	}
-	var bsConn *grpc.ClientConn
-	var bsClient bspb.ByteStreamClient
-	if cfg.EnableBytestream {
-		bsConn, err = server.DialContext(ctx, execAddr)
-		if err != nil {
-			fileConn.Close()
-			return GRPC{}, func() {}, fmt.Errorf("dial %s: %v", execAddr, err)
-		}
-		bsClient = bspb.NewByteStreamClient(bsConn)
-	}
-	dialOptions := append(
-		[]grpc.DialOption{
-			grpc.WithDefaultCallOptions(grpc.FailFast(false)),
-		},
-		server.DefaultDialOption()...)
-	execlogAddr := cfg.ExeclogAddr
-	if execlogAddr == "" {
-		execlogAddr = "execlog-server:5050"
-	}
-	be := GRPC{
-		ExecServer: ExecServer{
-			Client: exec.NewClient(execAddr, dialOptions...),
-		},
-		FileServer: FileServer{
-			Client: filepb.NewFileServiceClient(fileConn),
-		},
-		ExeclogServer: ExeclogServer{
-			Client: execlog.NewClient(execlogAddr, dialOptions...),
-		},
-		ByteStreamClient: bsClient,
-		Auth:             opt.Auth,
-	}
-	if cfg.TraceOption != nil {
-		be.Namespace = cfg.TraceOption.Namespace
-		be.Cluster = cfg.TraceOption.Cluster
-	}
-	return be, func() {
-		bsConn.Close()
-		fileConn.Close()
-	}, nil
-}
diff --git a/backend/mixer.go b/backend/mixer.go
deleted file mode 100644
index 0f16ac3..0000000
--- a/backend/mixer.go
+++ /dev/null
@@ -1,180 +0,0 @@
-// Copyright 2018 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.
-
-package backend
-
-import (
-	"context"
-	"errors"
-	"fmt"
-	"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"
-)
-
-func fromBackendMapping(ctx context.Context, cfg *pb.BackendMapping, opt Option) (Backend, func(), error) {
-	groupId := cfg.GroupId
-	if groupId == "" {
-		groupId = "default group"
-	}
-	switch be := cfg.Backend.(type) {
-	case *pb.BackendMapping_HttpRpc:
-		return FromHTTPRPCBackend(ctx, be.HttpRpc)
-	case *pb.BackendMapping_Remote:
-		return FromRemoteBackend(ctx, be.Remote, opt)
-	case nil:
-		return nil, func() {}, fmt.Errorf("no backend for group:%q", groupId)
-	default:
-		return nil, func() {}, fmt.Errorf("unknown type in %s: %T", groupId, cfg.Backend)
-	}
-}
-
-// FromBackendRule creates new Mixer from cfg.
-// returned func would release resources associated with Mixer.
-func FromBackendRule(ctx context.Context, cfg *pb.BackendRule, opt Option) (mixer Mixer, cleanup func(), err error) {
-	var cleanups []func()
-	defer func() {
-		if err != nil {
-			for _, c := range cleanups {
-				c()
-			}
-		}
-	}()
-	logger := log.FromContext(ctx)
-	mixer.Auth = opt.Auth
-	mixer.backends = make(map[string]Backend)
-	for _, backend := range cfg.Backends {
-		if backend.GroupId == "" {
-			if len(backend.QueryParams) > 0 {
-				logger.Warnf("non empty query_params for default backend: %v", backend.QueryParams)
-			}
-			if mixer.defaultBackend != nil {
-				return Mixer{}, func() {}, errors.New("duplicate default backend")
-			}
-			be, cleanup, err := fromBackendMapping(ctx, backend, opt)
-			if err != nil {
-				logger.Warnf("ignore bad backend[default] %s: %v", backend, err)
-				continue
-			}
-			mixer.defaultBackend = be
-			cleanups = append(cleanups, cleanup)
-			continue
-		}
-		key := backendKeyFromProto(ctx, backend)
-		if _, found := mixer.backends[key]; found {
-			return Mixer{}, func() {}, fmt.Errorf("duplicate backend group: %s", key)
-		}
-		be, cleanup, err := fromBackendMapping(ctx, backend, opt)
-		if err != nil {
-			logger.Warnf("ignore bad backend %s: %v", backend, err)
-			continue
-		}
-		mixer.backends[key] = be
-		cleanups = append(cleanups, cleanup)
-	}
-	if len(mixer.backends) == 0 && mixer.defaultBackend == nil {
-		return Mixer{}, func() {}, fmt.Errorf("no valid backends in %s", cfg)
-	}
-	return mixer, func() {
-		for _, c := range cleanups {
-			c()
-		}
-	}, nil
-}
-
-func backendKeyFromProto(ctx context.Context, m *pb.BackendMapping) string {
-	q, err := url.ParseQuery(m.QueryParams)
-	if err != nil {
-		logger := log.FromContext(ctx)
-		logger.Errorf("invalid query_params %q: %v", m.QueryParams, err)
-		return backendKey(m.GroupId, nil)
-	}
-	return backendKey(m.GroupId, q)
-}
-
-func backendKey(group string, q url.Values) string {
-	if len(q) == 0 {
-		return group
-	}
-	return group + "?" + q.Encode()
-}
-
-// Mixer is mixer backend, dispatched by group of enduser.
-type Mixer struct {
-	backends       map[string]Backend
-	defaultBackend Backend
-	Auth           Auth
-}
-
-func (m Mixer) Ping() http.Handler       { return m.dispatcher(Backend.Ping) }
-func (m Mixer) Exec() http.Handler       { return m.dispatcher(Backend.Exec) }
-func (m Mixer) ByteStream() http.Handler { return m.dispatcher(Backend.ByteStream) }
-func (m Mixer) StoreFile() http.Handler  { return m.dispatcher(Backend.StoreFile) }
-func (m Mixer) LookupFile() http.Handler { return m.dispatcher(Backend.LookupFile) }
-func (m Mixer) Execlog() http.Handler    { return m.dispatcher(Backend.Execlog) }
-
-func (m Mixer) selectBackend(ctx context.Context, group string, q url.Values) (Backend, bool) {
-	logger := log.FromContext(ctx)
-	key := backendKey(group, q)
-	backend, found := m.backends[key]
-	if found {
-		logger.Infof("backend %s", key)
-		return backend, true
-	}
-	key = backendKey(group, nil)
-	backend, found = m.backends[key]
-	if found {
-		logger.Infof("backend %s (ignore query param:%s)", key, q)
-		return backend, true
-	}
-	backend = m.defaultBackend
-	if backend != nil {
-		logger.Infof("backend default for %s", key)
-		return backend, true
-	}
-	return nil, false
-}
-
-func (m Mixer) dispatcher(handler func(Backend) http.Handler) http.Handler {
-	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
-		ctx := req.Context()
-		logger := log.FromContext(ctx)
-		if m.Auth != nil {
-			var err error
-			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 and http://b/230568560) for details.
-				switch err {
-				case auth.ErrExpired, auth.ErrInternal:
-					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
-			}
-		}
-		user, ok := enduser.FromContext(ctx)
-		if !ok {
-			code := http.StatusForbidden
-			http.Error(w, "no enduser info available", code)
-			logger.Errorf("server error %s: %d %s: no enduser in context", req.URL.Path, code, http.StatusText(code))
-			return
-		}
-		q := req.URL.Query()
-		backend, found := m.selectBackend(ctx, user.Group, q)
-		if !found {
-			logger.Errorf("no backend config for group:%q query:%q", user.Group, q.Encode())
-			http.Error(w, "no backend config", http.StatusForbidden)
-			return
-		}
-		h := handler(backend)
-		h.ServeHTTP(w, req)
-	})
-}
diff --git a/backend/mixer_test.go b/backend/mixer_test.go
deleted file mode 100644
index 428e7d1..0000000
--- a/backend/mixer_test.go
+++ /dev/null
@@ -1,134 +0,0 @@
-// Copyright 2018 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.
-
-package backend
-
-import (
-	"context"
-	"net/url"
-	"testing"
-)
-
-type dummyBackend struct {
-	Backend
-	id string
-}
-
-func TestMixerSelectBackend(t *testing.T) {
-	ctx := context.Background()
-	winQuery, err := url.ParseQuery("win")
-	if err != nil {
-		t.Fatalf("win: %v", err)
-	}
-	stagingQuery, err := url.ParseQuery("staging")
-	if err != nil {
-		t.Fatalf("staging: %v", err)
-	}
-	stagingWinQuery, err := url.ParseQuery("win&staging")
-	if err != nil {
-		t.Fatalf("win&staging: %v", err)
-	}
-	mixer := Mixer{
-		backends: map[string]Backend{
-			backendKey("goma-group1", nil): dummyBackend{
-				id: "group1-backend",
-			},
-			backendKey("goma-group2", nil): dummyBackend{
-				id: "group2-backend",
-			},
-			backendKey("service-account-goma-client", nil): dummyBackend{
-				id: "prod-backend",
-			},
-			backendKey("service-account-goma-client", winQuery): dummyBackend{
-				id: "prod-backend",
-			},
-			backendKey("service-account-goma-client", stagingQuery): dummyBackend{
-				id: "staging-backend",
-			},
-			backendKey("service-account-goma-client", stagingWinQuery): dummyBackend{
-				id: "staging-backend",
-			},
-		},
-		defaultBackend: dummyBackend{
-			id: "default-backend",
-		},
-	}
-
-	for _, tc := range []struct {
-		group    string
-		rawQuery string
-		target   string
-	}{
-		{
-			group:  "googlers",
-			target: "default-backend",
-		},
-		{
-			group:    "googlers",
-			rawQuery: "win",
-			target:   "default-backend",
-		},
-		{
-			group:    "googlers",
-			rawQuery: "staging",
-			target:   "default-backend",
-		},
-		{
-			group:  "goma-group1",
-			target: "group1-backend",
-		},
-		{
-			group:    "goma-group1",
-			rawQuery: "win",
-			target:   "group1-backend",
-		},
-		{
-			group:  "goma-group2",
-			target: "group2-backend",
-		},
-		{
-			group:  "service-account-goma-client",
-			target: "prod-backend",
-		},
-		{
-			group:    "service-account-goma-client",
-			rawQuery: "win",
-			target:   "prod-backend",
-		},
-		{
-			group:    "service-account-goma-client",
-			rawQuery: "staging",
-			target:   "staging-backend",
-		},
-		{
-			group:    "service-account-goma-client",
-			rawQuery: "staging&win",
-			target:   "staging-backend",
-		},
-		{
-			group:    "service-account-goma-client",
-			rawQuery: "win&staging",
-			target:   "staging-backend",
-		},
-		{
-			group:    "service-account-goma-client",
-			rawQuery: "staging&extra",
-			target:   "prod-backend",
-		},
-	} {
-		t.Run(tc.group+"?"+tc.rawQuery, func(t *testing.T) {
-			q, err := url.ParseQuery(tc.rawQuery)
-			if err != nil {
-				t.Fatal(err)
-			}
-			backend, found := mixer.selectBackend(ctx, tc.group, q)
-			if !found {
-				t.Fatal("not found")
-			}
-			if got, want := backend.(dummyBackend).id, tc.target; got != want {
-				t.Errorf("selectBackend=%q; want=%q", got, want)
-			}
-		})
-	}
-}
diff --git a/backend/remote.go b/backend/remote.go
deleted file mode 100644
index a3633a3..0000000
--- a/backend/remote.go
+++ /dev/null
@@ -1,97 +0,0 @@
-// Copyright 2018 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.
-
-package backend
-
-import (
-	"context"
-	"crypto/tls"
-	"fmt"
-	"io/ioutil"
-	"path/filepath"
-	"strings"
-	"time"
-
-	"github.com/bazelbuild/remote-apis-sdks/go/pkg/balancer"
-	bpb "github.com/bazelbuild/remote-apis-sdks/go/pkg/balancer/proto"
-	"github.com/bazelbuild/remote-apis-sdks/go/pkg/client"
-	"go.opencensus.io/plugin/ocgrpc"
-	bspb "google.golang.org/genproto/googleapis/bytestream"
-	"google.golang.org/grpc"
-	"google.golang.org/grpc/credentials"
-	"google.golang.org/grpc/keepalive"
-
-	"go.chromium.org/goma/server/log"
-	pb "go.chromium.org/goma/server/proto/backend"
-	execpb "go.chromium.org/goma/server/proto/exec"
-	execlogpb "go.chromium.org/goma/server/proto/execlog"
-	filepb "go.chromium.org/goma/server/proto/file"
-)
-
-// FromRemoteBackend creates new GRPC from cfg.
-// returned func would release resources associated with GRPC.
-func FromRemoteBackend(ctx context.Context, cfg *pb.RemoteBackend, opt Option) (GRPC, func(), error) {
-	logger := log.FromContext(ctx)
-	opts := []grpc.DialOption{
-		grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{})),
-		grpc.WithStatsHandler(&ocgrpc.ClientHandler{}),
-		grpc.WithKeepaliveParams(keepalive.ClientParameters{
-			Time: 10 * time.Second,
-		}),
-	}
-	// TODO: configurable?
-	// use the same default as re-client (remote-apis-sdks).
-	// but don't set MaxConcurrentStreamsLowWatermark not to
-	// open more connections to avoid "Too many open files" error
-	// in nginx.  crbug.com/1151576
-	ac := &bpb.ApiConfig{
-		ChannelPool: &bpb.ChannelPoolConfig{
-			MaxSize: client.DefaultMaxConcurrentRequests,
-		},
-		Method: []*bpb.MethodConfig{
-			{
-				Name: []string{".*"},
-				Affinity: &bpb.AffinityConfig{
-					Command:     bpb.AffinityConfig_BIND,
-					AffinityKey: "bind-affinity",
-				},
-			},
-		},
-	}
-	logger.Infof("api_config=%s", ac)
-	grpcInt := balancer.NewGCPInterceptor(ac)
-	opts = append(opts,
-		grpc.WithDisableServiceConfig(),
-		grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"loadBalancingConfig": [{%q:{}}]}`, balancer.Name)),
-		grpc.WithUnaryInterceptor(grpcInt.GCPUnaryClientInterceptor),
-		grpc.WithStreamInterceptor(grpcInt.GCPStreamClientInterceptor))
-
-	conn, err := grpc.DialContext(ctx, cfg.Address, opts...)
-	if err != nil {
-		return GRPC{}, func() {}, err
-	}
-	var apiKey []byte
-	if cfg.ApiKeyName != "" {
-		apiKey, err = ioutil.ReadFile(filepath.Join(opt.APIKeyDir, cfg.ApiKeyName))
-		if err != nil {
-			return GRPC{}, func() { conn.Close() }, err
-		}
-	}
-	be := GRPC{
-		ExecServer: ExecServer{
-			Client: execpb.NewExecServiceClient(conn),
-		},
-		FileServer: FileServer{
-			Client: filepb.NewFileServiceClient(conn),
-		},
-		ExeclogServer: ExeclogServer{
-			Client: execlogpb.NewLogServiceClient(conn),
-		},
-		// TODO: propagate metadata.
-		ByteStreamClient: bspb.NewByteStreamClient(conn),
-		Auth:             opt.Auth,
-		APIKey:           strings.TrimSpace(string(apiKey)),
-	}
-	return be, func() { conn.Close() }, nil
-}
diff --git a/buildsetup.sh b/buildsetup.sh
deleted file mode 100755
index 4ae1788..0000000
--- a/buildsetup.sh
+++ /dev/null
@@ -1,37 +0,0 @@
-#!/bin/bash
-# Copyright 2019 Google Inc. All Rights Reserved.
-
-set -e
-
-script_dir=$(cd $(dirname $0); pwd)
-sdk_dir="$1"
-if [ ! -d "${sdk_dir}" ]; then
-  echo "usage: $0 sdk_dir" >&2
-  exit 1
-fi
-
-# assume host has ca-certificates, curl, git and gcc.
-pkg_missing=false
-for pkg in ca-certificates curl git gcc; do
-  if ! dpkg-query -s "$pkg" >/dev/null; then
-    echo "E: $pkg not installed" >&2
-    pkg_missing=true
-  fi
-done
-if $pkg_missing; then
-  echo "E: package(s) missing" >&2
-  exit 1
-fi
-
-cipd_dir="${sdk_dir}/.cipd_bin"
-cipd_manifest="$script_dir/cipd_manifest.txt"
-mkdir -p "$cipd_dir"
-cipd ensure -log-level warning \
-  -ensure-file "$cipd_manifest"\
-  -root "$cipd_dir"
-
-(cd "$sdk_dir"; rm -f go; ln -s .cipd_bin/bin/go)
-(cd "$sdk_dir"; rm -f protoc; ln -s .cipd_bin/bin/protoc)
-
-echo "I: protoc and go are installed in ${sdk_dir}"
-echo "I: set ${sdk_dir} in \$PATH"
diff --git a/bytestreamio/bytestreamio.go b/bytestreamio/bytestreamio.go
deleted file mode 100644
index edca6a9..0000000
--- a/bytestreamio/bytestreamio.go
+++ /dev/null
@@ -1,163 +0,0 @@
-// Copyright 2018 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.
-
-// Package bytestreamio provides io interfaces on bytestream service.
-package bytestreamio
-
-import (
-	"context"
-	"errors"
-	"fmt"
-	"io"
-
-	pb "google.golang.org/genproto/googleapis/bytestream"
-)
-
-// Exists checks resource identified by resourceName exists in bytestream server.
-func Exists(ctx context.Context, c pb.ByteStreamClient, resourceName string) error {
-	rd, err := c.Read(ctx, &pb.ReadRequest{
-		ResourceName: resourceName,
-		ReadLimit:    1,
-	})
-	if err != nil {
-		return err
-	}
-	_, err = rd.Recv()
-	return err
-}
-
-// Open opens reader on bytestream for resourceName.
-// ctx will be used until Reader is closed.
-func Open(ctx context.Context, c pb.ByteStreamClient, resourceName string) (*Reader, error) {
-	rd, err := c.Read(ctx, &pb.ReadRequest{
-		ResourceName: resourceName,
-	})
-	if err != nil {
-		return nil, err
-	}
-	return &Reader{
-		rd: rd,
-	}, nil
-}
-
-// Reader is a reader on bytestream.
-type Reader struct {
-	rd   pb.ByteStream_ReadClient
-	buf  []byte
-	size int64
-}
-
-// Read reads data from bytestream.
-// The maximum data chunk size would be determined by server side.
-func (r *Reader) Read(buf []byte) (int, error) {
-	if r.rd == nil {
-		return 0, errors.New("bad Reader")
-	}
-	if len(r.buf) > 0 {
-		n := copy(buf, r.buf)
-		r.buf = r.buf[n:]
-		r.size += int64(n)
-		return n, nil
-	}
-	resp, err := r.rd.Recv()
-	if err != nil {
-		return 0, err
-	}
-	// resp.Data may be empty.
-	// TODO: better to fill buf?
-	r.buf = resp.Data
-	n := copy(buf, r.buf)
-	r.buf = r.buf[n:]
-	r.size += int64(n)
-	return n, nil
-}
-
-// Size reports read size by Read.
-func (r *Reader) Size() int64 {
-	return r.size
-}
-
-// Create creates writer on bytestream for resourceName.
-// ctx will be used until Writer is closed.
-func Create(ctx context.Context, c pb.ByteStreamClient, resourceName string) (*Writer, error) {
-	wr, err := c.Write(ctx)
-	if err != nil {
-		return nil, err
-	}
-	return &Writer{
-		resname: resourceName,
-		wr:      wr,
-	}, nil
-}
-
-// Writer is a writer on bytestream.
-type Writer struct {
-	resname string
-	wr      pb.ByteStream_WriteClient
-	offset  int64
-
-	// bytestream will accept blobs by partial upload if the same
-	// blobs are already uploaded by io.EOF of Send.
-	// then, we don't need to Send rest of data, so Write just returns
-	// success.  Close issues CloseAndRecv and don't check offset.
-	ok bool
-}
-
-// Write writes data to bytestream.
-// The maximum data chunk size would be determined by server side,
-// so don't pass larger chunk than maximum data chunk size.
-func (w *Writer) Write(buf []byte) (int, error) {
-	if w.wr == nil {
-		return 0, errors.New("bad Writer")
-	}
-	if w.ok {
-		return len(buf), nil
-	}
-	err := w.wr.Send(&pb.WriteRequest{
-		ResourceName: w.resname,
-		WriteOffset:  w.offset,
-		Data:         buf,
-	})
-	if err == io.EOF {
-		// the blob already stored in CAS.
-		w.ok = true
-		return len(buf), nil
-	}
-	if err != nil {
-		return 0, err
-	}
-	w.offset += int64(len(buf))
-	return len(buf), nil
-}
-
-// Close cloes the writer.
-func (w *Writer) Close() error {
-	if w.wr == nil {
-		return errors.New("bad Writer")
-	}
-	if w.ok {
-		w.wr.CloseAndRecv()
-		return nil
-	}
-	// The service will not view the resource as 'complete'
-	// until the client has sent a 'WriteRequest' with 'finish_write'
-	// set to 'true'.
-	err := w.wr.Send(&pb.WriteRequest{
-		ResourceName: w.resname,
-		WriteOffset:  w.offset,
-		FinishWrite:  true,
-		// The client may leave 'data' empty.
-	})
-	if err != nil && err != io.EOF {
-		return err
-	}
-	resp, err := w.wr.CloseAndRecv()
-	if err != nil && err != io.EOF {
-		return err
-	}
-	if resp.CommittedSize != w.offset {
-		return fmt.Errorf("upload committed size %d != offset %d", resp.CommittedSize, w.offset)
-	}
-	return nil
-}
diff --git a/bytestreamio/bytestreamio_test.go b/bytestreamio/bytestreamio_test.go
deleted file mode 100644
index 38b7235..0000000
--- a/bytestreamio/bytestreamio_test.go
+++ /dev/null
@@ -1,250 +0,0 @@
-// Copyright 2018 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.
-
-package bytestreamio
-
-import (
-	"bytes"
-	"context"
-	"crypto/rand"
-	"errors"
-	"fmt"
-	"io"
-	"testing"
-
-	"github.com/google/go-cmp/cmp"
-	pb "google.golang.org/genproto/googleapis/bytestream"
-	"google.golang.org/grpc"
-)
-
-type stubByteStreamReadClient struct {
-	pb.ByteStreamClient
-	resourceName string
-	data         []byte
-	chunksize    int
-}
-
-func (c *stubByteStreamReadClient) Read(ctx context.Context, req *pb.ReadRequest, opts ...grpc.CallOption) (pb.ByteStream_ReadClient, error) {
-	if req.ResourceName != c.resourceName {
-		return nil, fmt.Errorf("bad resource name: %q; want %q", req.ResourceName, c.resourceName)
-	}
-	if req.ReadOffset != 0 {
-		return nil, fmt.Errorf("bad read offset=%d; want=%d", req.ReadOffset, 0)
-	}
-	if req.ReadLimit != 0 {
-		return nil, fmt.Errorf("bad read limit=%d; want=%d", req.ReadLimit, 0)
-	}
-	return &stubReadClient{
-		c: c,
-	}, nil
-}
-
-type stubReadClient struct {
-	pb.ByteStream_ReadClient
-	c      *stubByteStreamReadClient
-	offset int
-}
-
-func (r *stubReadClient) Recv() (*pb.ReadResponse, error) {
-	if r.offset >= len(r.c.data) {
-		return nil, io.EOF
-	}
-	data := r.c.data[r.offset:]
-	if len(data) > r.c.chunksize {
-		data = data[:r.c.chunksize]
-	}
-	r.offset += len(data)
-	return &pb.ReadResponse{
-		Data: data,
-	}, nil
-}
-
-func TestReader(t *testing.T) {
-	const datasize = 1 * 1024 * 1024
-	const chunksize = 8192
-	const bufsize = 1024
-	data := make([]byte, 4*1024*1024)
-	_, err := rand.Read(data)
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	const resourceName = "resource-name"
-	c := &stubByteStreamReadClient{
-		resourceName: resourceName,
-		data:         data,
-		chunksize:    chunksize,
-	}
-	ctx := context.Background()
-
-	r, err := Open(ctx, c, resourceName)
-	if err != nil {
-		t.Fatal(err)
-	}
-	var out bytes.Buffer
-	if bytes.Equal(out.Bytes(), data) {
-		t.Fatal("data setup failed")
-	}
-
-	buf := make([]byte, bufsize)
-	_, err = io.CopyBuffer(&out, r, buf)
-	if err != nil {
-		t.Fatal(err)
-	}
-	if !bytes.Equal(out.Bytes(), data) {
-		t.Errorf("read doesn't match: (-want +got)\n%s", cmp.Diff(data, out))
-	}
-}
-
-type stubByteStreamWriteClient struct {
-	pb.ByteStreamClient
-	resourceName  string
-	buf           bytes.Buffer
-	chunksize     int
-	alreadyExists bool
-	finished      bool
-}
-
-func (c *stubByteStreamWriteClient) Write(ctx context.Context, opts ...grpc.CallOption) (pb.ByteStream_WriteClient, error) {
-	return &stubWriteClient{
-		c: c,
-	}, nil
-}
-
-type stubWriteClient struct {
-	pb.ByteStream_WriteClient
-	c *stubByteStreamWriteClient
-}
-
-func (w *stubWriteClient) Send(req *pb.WriteRequest) error {
-	if req.ResourceName != w.c.resourceName {
-		return fmt.Errorf("bad resource name: %q; want %q", req.ResourceName, w.c.resourceName)
-	}
-	if w.c.finished {
-		return errors.New("bad write to finished client")
-	}
-	if req.WriteOffset != int64(w.c.buf.Len()) {
-		return fmt.Errorf("bad write offset=%d; want=%d", req.WriteOffset, w.c.buf.Len())
-	}
-	if req.FinishWrite {
-		w.c.finished = true
-	}
-	if len(req.Data) > w.c.chunksize {
-		return fmt.Errorf("too large data=%d. chunksize=%d", len(req.Data), w.c.chunksize)
-	}
-	w.c.buf.Write(req.Data) // err is always nil.
-	if w.c.alreadyExists {
-		return io.EOF
-	}
-	return nil
-}
-
-func (w *stubWriteClient) CloseAndRecv() (*pb.WriteResponse, error) {
-	return &pb.WriteResponse{
-		CommittedSize: int64(w.c.buf.Len()),
-	}, nil
-}
-
-// to hite WriteTo.
-type bytesReader struct {
-	r *bytes.Reader
-}
-
-func (r bytesReader) Read(buf []byte) (int, error) {
-	return r.r.Read(buf)
-}
-
-func TestWriter(t *testing.T) {
-	const datasize = 1*1024*1024 + 2048
-	const chunksize = 8192
-	const bufsize = 1024
-
-	data := make([]byte, 4*1024*1024)
-	_, err := rand.Read(data)
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	const resourceName = "resource-name"
-	c := &stubByteStreamWriteClient{
-		resourceName: resourceName,
-		chunksize:    chunksize,
-	}
-	if bytes.Equal(c.buf.Bytes(), data) {
-		t.Fatalf("data setup failed")
-	}
-	ctx := context.Background()
-
-	w, err := Create(ctx, c, resourceName)
-	if err != nil {
-		t.Fatal(err)
-	}
-	buf := make([]byte, bufsize)
-	_, err = io.CopyBuffer(w, bytesReader{bytes.NewReader(data)}, buf)
-	if err != nil {
-		w.Close()
-		t.Fatal(err)
-	}
-	err = w.Close()
-	if err != nil {
-		t.Fatal(err)
-	}
-	if !c.finished {
-		t.Errorf("write not finished")
-	}
-	if c.buf.Len() != len(data) {
-		t.Errorf("write len=%d; want=%d", c.buf.Len(), len(data))
-	}
-	if !bytes.Equal(c.buf.Bytes(), data) {
-		t.Errorf("write doesn't match: (-want +got)\n%s", cmp.Diff(data, c.buf.Bytes()))
-	}
-
-}
-
-func TestWriterAlreadyExists(t *testing.T) {
-	const datasize = 1*1024*1024 + 2048
-	const chunksize = 8192
-	const bufsize = 1024
-
-	data := make([]byte, 4*1024*1024)
-	_, err := rand.Read(data)
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	const resourceName = "resource-name"
-	c := &stubByteStreamWriteClient{
-		resourceName:  resourceName,
-		chunksize:     chunksize,
-		alreadyExists: true,
-	}
-	if bytes.Equal(c.buf.Bytes(), data) {
-		t.Fatalf("data setup failed")
-	}
-	ctx := context.Background()
-
-	w, err := Create(ctx, c, resourceName)
-	if err != nil {
-		t.Fatal(err)
-	}
-	buf := make([]byte, bufsize)
-	_, err = io.CopyBuffer(w, bytesReader{bytes.NewReader(data)}, buf)
-	if err != nil {
-		w.Close()
-		t.Fatal(err)
-	}
-	err = w.Close()
-	if err != nil {
-		t.Fatal(err)
-	}
-	if !w.ok {
-		t.Errorf("writer.ok=%t; want=true", w.ok)
-	}
-	if c.buf.Len() == len(data) {
-		t.Errorf("write len=%d << %d", c.buf.Len(), len(data))
-	}
-	if bytes.Equal(c.buf.Bytes(), data) {
-		t.Errorf("write match? should not match for already exists resource")
-	}
-}
diff --git a/cache/cache.go b/cache/cache.go
deleted file mode 100644
index 017ecce..0000000
--- a/cache/cache.go
+++ /dev/null
@@ -1,289 +0,0 @@
-// Copyright 2017 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.
-
-package cache
-
-import (
-	"bytes"
-	"context"
-	"errors"
-	"expvar"
-	"sync"
-
-	"cloud.google.com/go/storage"
-	"github.com/golang/groupcache/lru"
-	"go.opencensus.io/trace"
-	"google.golang.org/grpc"
-	"google.golang.org/grpc/codes"
-
-	"go.chromium.org/goma/server/cache/gcs"
-	"go.chromium.org/goma/server/log"
-	cachepb "go.chromium.org/goma/server/proto/cache"
-)
-
-// cache is a wrapper around an *lru.Cache that adds synchronization,
-// and counts the size of all keys and values.
-type memcache struct {
-	MaxBytes   int64
-	mu         sync.RWMutex
-	nbytes     int64 // of all keys and vlaues
-	lru        *lru.Cache
-	nhit, nget int64
-	nevict     int64 // number of evictions
-	nreplace   int64
-}
-
-var errNoChange = errors.New("cache: no change")
-
-type replaceError struct {
-	old []byte
-}
-
-func (r replaceError) Error() string {
-	return "cache: value is replaced"
-}
-
-// Put puts key-value pair in memcache.
-// It returns errNoChange if key-value pair was already stored.
-// It returns replaceError if value is replaced.
-func (c *memcache) Put(ctx context.Context, key string, value []byte) error {
-	span := trace.FromContext(ctx)
-	span.Annotatef(nil, "put %s (size:%d)", key, len(value))
-	c.mu.Lock()
-	defer c.mu.Unlock()
-	err := c.add(ctx, key, value)
-	if c.MaxBytes == 0 {
-		return err
-	}
-	for {
-		if c.nbytes < c.MaxBytes {
-			return err
-		}
-		span.Annotatef(nil, "eviction %d exceeding max=%d", c.nbytes, c.MaxBytes)
-		c.lru.RemoveOldest()
-	}
-}
-
-// add adds key-value pair in memcache.
-// It returns errNoChange if key-value pair already exists in memcache.
-// It returns replaceError if key exists but value differs.
-func (c *memcache) add(ctx context.Context, key string, value []byte) error {
-	logger := log.FromContext(ctx)
-
-	if c.lru == nil {
-		c.lru = &lru.Cache{
-			OnEvicted: func(key lru.Key, value interface{}) {
-				logger := log.FromContext(context.Background())
-				logger.Infof("mem.evict %s %d", key.(string), len(value.([]byte)))
-				c.nbytes -= int64(len(key.(string))) + int64(len(value.([]byte)))
-				c.nevict++
-			},
-		}
-	}
-	var err error
-	vi, ok := c.lru.Get(key)
-	if ok {
-		ov := vi.([]byte)
-		if bytes.Equal(ov, value) {
-			logger.Infof("mem.put2  %s %d", key, len(value))
-			return errNoChange
-		}
-		logger.Errorf("mem.repl  %s %d <= %d", key, len(value), len(ov))
-		// replace won't call OnEvicted.
-		c.nbytes -= int64(len(key)) + int64(len(ov))
-		c.nreplace++
-		err = replaceError{old: ov}
-	} else {
-		logger.Infof("mem.put   %s %d", key, len(value))
-	}
-	c.lru.Add(key, value)
-	c.nbytes += int64(len(key)) + int64(len(value))
-	return err
-}
-
-func (c *memcache) Get(ctx context.Context, key string) (value []byte, ok bool) {
-	span := trace.FromContext(ctx)
-	span.Annotatef(nil, "get %s", key)
-	logger := log.FromContext(ctx)
-
-	c.mu.Lock()
-	defer c.mu.Unlock()
-	c.nget++
-	if c.lru == nil {
-		logger.Infof("mem.miss  %s", key)
-		return nil, false
-	}
-	vi, ok := c.lru.Get(key)
-	if !ok {
-		logger.Infof("mem.miss  %s", key)
-		return nil, false
-	}
-	c.nhit++
-	logger.Infof("mem.hit   %s %d", key, len(vi.([]byte)))
-	return vi.([]byte), true
-}
-
-// TODO: use opencensus stats, view.
-type memstats struct {
-	MaxBytes int64
-
-	Bytes    int64
-	Num      int
-	Hits     int64
-	Gets     int64
-	Evicts   int64
-	Replaces int64
-}
-
-func (c *memcache) stats() memstats {
-	c.mu.RLock()
-	defer c.mu.RUnlock()
-	var num int
-	if c.lru != nil {
-		num = c.lru.Len()
-	}
-	return memstats{
-		MaxBytes: c.MaxBytes,
-		Bytes:    c.nbytes,
-		Num:      num,
-		Hits:     c.nhit,
-		Gets:     c.nget,
-		Evicts:   c.nevict,
-		Replaces: c.nreplace,
-	}
-}
-
-// Config is a configuration for Cache.
-type Config struct {
-	// MaxBytes is maximum number of bytes used for cache.
-	MaxBytes int64
-	// TODO:
-	// Dir          string
-	// MaxDiskBytes int64
-
-	Bucket *storage.BucketHandle
-}
-
-// TODO: put it in Config?
-const writeBackSemaphore = 8
-
-// Cache represents key-value cache.
-type Cache struct {
-	cachepb.UnimplementedCacheServiceServer
-	mem memcache
-	gcs *gcs.Cache
-
-	wbsema chan bool
-}
-
-var (
-	mutex  sync.RWMutex
-	caches []*Cache
-)
-
-// New creates new Cache for Config.
-func New(c Config) (*Cache, error) {
-	cache := &Cache{
-		mem: memcache{
-			MaxBytes: c.MaxBytes,
-		},
-	}
-
-	if c.Bucket != nil {
-		cache.gcs = gcs.New(c.Bucket)
-		cache.wbsema = make(chan bool, writeBackSemaphore)
-	}
-
-	mutex.Lock()
-	caches = append(caches, cache)
-	mutex.Unlock()
-	return cache, nil
-}
-
-// Put puts new key-value pair in memcache (always; i.e. overwrite existing one)
-// and cloud cache (if gcs is configured, and new value is put).
-// It returns error if it fails to put cache in cloud storage.
-func (c *Cache) Put(ctx context.Context, req *cachepb.PutReq) (*cachepb.PutResp, error) {
-	err := c.mem.Put(ctx, req.Kv.Key, req.Kv.Value)
-
-	if err == errNoChange {
-		return &cachepb.PutResp{}, nil
-	}
-	if c.gcs == nil {
-		return &cachepb.PutResp{}, nil
-	}
-	if req.WriteBack {
-		ctx, _ := trace.StartSpanWithRemoteParent(context.Background(), "go.chromium.org/goma/server/cache.Cache.Put.WriteBack", trace.FromContext(ctx).SpanContext())
-		// TODO: pass tag?
-		go func(ctx context.Context) {
-			logger := log.FromContext(ctx)
-			c.wbsema <- true
-			defer func() {
-				<-c.wbsema
-			}()
-			logger.Infof("gcs.put write back %s", req.Kv.Key)
-
-			_, err := c.gcs.Put(ctx, req)
-			if err != nil {
-				logger.Errorf("gcs.put write back %s: %v", req.Kv.Key, err)
-				return
-			}
-			logger.Infof("gcs.put write back %s: OK", req.Kv.Key)
-		}(ctx)
-		return &cachepb.PutResp{}, nil
-	}
-
-	return c.gcs.Put(ctx, req)
-}
-
-// Get gets key-value for requested key.
-// It returns codes.NotFound if value not found in cache.
-func (c *Cache) Get(ctx context.Context, req *cachepb.GetReq) (*cachepb.GetResp, error) {
-	resp := &cachepb.GetResp{}
-	v, ok := c.mem.Get(ctx, req.Key)
-	if ok {
-		resp.Kv = &cachepb.KV{
-			Key:   req.Key,
-			Value: v,
-		}
-		resp.InMemory = true
-		return resp, nil
-	}
-
-	if req.Fast || c.gcs == nil {
-		return nil, grpc.Errorf(codes.NotFound, "cache.Get: not found %s", req.Key)
-	}
-	resp, err := c.gcs.Get(ctx, req)
-	if err != nil || resp.Kv == nil {
-		return nil, grpc.Errorf(codes.NotFound, "cache.Get(%s): %v", req.Key, err)
-	}
-	c.mem.Put(ctx, req.Key, resp.Kv.Value)
-	return resp, nil
-}
-
-type stats struct {
-	Mem memstats
-	GCS gcs.Stats
-}
-
-func (c *Cache) stats() stats {
-	return stats{
-		Mem: c.mem.stats(),
-		GCS: c.gcs.Stats(),
-	}
-}
-
-func publishStats() interface{} {
-	mutex.RLock()
-	defer mutex.RUnlock()
-	var r []stats
-	for _, cache := range caches {
-		r = append(r, cache.stats())
-	}
-	return r
-}
-
-func init() {
-	expvar.Publish("cache", expvar.Func(publishStats))
-}
diff --git a/cache/cache_test.go b/cache/cache_test.go
deleted file mode 100644
index 5bb5038..0000000
--- a/cache/cache_test.go
+++ /dev/null
@@ -1,163 +0,0 @@
-// Copyright 2017 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.
-
-package cache
-
-import (
-	"context"
-	"testing"
-
-	"google.golang.org/grpc/codes"
-	"google.golang.org/grpc/status"
-	"google.golang.org/protobuf/proto"
-
-	pb "go.chromium.org/goma/server/proto/cache"
-)
-
-func TestGetPut(t *testing.T) {
-	ctx := context.Background()
-	cache, err := New(Config{
-		MaxBytes: 1024 * 1024 * 1024,
-	})
-
-	if err != nil {
-		t.Fatalf("cache.New(...): %v", err)
-	}
-
-	key := "key"
-
-	kv := &pb.KV{
-		Key:   key,
-		Value: []byte("value"),
-	}
-
-	cache.Put(ctx, &pb.PutReq{
-		Kv: kv,
-	})
-
-	getReq := &pb.GetReq{
-		Key:  key,
-		Fast: false,
-	}
-
-	gotResp, err := cache.Get(ctx, getReq)
-	if err != nil {
-		t.Errorf("cache.Get(%s): %v", key, err)
-	}
-
-	wantResp := &pb.GetResp{
-		Kv:       kv,
-		InMemory: true,
-	}
-
-	if !proto.Equal(gotResp, wantResp) {
-		t.Errorf("got %#v; want %#v", gotResp, wantResp)
-	}
-
-}
-
-func TestGetNotFound(t *testing.T) {
-	ctx := context.Background()
-	cache, err := New(Config{
-		MaxBytes: 1024 * 1024 * 1024,
-	})
-
-	if err != nil {
-		t.Fatalf("cache.New(...): %v", err)
-	}
-
-	key := "key"
-	getReq := &pb.GetReq{
-		Key:  key,
-		Fast: false,
-	}
-
-	_, err = cache.Get(ctx, getReq)
-	s, ok := status.FromError(err)
-	if !ok || s.Code() != codes.NotFound {
-		t.Errorf("cache.Get(%s): got %v, want NotFound error", key, err)
-	}
-
-}
-
-func TestPutReplace(t *testing.T) {
-	ctx := context.Background()
-	cache, err := New(Config{
-		MaxBytes: 1024 * 1024 * 1024,
-	})
-
-	if err != nil {
-		t.Fatalf("cache.New(...): %v", err)
-	}
-
-	key := "key"
-
-	kv := &pb.KV{
-		Key:   key,
-		Value: nil,
-	}
-
-	cache.Put(ctx, &pb.PutReq{
-		Kv: kv,
-	})
-
-	getReq := &pb.GetReq{
-		Key:  key,
-		Fast: false,
-	}
-
-	gotResp, err := cache.Get(ctx, getReq)
-	if err != nil {
-		t.Errorf("cache.Get(%s): %v", key, err)
-	}
-
-	wantResp := &pb.GetResp{
-		Kv:       kv,
-		InMemory: true,
-	}
-
-	if !proto.Equal(gotResp, wantResp) {
-		t.Errorf("got %#v; want %#v", gotResp, wantResp)
-	}
-
-	if got, want := cache.stats().Mem.Bytes, int64(len(key)); got != want {
-		t.Errorf("Mem.Bytes=%d; want=%d", got, want)
-	}
-
-	t.Logf(`replace value to "value"`)
-
-	kv.Value = []byte("value")
-
-	cache.Put(ctx, &pb.PutReq{
-		Kv: kv,
-	})
-
-	getReq = &pb.GetReq{
-		Key:  key,
-		Fast: false,
-	}
-
-	gotResp, err = cache.Get(ctx, getReq)
-	if err != nil {
-		t.Errorf("cache.Get(%s): %v", key, err)
-	}
-
-	wantResp = &pb.GetResp{
-		Kv:       kv,
-		InMemory: true,
-	}
-
-	if !proto.Equal(gotResp, wantResp) {
-		t.Errorf("got %#v; want %#v", gotResp, wantResp)
-	}
-
-	st := cache.stats().Mem
-	if got, want := st.Bytes, int64(len(key))+int64(len(kv.Value)); got != want {
-		t.Errorf("Mem.Bytes=%d; want=%d", got, want)
-	}
-	if got, want := st.Replaces, int64(1); got != want {
-		t.Errorf("Mem.Replaces=%d; want=%d", got, want)
-	}
-
-}
diff --git a/cache/client.go b/cache/client.go
deleted file mode 100644
index 2f271eb..0000000
--- a/cache/client.go
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright 2017 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.
-
-package cache
-
-import (
-	"context"
-
-	"google.golang.org/grpc"
-
-	"go.chromium.org/goma/server/rpc"
-
-	pb "go.chromium.org/goma/server/proto/cache"
-)
-
-// TODO: opencensus metrics.
-
-// Client is a client to access cache service via gRPC.
-type Client struct {
-	client *rpc.Client
-}
-
-// NewClient creates new client to access cache service serving on address.
-// cache service will be sharded by key.
-func NewClient(ctx context.Context, address string, opts ...grpc.DialOption) Client {
-	return Client{
-		client: rpc.NewClient(ctx, address,
-			func(cc *grpc.ClientConn) interface{} {
-				return pb.NewCacheServiceClient(cc)
-			},
-			rpc.DialOptions(opts...)...),
-	}
-}
-
-// Close releases the resources used by the client.
-func (c Client) Close() error {
-	return c.client.Close()
-}
-
-// Get gets key-value data for requested key.
-func (c Client) Get(ctx context.Context, in *pb.GetReq, opts ...grpc.CallOption) (*pb.GetResp, error) {
-	var resp *pb.GetResp
-	var err error
-	err = c.client.Call(ctx, c.client.Shard, in.Key,
-		func(client interface{}) error {
-			resp, err = client.(pb.CacheServiceClient).Get(ctx, in, opts...)
-			return err
-		})
-	return resp, err
-}
-
-// Put puts new key-value data.
-// If no key-value is given, do nothing.
-func (c Client) Put(ctx context.Context, in *pb.PutReq, opts ...grpc.CallOption) (*pb.PutResp, error) {
-	if in.Kv == nil {
-		return nil, nil
-	}
-	var resp *pb.PutResp
-	var err error
-	err = c.client.Call(ctx, c.client.Shard, in.Kv.Key,
-		func(client interface{}) error {
-			resp, err = client.(pb.CacheServiceClient).Put(ctx, in, opts...)
-			return err
-		})
-	return resp, err
-}
diff --git a/cache/doc.go b/cache/doc.go
deleted file mode 100644
index 29e7ab5..0000000
--- a/cache/doc.go
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright 2017 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.
-
-/*
-Package cache provides cache service.
-*/
-package cache
diff --git a/cache/gcs/cache.go b/cache/gcs/cache.go
deleted file mode 100644
index fcf91e7..0000000
--- a/cache/gcs/cache.go
+++ /dev/null
@@ -1,234 +0,0 @@
-// Copyright 2019 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.
-
-package gcs
-
-import (
-	"bytes"
-	"context"
-	"crypto/md5"
-	"encoding/base64"
-	"encoding/binary"
-	"errors"
-	"fmt"
-	"hash/crc32"
-	"io"
-	"math/rand"
-	"sync/atomic"
-	"time"
-
-	"cloud.google.com/go/storage"
-	"google.golang.org/api/googleapi"
-
-	"go.chromium.org/goma/server/log"
-	pb "go.chromium.org/goma/server/proto/cache"
-)
-
-// AdmissionController checks incoming request.
-type AdmissionController interface {
-	AdmitPut(context.Context, *pb.PutReq) error
-}
-
-type nullAdmissionController struct{}
-
-func (nullAdmissionController) AdmitPut(context.Context, *pb.PutReq) error { return nil }
-
-// Cache represents key-value cache using google cloud storage.
-type Cache struct {
-	pb.UnimplementedCacheServiceServer
-
-	bkt                 *storage.BucketHandle
-	AdmissionController AdmissionController
-	// should be accessed via stomic pkg.
-	nhit, nget int64
-}
-
-// New creates new cache.
-func New(bkt *storage.BucketHandle) *Cache {
-	return &Cache{
-		bkt:                 bkt,
-		AdmissionController: nullAdmissionController{},
-	}
-}
-
-var crc32cTable = crc32.MakeTable(crc32.Castagnoli)
-
-func crc32cStr(s uint32) string {
-	buf := make([]byte, 4)
-	binary.BigEndian.PutUint32(buf, s)
-	return base64.StdEncoding.EncodeToString(buf)
-}
-
-func md5sumStr(b []byte) string {
-	return base64.StdEncoding.EncodeToString(b)
-}
-
-// checkAttrs checks attr matches with value.
-// use hashes for integrity check.
-// https://cloud.google.com/storage/docs/hashes-etags
-func checkAttrs(attr *storage.ObjectAttrs, value []byte) error {
-	if attr.Size != int64(len(value)) {
-		return fmt.Errorf("storage: size: attr:%d != value:%d", attr.Size, len(value))
-	}
-	crc32cSum := crc32.Checksum(value, crc32cTable)
-	if attr.CRC32C != crc32cSum {
-		return fmt.Errorf("storage: crc32: attr:%s != value:%s", crc32cStr(attr.CRC32C), crc32cStr(crc32cSum))
-	}
-	md5sum := md5.Sum(value)
-	if !bytes.Equal(attr.MD5, md5sum[:]) {
-		return fmt.Errorf("storage: md5: attr:%s != value:%s", md5sumStr(attr.MD5), md5sumStr(md5sum[:]))
-	}
-	return nil
-}
-
-func (c *Cache) put(ctx context.Context, obj *storage.ObjectHandle, key string, value []byte, t time.Time) (*pb.PutResp, error) {
-	logger := log.FromContext(ctx)
-	attr, err := obj.Attrs(ctx)
-	if err == nil {
-		err = checkAttrs(attr, value)
-		if err == nil {
-			logger.Infof("gcs.put   %s %d %s: no change gen:%d %d", key, len(value), time.Since(t), attr.Generation, attr.Metageneration)
-			return &pb.PutResp{}, nil
-		}
-		if ctx.Err() != nil {
-			logger.Infof("gcs.put  %s %d %s: %v", key, len(value), time.Since(t), err)
-			return nil, err
-		}
-		// attr mismatch. need overwrite.
-		logger.Errorf("gcs.put   %s %d %s: %v", key, len(value), time.Since(t), err)
-		t = time.Now()
-	}
-	w := obj.NewWriter(ctx)
-	w.CRC32C = crc32.Checksum(value, crc32cTable)
-	w.SendCRC32C = true
-	w.ChunkSize = len(value)
-	if w.ChunkSize > googleapi.DefaultUploadChunkSize {
-		w.ChunkSize = googleapi.DefaultUploadChunkSize
-	}
-
-	if _, err := w.Write(value); err != nil {
-		w.CloseWithError(err)
-		logger.Errorf("gcs.put   %s %d %s: write:%v", key, len(value), time.Since(t), err)
-		return nil, err
-	}
-	if err := w.Close(); err != nil {
-		logger.Errorf("gcs.put   %s %d %s: close:%v", key, len(value), time.Since(t), err)
-		return nil, err
-	}
-	attr = w.Attrs()
-	logger.Infof("gcs.put   %s %d %s crc32c:%s md5:%s gen:%d %d", key, len(value), time.Since(t), crc32cStr(attr.CRC32C), md5sumStr(attr.MD5), attr.Generation, attr.Metageneration)
-	return &pb.PutResp{}, nil
-}
-
-func (c *Cache) Put(ctx context.Context, in *pb.PutReq) (*pb.PutResp, error) {
-	logger := log.FromContext(ctx)
-	if err := c.AdmissionController.AdmitPut(ctx, in); err != nil {
-		logger.Warnf("admission error: %v", err)
-		return nil, err
-	}
-	key := in.Kv.Key
-	value := in.Kv.Value
-	t := time.Now()
-
-	obj := c.bkt.Object(key)
-	for retry := 0; ; retry++ {
-		resp, err := c.put(ctx, obj, key, value, t)
-		if err == nil {
-			return resp, err
-		}
-		var gerr *googleapi.Error
-		if !errors.As(err, &gerr) {
-			return resp, err
-		}
-		if gerr.Code != 429 {
-			return resp, err
-		}
-		// https://cloud.google.com/storage/quotas#objects
-		// an update limit on each object of once per second.
-		// http://b/145956239 gcp rate limit exceeded?
-		backoff := float64(500)
-		for n := retry; n > 0; n-- {
-			backoff *= 1.6
-		}
-		const maxBackoff = 2000.0
-		if backoff > maxBackoff {
-			backoff = maxBackoff
-		}
-		backoff *= 1 + 0.2*(rand.Float64()*2-1)
-		const minBackoff = 50
-		if backoff < minBackoff {
-			backoff = minBackoff
-		}
-		w := time.Duration(backoff) * time.Millisecond
-		logger.Warnf("gcs.put rate limit for %s. backoff %s", key, w)
-		select {
-		case <-time.After(w):
-		case <-ctx.Done():
-			return nil, ctx.Err()
-		}
-	}
-}
-
-func (c *Cache) Get(ctx context.Context, in *pb.GetReq) (*pb.GetResp, error) {
-	logger := log.FromContext(ctx)
-	key := in.Key
-
-	t := time.Now()
-
-	atomic.AddInt64(&c.nget, 1)
-	obj := c.bkt.Object(key)
-	attr, err := obj.Attrs(ctx)
-	if err == storage.ErrObjectNotExist {
-		logger.Infof("gcs.miss  %s %s: %v", key, time.Since(t), err)
-		return nil, err
-	}
-	if err != nil {
-		logger.Errorf("gcs.attrs %s %s: %v", key, time.Since(t), err)
-		return nil, err
-	}
-
-	r, err := obj.NewReader(ctx)
-	if err != nil {
-		logger.Errorf("gcs.miss  %s %s: %v", key, time.Since(t), err)
-		return nil, err
-	}
-	defer r.Close()
-
-	b := make([]byte, attr.Size)
-	_, err = io.ReadFull(r, b)
-	if err != nil {
-		logger.Errorf("gcs.miss  %s %s: %v", key, time.Since(t), err)
-		return nil, err
-	}
-	err = checkAttrs(attr, b)
-	if err != nil {
-		logger.Errorf("gcs.bad   %s %d %s: %v", key, len(b), time.Since(t), err)
-		return nil, fmt.Errorf("key:%s %v", key, err)
-	}
-	atomic.AddInt64(&c.nhit, 1)
-	logger.Infof("gcs.hit   %s %d %s", key, len(b), time.Since(t))
-	return &pb.GetResp{
-		Kv: &pb.KV{
-			Key:   key,
-			Value: b,
-		},
-	}, nil
-}
-
-// Stats represents stats of gcs.Cache.
-// TODO: use opencensus stats, view.
-type Stats struct {
-	Hits int64
-	Gets int64
-}
-
-func (c *Cache) Stats() Stats {
-	if c == nil {
-		return Stats{}
-	}
-	return Stats{
-		Hits: atomic.LoadInt64(&c.nhit),
-		Gets: atomic.LoadInt64(&c.nget),
-	}
-}
diff --git a/cache/gcs/cache_test.go b/cache/gcs/cache_test.go
deleted file mode 100644
index f3f41fb..0000000
--- a/cache/gcs/cache_test.go
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright 2019 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.
-
-package gcs
-
-import (
-	"crypto/md5"
-	"hash/crc32"
-	"testing"
-)
-
-// hash value was retrieved from
-//  gsutil ls -L gs://goma-dev-temp/empty
-//  gsutil ls -L gs://goma-dev-temp/hello.c
-
-func TestHash(t *testing.T) {
-	for _, tc := range []struct {
-		desc   string
-		in     string
-		crc32c string
-		md5    string
-	}{
-		{
-			desc:   "empty",
-			in:     "",
-			crc32c: "AAAAAA==",
-			md5:    "1B2M2Y8AsgTpgAmY7PhCfg==",
-		},
-		{
-			desc: "hello.c",
-			in: `#include <stdio.h>
-
-int i = 1;
-
-int main(int argc, char *argv[]) {
-	 printf("hello, world\n");
-	 return 0;
-}
-`,
-			crc32c: "r+rffw==",
-			md5:    "Q9yWleHH0r5N2AYXTIBHQw==",
-		},
-	} {
-		got := crc32cStr(crc32.Checksum([]byte(tc.in), crc32cTable))
-		if got != tc.crc32c {
-			t.Errorf("%s: crc32c: %s; want %s", tc.desc, tc.in, tc.crc32c)
-		}
-		md5sum := md5.Sum([]byte(tc.in))
-		got = md5sumStr(md5sum[:])
-		if got != tc.md5 {
-			t.Errorf("%s: md5: %s; want %s", tc.desc, tc.in, tc.md5)
-		}
-	}
-}
diff --git a/cache/gcs/doc.go b/cache/gcs/doc.go
deleted file mode 100644
index 2e151d1..0000000
--- a/cache/gcs/doc.go
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright 2019 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.
-
-/*
-Package gcs provides cache service by google cloud storage.
-*/
-package gcs
diff --git a/cache/local_client.go b/cache/local_client.go
deleted file mode 100644
index 3d802b2..0000000
--- a/cache/local_client.go
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2019 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.
-
-package cache
-
-import (
-	"context"
-
-	"google.golang.org/grpc"
-
-	pb "go.chromium.org/goma/server/proto/cache"
-)
-
-// LocalClient is an adaptor to make CacheServiceServer as CacheServiceClient,
-// ignoring any grpc.CallOption.
-// TODO: use localhost server? https://github.com/grpc/grpc-go/issues/520
-type LocalClient struct {
-	pb.CacheServiceServer
-}
-
-func (c LocalClient) Get(ctx context.Context, in *pb.GetReq, opts ...grpc.CallOption) (*pb.GetResp, error) {
-	return c.CacheServiceServer.Get(ctx, in)
-}
-
-func (c LocalClient) Put(ctx context.Context, in *pb.PutReq, opts ...grpc.CallOption) (*pb.PutResp, error) {
-	return c.CacheServiceServer.Put(ctx, in)
-}
diff --git a/cache/redis/client.go b/cache/redis/client.go
deleted file mode 100644
index 7b65a4b..0000000
--- a/cache/redis/client.go
+++ /dev/null
@@ -1,230 +0,0 @@
-// Copyright 2019 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.
-
-package redis
-
-import (
-	"context"
-	"errors"
-	"fmt"
-	"net"
-	"os"
-	"syscall"
-	"time"
-
-	"github.com/gomodule/redigo/redis"
-	"google.golang.org/grpc"
-	"google.golang.org/grpc/codes"
-	"google.golang.org/grpc/status"
-
-	"go.chromium.org/goma/server/log"
-	pb "go.chromium.org/goma/server/proto/cache"
-	"go.chromium.org/goma/server/rpc"
-)
-
-// Client is cache service client for redis.
-type Client struct {
-	prefix string
-	pool   *redis.Pool
-
-	// to workaround pool.wait. maintain active conns.
-	sema chan struct{}
-	ttl  time.Duration
-}
-
-// AddrFromEnv returns redis server address from environment variables.
-func AddrFromEnv() (string, error) {
-	host := os.Getenv("REDISHOST")
-	port := os.Getenv("REDISPORT")
-	if host == "" {
-		return "", errors.New("no REDISHOST environment")
-	}
-	if port == "" {
-		port = "6379" // redis default port
-	}
-	return fmt.Sprintf("%s:%s", host, port), nil
-}
-
-// Opts is redis client option.
-type Opts struct {
-	// Prefix is key prefix used by the client.
-	Prefix string
-
-	// MaxIdleConns is max number of idle connections.
-	MaxIdleConns int
-
-	// MaxActiveConns is max number of active connections.
-	MaxActiveConns int
-
-	// EntryTTL sets the expiration time of an entry, 0 means entry will never expire.
-	EntryTTL time.Duration
-}
-
-// default max number of connections.
-// note: in GCP, redis quota is 65,000
-const (
-	DefaultMaxIdleConns   = 50
-	DefaultMaxActiveConns = 200
-)
-
-// NewClient creates new cache client for redis.
-func NewClient(ctx context.Context, addr string, opts Opts) Client {
-	return Client{
-		prefix: opts.Prefix,
-		pool: &redis.Pool{
-			DialContext: func(ctx context.Context) (redis.Conn, error) {
-				return redis.DialContext(ctx, "tcp", addr)
-			},
-			MaxIdle:   opts.MaxIdleConns,
-			MaxActive: opts.MaxActiveConns,
-			// https://github.com/gomodule/redigo/issues/520
-			Wait: false,
-		},
-		sema: make(chan struct{}, opts.MaxActiveConns),
-		ttl:  opts.EntryTTL,
-	}
-}
-
-// Close releases the resources used by the client.
-func (c Client) Close() error {
-	return c.pool.Close()
-}
-
-type temporary interface {
-	Temporary() bool
-}
-
-func isConnError(err error) bool {
-	errno, ok := err.(syscall.Errno)
-	if !ok {
-		serr, ok := err.(*os.SyscallError)
-		if !ok {
-			return false
-		}
-		errno, ok = serr.Err.(syscall.Errno)
-		if !ok {
-			return false
-		}
-	}
-	return errno == syscall.ECONNRESET || errno == syscall.ECONNABORTED
-}
-
-// retryErr converts err to rpc.RetriableError if it is retriable error.
-func retryErr(err error) error {
-	if errors.Is(err, redis.ErrNil) {
-		return status.Error(codes.NotFound, err.Error())
-	}
-	// retriable if temporary error.
-	if terr, ok := err.(temporary); ok && terr.Temporary() {
-		return rpc.RetriableError{
-			Err: err,
-		}
-	}
-	// redis might return net.OpError as is.
-	operr, ok := err.(*net.OpError)
-	if !ok {
-		return err
-	}
-	// retry if it is ECONNRESET or ECONNABORTED.
-	if isConnError(operr.Err) {
-		return rpc.RetriableError{
-			Err: err,
-		}
-	}
-	return err
-}
-
-type activeConn struct {
-	redis.Conn
-	c Client
-}
-
-func (c activeConn) Close() error {
-	<-c.c.sema
-	return c.Conn.Close()
-}
-
-func (c Client) poolGetContext(ctx context.Context) (redis.Conn, error) {
-	t := time.Now()
-	select {
-	case c.sema <- struct{}{}:
-		d := time.Since(t)
-		if d > 100*time.Millisecond {
-			logger := log.FromContext(ctx)
-			logger.Warnf("redis pool wait %s actives=%d", d, len(c.sema))
-		}
-		conn, err := c.pool.GetContext(ctx)
-		if err != nil {
-			<-c.sema
-			return nil, err
-		}
-		return activeConn{
-			Conn: conn,
-			c:    c,
-		}, nil
-	case <-ctx.Done():
-		d := time.Since(t)
-		if d > 100*time.Millisecond {
-			logger := log.FromContext(ctx)
-			logger.Warnf("redis pool timed-out wait %s actives=%d", d, len(c.sema))
-		}
-		return nil, ctx.Err()
-	}
-}
-
-// Get fetches value for the key from redis.
-func (c Client) Get(ctx context.Context, in *pb.GetReq, opts ...grpc.CallOption) (*pb.GetResp, error) {
-	conn, err := c.poolGetContext(ctx)
-	if err != nil {
-		return nil, err
-	}
-	defer conn.Close()
-	var v []byte
-	err = rpc.Retry{
-		MaxRetry: -1,
-	}.Do(ctx, func() error {
-		ttlMs := c.ttl.Milliseconds()
-		if ttlMs > 0 {
-			v, err = redis.Bytes(conn.Do("GETEX", c.prefix+in.Key, "PX", ttlMs))
-		} else {
-			v, err = redis.Bytes(conn.Do("GET", c.prefix+in.Key))
-		}
-		return retryErr(err)
-	})
-	if err != nil {
-		return nil, err
-	}
-	return &pb.GetResp{
-		Kv: &pb.KV{
-			Key:   in.Key,
-			Value: v,
-		},
-		InMemory: true,
-	}, nil
-}
-
-// Put stores key:value pair on redis.
-func (c Client) Put(ctx context.Context, in *pb.PutReq, opts ...grpc.CallOption) (*pb.PutResp, error) {
-	conn, err := c.poolGetContext(ctx)
-	if err != nil {
-		return nil, err
-	}
-	defer conn.Close()
-	err = rpc.Retry{
-		MaxRetry: -1,
-	}.Do(ctx, func() error {
-		args := redis.Args{}.Add(c.prefix+in.Kv.Key, in.Kv.Value)
-		ttlMs := c.ttl.Milliseconds()
-		if ttlMs > 0 {
-			args = args.Add("PX", ttlMs)
-		}
-		_, err := conn.Do("SET", args...)
-
-		return retryErr(err)
-	})
-	if err != nil {
-		return nil, err
-	}
-	return &pb.PutResp{}, nil
-}
diff --git a/cache/redis/client_test.go b/cache/redis/client_test.go
deleted file mode 100644
index 256cd5e..0000000
--- a/cache/redis/client_test.go
+++ /dev/null
@@ -1,145 +0,0 @@
-// Copyright 2020 Google LLC. All Rights Reserved.
-
-package redis
-
-import (
-	"context"
-	"flag"
-	"strconv"
-	"sync"
-	"testing"
-	"time"
-
-	"github.com/google/go-cmp/cmp"
-	"go.uber.org/zap"
-
-	"go.chromium.org/goma/server/log"
-	pb "go.chromium.org/goma/server/proto/cache"
-)
-
-var (
-	numFilesPerExecReq = flag.Int("num_files_per_exec_req", 500, "number of files per ExecReq for BenchmarkGet")
-)
-
-func BenchmarkGet(b *testing.B) {
-	log.SetZapLogger(zap.NewNop())
-	s := NewFakeServer(b)
-
-	ctx := context.Background()
-	c := NewClient(ctx, s.Addr().String(), Opts{
-		MaxIdleConns:   DefaultMaxIdleConns,
-		MaxActiveConns: DefaultMaxActiveConns,
-	})
-	defer c.Close()
-
-	b.Logf("b.N=%d", b.N)
-	var wg sync.WaitGroup
-	var (
-		mu    sync.Mutex
-		nerrs int
-	)
-	wg.Add(b.N)
-	b.ResetTimer()
-	for i := 0; i < b.N; i++ {
-		go func() {
-			defer wg.Done()
-			ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
-			defer cancel()
-
-			var rg sync.WaitGroup
-			rg.Add(*numFilesPerExecReq)
-			for i := 0; i < *numFilesPerExecReq; i++ {
-				go func() {
-					defer rg.Done()
-					_, err := c.Get(ctx, &pb.GetReq{
-						Key: "key",
-					})
-					if err != nil {
-						mu.Lock()
-						nerrs++
-						mu.Unlock()
-					}
-				}()
-			}
-			rg.Wait()
-		}()
-	}
-	wg.Wait()
-	mu.Lock()
-	b.Logf("nerrs=%d", nerrs)
-	mu.Unlock()
-}
-
-func TestSetNonZeroTTL(t *testing.T) {
-	expectedKey := "test_key"
-	expectedValue := "test_value"
-	expectedTTL := 5 * time.Millisecond
-
-	log.SetZapLogger(zap.NewNop())
-	s := NewFakeServer(t)
-
-	ctx := context.Background()
-	c := NewClient(ctx, s.Addr().String(), Opts{
-		MaxIdleConns:   DefaultMaxIdleConns,
-		MaxActiveConns: DefaultMaxActiveConns,
-		EntryTTL:       expectedTTL,
-	})
-	defer func() {
-		if err := c.Close(); err != nil {
-			t.Fatal(err)
-		}
-	}()
-
-	_, err := c.Put(ctx, &pb.PutReq{
-		Kv: &pb.KV{
-			Key:   expectedKey,
-			Value: []byte(expectedValue),
-		},
-	})
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	want := []string{"SET", expectedKey, expectedValue, "PX", strconv.FormatInt(expectedTTL.Milliseconds(), 10)}
-	got := s.lastRequest()
-	if diff := cmp.Diff(want, got); diff != "" {
-		t.Errorf("lastRequest() mismatch (-want +got):\n%s", diff)
-	}
-}
-
-func TestSetZeroTTL(t *testing.T) {
-	expectedKey := "test_key"
-	expectedValue := "test_value"
-	expectedTTL := 0 * time.Millisecond
-
-	log.SetZapLogger(zap.NewNop())
-	s := NewFakeServer(t)
-
-	ctx := context.Background()
-	c := NewClient(ctx, s.Addr().String(), Opts{
-		MaxIdleConns:   DefaultMaxIdleConns,
-		MaxActiveConns: DefaultMaxActiveConns,
-		EntryTTL:       expectedTTL,
-	})
-	defer func() {
-		if err := c.Close(); err != nil {
-			t.Fatal(err)
-		}
-	}()
-
-	_, err := c.Put(ctx, &pb.PutReq{
-		Kv: &pb.KV{
-			Key:   expectedKey,
-			Value: []byte(expectedValue),
-		},
-	})
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	want := []string{"SET", expectedKey, expectedValue}
-	got := s.lastRequest()
-	if diff := cmp.Diff(want, got); diff != "" {
-		t.Errorf("lastRequest() mismatch (-want +got):\n%s", diff)
-	}
-}
diff --git a/cache/redis/doc.go b/cache/redis/doc.go
deleted file mode 100644
index f23cea9..0000000
--- a/cache/redis/doc.go
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright 2019 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.
-
-/*
-Package redis provides cache service by redis (cloud memorystore).
-*/
-package redis
diff --git a/cache/redis/fake_redis.go b/cache/redis/fake_redis.go
deleted file mode 100644
index 23aff40..0000000
--- a/cache/redis/fake_redis.go
+++ /dev/null
@@ -1,120 +0,0 @@
-// Copyright 2020 Google LLC. All Rights Reserved.
-
-package redis
-
-import (
-	"bufio"
-	"bytes"
-	"fmt"
-	"net"
-	"strconv"
-	"testing"
-)
-
-// FakeServer is a fake redis server for stress test.
-type FakeServer struct {
-	ln   net.Listener
-	tb   testing.TB
-	last []string
-}
-
-// NewFakeServer starts a new fake redis server.
-func NewFakeServer(tb testing.TB) *FakeServer {
-	ln, err := net.Listen("tcp", "")
-	if err != nil {
-		tb.Fatal(err)
-	}
-	s := &FakeServer{ln: ln, tb: tb}
-	go s.serve()
-	tb.Cleanup(func() { s.Close() })
-	return s
-}
-
-// Addr returns address of the fake redis server.
-func (s *FakeServer) Addr() net.Addr {
-	return s.ln.Addr()
-}
-
-// Close shuts down the fake redis server.
-func (s *FakeServer) Close() {
-	s.ln.Close()
-}
-
-func (s *FakeServer) serve() {
-	for {
-		conn, err := s.ln.Accept()
-		if err != nil {
-			return
-		}
-		go s.handle(conn)
-	}
-}
-
-func (s *FakeServer) handle(conn net.Conn) {
-	defer conn.Close()
-	b := bufio.NewReader(conn)
-	for {
-		request, err := s.readRequest(b)
-		if err != nil {
-			return
-		}
-		s.last = request
-		s.tb.Logf("request: %q", request)
-
-		if len(request) > 0 && request[0] == "SET" {
-			conn.Write([]byte("+OK\r\n"))
-		} else {
-			// assume GET
-			conn.Write([]byte("$10\r\n0123456789\r\n"))
-		}
-	}
-}
-
-func (s *FakeServer) readRequest(r *bufio.Reader) ([]string, error) {
-	var line []byte
-	nline, _, err := r.ReadLine()
-	if err != nil {
-		return nil, err
-	}
-	line = append(line, nline...)
-	if !bytes.HasPrefix(nline, []byte("*")) {
-		return nil, err
-	}
-	// *<n> array
-	n, err := strconv.Atoi(string(nline[1:]))
-	if err != nil {
-		return nil, fmt.Errorf("wrong array %q: %v", nline, err)
-	}
-	var request []string
-	for i := 0; i < n; i++ {
-		nline, _, err := r.ReadLine()
-		if err != nil {
-			return nil, err
-		}
-		line = append(line, '\n')
-		line = append(line, nline...)
-		if !bytes.HasPrefix(nline, []byte("$")) {
-			continue
-		}
-		// $<n>\r\n<value>\r\n
-		sz, err := strconv.Atoi(string(nline[1:]))
-		if err != nil {
-			return nil, fmt.Errorf("wrong bytes %q: %v", nline, err)
-		}
-		nline, _, err = r.ReadLine()
-		if err != nil {
-			return nil, err
-		}
-		line = append(line, '\n')
-		line = append(line, nline...)
-		if sz != len(nline) {
-			return nil, fmt.Errorf("unexpected value sz=%d v=%q", sz, nline)
-		}
-		request = append(request, string(nline))
-	}
-	return request, nil
-}
-
-func (s *FakeServer) lastRequest() []string {
-	return s.last
-}
diff --git a/cipd_manifest.txt b/cipd_manifest.txt
deleted file mode 100644
index 736db32..0000000
--- a/cipd_manifest.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-# Copyright 2019 Google Inc. All Rights Reserved.
-
-# Pin resolved versions in the repo, to reduce trust in the CIPD backend.
-#
-# To regenerate them (after modifying this file):
-#   cipd ensure-file-resolve -ensure-file cipd_manifest.txt
-$ResolvedVersions cipd_manifest.versions
-
-# Fully supported plaforms.
-$VerifiedPlatform linux-amd64
-
-# You can check available cipd package in:
-# https://chrome-infra-packages.appspot.com/
-
-# go
-infra/3pp/tools/go/${platform} version:2@1.21.6
-
-# protoc
-infra/3pp/tools/protoc/${platform} version:2@24.2
diff --git a/cipd_manifest.versions b/cipd_manifest.versions
deleted file mode 100644
index 8aee50b..0000000
--- a/cipd_manifest.versions
+++ /dev/null
@@ -1,10 +0,0 @@
-# This file is auto-generated by 'cipd ensure-file-resolve'.
-# Do not modify manually. All changes will be overwritten.
-
-infra/3pp/tools/go/linux-amd64
-	version:2@1.21.6
-	nTT9tex9aFGU2QvIgJFs8dlu0TeclC3G0xGVw9OAtC8C
-
-infra/3pp/tools/protoc/linux-amd64
-	version:2@24.2
-	9p1eUW0T-juc1nEy08VsAFhqoOMtYPj5d2Nf13hw3P4C
diff --git a/cmd/auth_server/main.go b/cmd/auth_server/main.go
deleted file mode 100644
index 73092fe..0000000
--- a/cmd/auth_server/main.go
+++ /dev/null
@@ -1,253 +0,0 @@
-// Copyright 2017 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.
-
-/*
-Binary auth_server provides auth service via gRPC.
-*/
-package main
-
-import (
-	"context"
-	"crypto/tls"
-	"flag"
-	"net/http"
-	"path/filepath"
-
-	rpb "github.com/bazelbuild/remote-apis/build/bazel/remote/execution/v2"
-
-	"go.opencensus.io/plugin/ocgrpc"
-	"go.opencensus.io/stats"
-	"go.opencensus.io/stats/view"
-	"go.opencensus.io/tag"
-	"go.opencensus.io/trace"
-	"go.opencensus.io/zpages"
-	"golang.org/x/oauth2"
-	"google.golang.org/grpc"
-	"google.golang.org/grpc/credentials"
-	"google.golang.org/grpc/credentials/oauth"
-
-	"go.chromium.org/goma/server/auth"
-	"go.chromium.org/goma/server/auth/account"
-	"go.chromium.org/goma/server/auth/acl"
-	"go.chromium.org/goma/server/auth/authdb"
-	"go.chromium.org/goma/server/fswatch"
-	"go.chromium.org/goma/server/httprpc"
-	"go.chromium.org/goma/server/log"
-	"go.chromium.org/goma/server/log/errorreporter"
-	"go.chromium.org/goma/server/profiler"
-	pb "go.chromium.org/goma/server/proto/auth"
-	"go.chromium.org/goma/server/remoteexec"
-	"go.chromium.org/goma/server/remoteexec/digest"
-	"go.chromium.org/goma/server/rpc"
-	"go.chromium.org/goma/server/server"
-)
-
-var (
-	port  = flag.Int("port", 5050, "rpc port")
-	mport = flag.Int("mport", 8081, "monitor port")
-
-	projectID = flag.String("project-id", "", "project id")
-
-	authDBAddr            = flag.String("auth-db-addr", "", "authdb url")
-	aclFile               = flag.String("acl-file", "", "filename of acl proto text message")
-	serviceAccountJSONDir = flag.String("service-account-json-dir", "", "directory for service account jsons")
-
-	remoteexecAddr     = flag.String("remoteexec-addr", "", "use remoteexec API endpoint")
-	remoteInstanceName = flag.String("remote-instance-name", "", "remote instance name.")
-)
-
-var (
-	configUpdate = stats.Int64("go.chromium.org/goma/server/cmd/auth_server.acl-updates", "acl updates", stats.UnitDimensionless)
-
-	configStatusKey = tag.MustNewKey("status")
-
-	configViews = []*view.View{
-		{
-			Description: "counts acl updates",
-			TagKeys: []tag.Key{
-				configStatusKey,
-			},
-			Measure:     configUpdate,
-			Aggregation: view.Count(),
-		},
-	}
-)
-
-func recordConfigUpdate(ctx context.Context, err error) {
-	logger := log.FromContext(ctx)
-	status := "success"
-	if err != nil {
-		status = "failure"
-	}
-	ctx, cerr := tag.New(ctx, tag.Upsert(configStatusKey, status))
-	if cerr != nil {
-		logger.Fatal(cerr)
-	}
-	stats.Record(ctx, configUpdate.M(1))
-	if err != nil {
-		server.Flush()
-	}
-}
-
-type tokenChecker struct {
-	Client   remoteexec.Client
-	Instance string
-}
-
-func (tc *tokenChecker) CheckToken(ctx context.Context, token *oauth2.Token, tokenInfo *auth.TokenInfo) (string, *oauth2.Token, error) {
-	d := digest.Bytes("auth check", []byte("auth check"))
-	err := rpc.Retry{}.Do(ctx, func() error {
-		_, err := tc.Client.CAS().FindMissingBlobs(ctx, &rpb.FindMissingBlobsRequest{
-			InstanceName: tc.Instance,
-			BlobDigests: []*rpb.Digest{
-				d.Digest(),
-			},
-		}, grpc.PerRPCCredentials(oauth.NewOauthAccess(token)))
-		return err
-	})
-	if err != nil {
-		return "", nil, err
-	}
-	return "", token, nil
-}
-
-func main() {
-	flag.Parse()
-
-	ctx := context.Background()
-
-	profiler.Setup(ctx)
-
-	logger := log.FromContext(ctx)
-	defer logger.Sync()
-
-	err := server.Init(ctx, *projectID, "auth_server")
-	if err != nil {
-		logger.Fatal(err)
-	}
-
-	err = view.Register(configViews...)
-	if err != nil {
-		logger.Fatal(err)
-	}
-	trace.ApplyConfig(trace.Config{
-		DefaultSampler: server.NewLimitedSampler(server.DefaultTraceFraction, server.DefaultTraceQPS),
-	})
-
-	s, err := server.NewGRPC(*port)
-	if err != nil {
-		logger.Fatal(err)
-	}
-	var checkToken func(context.Context, *oauth2.Token, *auth.TokenInfo) (string, *oauth2.Token, error)
-	if *remoteexecAddr != "" {
-		logger.Infof("use remoteexec API: %s", *remoteexecAddr)
-		reConn, err := grpc.DialContext(ctx, *remoteexecAddr,
-			grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{})),
-			grpc.WithStatsHandler(&ocgrpc.ClientHandler{}))
-		if err != nil {
-			logger.Fatalf("dial %s: %v", *remoteexecAddr, err)
-		}
-		defer reConn.Close()
-		if *remoteInstanceName == "" {
-			logger.Fatalf("--remote-instance-name must be given for remoteexec API")
-		}
-		tc := &tokenChecker{
-			Client: remoteexec.Client{
-				ClientConn: reConn,
-			},
-			Instance: *remoteInstanceName,
-		}
-		checkToken = tc.CheckToken
-	}
-
-	if *aclFile != "" {
-		var authDB acl.AuthDB
-		if *authDBAddr != "" {
-			authDB = authdb.Client{
-				Client: &httprpc.Client{
-					URL: *authDBAddr,
-				},
-			}
-			logger.Infof("use authdb: %s", *authDBAddr)
-		}
-		a := acl.ACL{
-			Loader: acl.FileLoader{
-				Filename: *aclFile,
-			},
-			Checker: acl.Checker{
-				AuthDB: authDB,
-				Pool: account.JSONDir{
-					Dir: *serviceAccountJSONDir,
-					Scopes: []string{
-						"https://www.googleapis.com/auth/cloud-build-service",
-					},
-				},
-			},
-		}
-		err := a.Update(ctx)
-		if err != nil {
-			recordConfigUpdate(ctx, err)
-			logger.Fatalf("acl update failed: %v", err)
-		}
-		recordConfigUpdate(ctx, nil)
-		go func() {
-			defer errorreporter.Do(nil, nil)
-			ctx := context.Background()
-			logger := log.FromContext(ctx)
-			watcher, err := fswatch.New(ctx, filepath.Dir(*aclFile))
-			if err != nil {
-				logger.Fatalf("fswatch failed: %v", err)
-			}
-			defer watcher.Close()
-			for {
-				logger.Infof("waiting for acl update...")
-				ev, err := watcher.Next(ctx)
-				if err != nil {
-					logger.Fatalf("watch failed: %v", err)
-				}
-				logger.Infof("acl update: %v", ev)
-				err = a.Update(ctx)
-				if err != nil {
-					recordConfigUpdate(ctx, err)
-					logger.Errorf("acl update failed: %v", err)
-					continue
-				}
-				logger.Infof("acl updated")
-				recordConfigUpdate(ctx, nil)
-			}
-		}()
-		rbeCheckToken := checkToken
-		checkToken = func(ctx context.Context, token *oauth2.Token, tokenInfo *auth.TokenInfo) (string, *oauth2.Token, error) {
-			account, token, err := a.CheckToken(ctx, token, tokenInfo)
-			if err != nil {
-				return account, nil, err
-			}
-			if rbeCheckToken != nil {
-				_, token, err = rbeCheckToken(ctx, token, tokenInfo)
-				return account, token, err
-			}
-			return account, token, nil
-		}
-		logger.Infof("acl configured")
-	}
-
-	if checkToken == nil {
-		var a acl.ACL
-		err := a.Update(ctx)
-		if err != nil {
-			logger.Fatalf("acl update failed: %v", err)
-		}
-		checkToken = a.CheckToken
-	}
-
-	as := &auth.Service{
-		CheckToken: checkToken,
-	}
-	pb.RegisterAuthServiceServer(s.Server, as)
-
-	hs := server.NewHTTP(*mport, nil)
-
-	zpages.Handle(http.DefaultServeMux, "/debug")
-	server.Run(ctx, s, hs)
-}
diff --git a/cmd/cache_server/client.go b/cmd/cache_server/client.go
deleted file mode 100644
index 7942688..0000000
--- a/cmd/cache_server/client.go
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright 2017 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.
-//go:build ignore
-// +build ignore
-
-// client.go is sample client of cache server.
-//
-//	$ go run client.go 'host:port' get 'key'
-//	$ go run client.go 'host:port' put 'key' < value
-package main
-
-import (
-	"context"
-	"fmt"
-	"io/ioutil"
-	"log"
-	"os"
-
-	"go.opencensus.io/plugin/ocgrpc"
-	"go.opencensus.io/trace"
-	"google.golang.org/grpc"
-
-	"go.chromium.org/goma/server/cache"
-	pb "go.chromium.org/goma/server/proto/cache"
-)
-
-func main() {
-	if len(os.Args) < 4 {
-		fmt.Fprintf(os.Stderr, "usage: go run client.go 'target' [get|put] 'key'\n")
-		os.Exit(1)
-	}
-
-	trace.SetDefaultSampler(trace.AlwaysSample())
-
-	target := os.Args[1]
-	client := cache.NewClient(target, grpc.WithInsecure(), grpc.WithStatsHandler(&ocgrpc.ClientHandler{}))
-	cmd := os.Args[2]
-	key := os.Args[3]
-	ctx := context.Background()
-	switch cmd {
-	case "get":
-		resp, err := client.Get(ctx, &pb.GetReq{
-			Key: key,
-		})
-		if err != nil {
-			log.Fatalf("get error: %v", err)
-		}
-		if len(resp.Kv.Value) == 0 {
-			os.Exit(0)
-		}
-		_, err = os.Stdout.Write(resp.Kv.Value)
-		if err != nil {
-			log.Fatalf("write failed: %v", err)
-		}
-	case "put":
-		value, err := ioutil.ReadAll(os.Stdin)
-		if err != nil {
-			log.Fatalf("read failed: %v", err)
-		}
-		_, err = client.Put(ctx, &pb.PutReq{
-			Kv: &pb.KV{
-				Key:   key,
-				Value: value,
-			},
-		})
-		if err != nil {
-			log.Fatalf("put error: %v", err)
-		}
-	}
-}
diff --git a/cmd/cache_server/main.go b/cmd/cache_server/main.go
deleted file mode 100644
index f363a4e..0000000
--- a/cmd/cache_server/main.go
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright 2017 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.
-
-/*
-Binary cache_server provides cache service via gRPC.
-*/
-package main
-
-import (
-	"context"
-	"flag"
-	"runtime/debug"
-
-	"cloud.google.com/go/storage"
-	"go.opencensus.io/zpages"
-	"google.golang.org/api/option"
-
-	"go.chromium.org/goma/server/cache"
-	"go.chromium.org/goma/server/log"
-	"go.chromium.org/goma/server/profiler"
-	pb "go.chromium.org/goma/server/proto/cache"
-	"go.chromium.org/goma/server/server"
-
-	_ "expvar"
-	"net/http"
-	_ "net/http/pprof"
-)
-
-var (
-	port               = flag.Int("port", 5050, "rpc port")
-	mport              = flag.Int("mport", 8081, "monitor port")
-	bucket             = flag.String("bucket", "", "backing store bucket")
-	serviceAccountFile = flag.String("service-account-file", "", "service account json file")
-	// config = flag.String("config", "", "config file")
-
-	traceProjectID = flag.String("trace-project-id", "", "project id for cloud tracing")
-)
-
-func main() {
-	flag.Parse()
-
-	ctx := context.Background()
-	// Set low GC percent for better memory usage.
-	debug.SetGCPercent(30)
-	profiler.Setup(ctx)
-
-	logger := log.FromContext(ctx)
-	defer logger.Sync()
-
-	err := server.Init(ctx, *traceProjectID, "cache_server")
-	if err != nil {
-		logger.Fatal(err)
-	}
-
-	var bucketHandle *storage.BucketHandle
-	if *bucket != "" {
-		var opts []option.ClientOption
-		if *serviceAccountFile != "" {
-			opts = append(opts, option.WithServiceAccountFile(*serviceAccountFile))
-		}
-		gsclient, err := storage.NewClient(ctx, opts...)
-		if err != nil {
-			logger.Fatalf("storage client failed: %v", err)
-		}
-		defer gsclient.Close()
-		bucketHandle = gsclient.Bucket(*bucket)
-	}
-
-	s, err := server.NewGRPC(*port)
-	if err != nil {
-		logger.Fatal(err)
-	}
-	c, err := cache.New(cache.Config{
-		MaxBytes: 1 * 1024 * 1024 * 1024,
-		Bucket:   bucketHandle,
-	})
-	if err != nil {
-		logger.Fatalf("failed to create cache client: %v", err)
-	}
-	pb.RegisterCacheServiceServer(s.Server, c)
-
-	hs := server.NewHTTP(*mport, nil)
-	zpages.Handle(http.DefaultServeMux, "/debug")
-	server.Run(ctx, s, hs)
-}
diff --git a/cmd/exec_server/main.go b/cmd/exec_server/main.go
deleted file mode 100644
index b937be9..0000000
--- a/cmd/exec_server/main.go
+++ /dev/null
@@ -1,575 +0,0 @@
-// Copyright 2017 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.
-
-/*
-Binary exec_server provides goma exec service via gRPC.
-*/
-package main
-
-import (
-	"context"
-	"crypto/tls"
-	"errors"
-	"flag"
-	"fmt"
-	"io"
-	"math/rand"
-	"net/http"
-	"path"
-	"strings"
-	"time"
-
-	rpb "github.com/bazelbuild/remote-apis/build/bazel/remote/execution/v2"
-
-	"cloud.google.com/go/pubsub"
-	"cloud.google.com/go/storage"
-	gax "github.com/googleapis/gax-go/v2"
-	"github.com/googleapis/google-cloud-go-testing/storage/stiface"
-	"go.opencensus.io/plugin/ocgrpc"
-	"go.opencensus.io/stats"
-	"go.opencensus.io/stats/view"
-	"go.opencensus.io/tag"
-	"go.opencensus.io/trace"
-	"go.opencensus.io/zpages"
-	"google.golang.org/api/option"
-	bspb "google.golang.org/genproto/googleapis/bytestream"
-	"google.golang.org/grpc"
-	"google.golang.org/grpc/credentials"
-	"google.golang.org/protobuf/encoding/prototext"
-
-	"go.chromium.org/goma/server/cache/redis"
-	"go.chromium.org/goma/server/command"
-	"go.chromium.org/goma/server/exec"
-	"go.chromium.org/goma/server/file"
-	"go.chromium.org/goma/server/log"
-	"go.chromium.org/goma/server/log/errorreporter"
-	"go.chromium.org/goma/server/profiler"
-	cmdpb "go.chromium.org/goma/server/proto/command"
-	pb "go.chromium.org/goma/server/proto/exec"
-	filepb "go.chromium.org/goma/server/proto/file"
-	"go.chromium.org/goma/server/remoteexec"
-	"go.chromium.org/goma/server/remoteexec/digest"
-	"go.chromium.org/goma/server/rpc"
-	"go.chromium.org/goma/server/server"
-)
-
-var (
-	port                  = flag.Int("port", 5050, "rpc port")
-	mport                 = flag.Int("mport", 8081, "monitor port")
-	fileAddr              = flag.String("file-addr", "passthrough:///file-server:5050", "file server address")
-	configMapURI          = flag.String("configmap_uri", "", "deprecated: configmap uri. e.g. gs://$project-toolchain-config/$name.config, text proto of command.ConfigMap.")
-	configMap             = flag.String("configmap", "", "configmap text proto")
-	toolchainConfigBucket = flag.String("toolchain-config-bucket", "", "cloud storage bucket for toolchain config")
-	configMapFile         = flag.String("configmap_file", "", "filename for configmap text proto")
-
-	traceProjectID     = flag.String("trace-project-id", "", "project id for cloud tracing")
-	pubsubProjectID    = flag.String("pubsub-project-id", "", "project id for pubsub")
-	serviceAccountFile = flag.String("service-account-file", "", "service account json file")
-
-	remoteexecAddr         = flag.String("remoteexec-addr", "", "use remoteexec API endpoint")
-	remoteInstancePrefix   = flag.String("remote-instance-prefix", "", "remote instance name path prefix.")
-	remoteInstanceBaseName = flag.String("remote-instance-basename", "default_instance", "remote instance basename under remote-instance-prefix")
-
-	// http://b/141901653
-	execMaxRetryCount     = flag.Int("exec-max-retry-count", 5, "max retry count for exec call. 0 is unlimited count, but bound to ctx timtout. Use small number for powerful clients to run local fallback quickly. Use large number for powerless clients to use remote more than local.")
-	execMissingInputLimit = flag.Int("exec-missing-input-limit", 100, "max missing inputs per exec call response. 0 is unlimited, meaning the client will be told about all missing inputs.")
-	execActionTimeout     = flag.Duration("exec-action-timeout", 15*time.Minute, "action timeout after which the execution should be killed.")
-
-	cmdFilesBucket      = flag.String("cmd-files-bucket", "", "cloud storage bucket for command binary files")
-	fetchConfigParallel = flag.Bool("fetch-config-parallel", true, "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")
-
-	// chromium code as of July 2020 (*.c*, *.h) = 230k
-	// also chromium clobber bulids has ~60k gomacc invocation.
-	// thinlto would upload *.o and *.thinlto.
-	// rbe-staging1 uses 2.2M keys (< 512MB memory usage in redis).
-	maxDigestCacheEntries = flag.Int("max-digest-cache-entries", 2e6, "maximum entries in in-memory digest cache. 0 means unimited")
-
-	// nsjail is applied in hardened request.
-	// note windows and chroot reqs are out of scope for the ratio.
-	// e.g.
-	//   hadening-ratio=0
-	//   nsjail-rario=<any>
-	//   => no hardening (no runsc nor nsjail) at all
-	//
-	//   hardening-ratio=1
-	//   nsjail-ratio=0
-	//   => hardening by runsc only
-	//
-	//   hardening-ratio=1
-	//   nsjail-ratio=1
-	//   => hardening by nsjail only
-	//
-	//   hardening-ratio=0.5
-	//   nsjail-ratio=0.5
-	//   => no hardeing 50%
-	//      hardening 50%
-	//        nsjail 25%  (50% in hardening)
-	//        runsc 25%   (50% in hardening)
-	experimentHardeningRatio = flag.Float64("experiment-hardening-ratio", 0, "Ratio [0,1] to enable hardening. 0=no hardening. 1=all hardening.")
-	experimentNsjailRatio    = flag.Float64("experiment-nsjail-ratio", 0, "Ratio [0,1] to use nsjail for hardening. 0=no nsjial (ie. runsc), 1=all nsjail.")
-	disableHardenings        = flag.String("disable-hardenings", "", "comma separated sha256 file hashes of command to disable hardening (i.e. for ELF-32)")
-
-	redisMaxIdleConns   = flag.Int("redis-max-idle-conns", redis.DefaultMaxIdleConns, "maximum number of idle connections to redis.")
-	redisMaxActiveConns = flag.Int("redis-max-active-conns", redis.DefaultMaxActiveConns, "maximum number of active connections to redis.")
-)
-
-var (
-	configUpdate = stats.Int64("go.chromium.org/goma/server/cmd/exec_server.toolchain-config-updates", "toolchain-config updates", stats.UnitDimensionless)
-
-	configStatusKey = tag.MustNewKey("status")
-
-	configViews = []*view.View{
-		{
-			Description: "counts toolchain-config updates",
-			TagKeys: []tag.Key{
-				configStatusKey,
-			},
-			Measure:     configUpdate,
-			Aggregation: view.Count(),
-		},
-	}
-)
-
-func recordConfigUpdate(ctx context.Context, err error) {
-	logger := log.FromContext(ctx)
-	status := "success"
-	if err != nil {
-		status = "failure"
-	}
-	ctx, cerr := tag.New(ctx, tag.Upsert(configStatusKey, status))
-	if cerr != nil {
-		logger.Fatal(cerr)
-	}
-	stats.Record(ctx, configUpdate.M(1))
-	if err != nil {
-		server.Flush()
-	}
-}
-
-func configMapToConfigResp(ctx context.Context, cm *cmdpb.ConfigMap) *cmdpb.ConfigResp {
-	resp := &cmdpb.ConfigResp{
-		VersionId: time.Now().UTC().Format(time.RFC3339),
-	}
-	for _, rt := range cm.Runtimes {
-		c := &cmdpb.Config{
-			Target: &cmdpb.Target{
-				Addr: *remoteexecAddr,
-			},
-			BuildInfo:          &cmdpb.BuildInfo{},
-			RemoteexecPlatform: &cmdpb.RemoteexecPlatform{},
-			Dimensions:         rt.PlatformRuntimeConfig.Dimensions,
-		}
-		for _, p := range rt.Platform.Properties {
-			c.RemoteexecPlatform.Properties = append(c.RemoteexecPlatform.Properties, &cmdpb.RemoteexecPlatform_Property{
-				Name:  p.Name,
-				Value: p.Value,
-			})
-		}
-		resp.Configs = append(resp.Configs, c)
-	}
-	return resp
-}
-
-func configureByLoader(ctx context.Context, loader *command.ConfigMapLoader, inventory *exec.Inventory, force bool) (string, error) {
-	logger := log.FromContext(ctx)
-	start := time.Now()
-	resp, err := loader.Load(ctx, force)
-	logger.Infof("loader.Load finished in %s: %v", time.Since(start), err)
-	if err != nil {
-		return "", err
-	}
-	start = time.Now()
-	err = inventory.Configure(ctx, resp)
-	logger.Infof("inventory.Configure finished in %s: %v", time.Since(start), err)
-	if err != nil {
-		return "", err
-	}
-	return resp.VersionId, nil
-}
-
-type cmdStorageBucket struct {
-	Bucket *storage.BucketHandle
-}
-
-func (b cmdStorageBucket) Open(ctx context.Context, hash string) (io.ReadCloser, error) {
-	return b.Bucket.Object(path.Join("sha256", hash)).NewReader(ctx)
-}
-
-type nullServer struct {
-	ch chan error
-}
-
-func (s nullServer) ListenAndServe() error { return <-s.ch }
-func (s nullServer) Shutdown(ctx context.Context) error {
-	close(s.ch)
-	return nil
-}
-
-type configServer struct {
-	inventory *exec.Inventory
-	configmap command.ConfigMap
-	psclient  *pubsub.Client
-	w         command.ConfigMapWatcher
-	loader    *command.ConfigMapLoader
-	cancel    func()
-}
-
-func newConfigServer(ctx context.Context, inventory *exec.Inventory, bucket, configMapFile string, cm *cmdpb.ConfigMap, gsclient *storage.Client, opts ...option.ClientOption) (*configServer, error) {
-	cs := &configServer{
-		inventory: inventory,
-	}
-	if *pubsubProjectID == "" {
-		*pubsubProjectID = server.ProjectID(ctx)
-		if *pubsubProjectID == "" {
-			return nil, errors.New("--pubsub-project-id must be set")
-		}
-	}
-	var err error
-	cs.psclient, err = pubsub.NewClient(ctx, *pubsubProjectID, opts...)
-	if err != nil {
-		return nil, fmt.Errorf("pubsub client failed: %v", err)
-	}
-	cs.configmap = command.ConfigMapBucket{
-		URI:            fmt.Sprintf("gs://%s/", bucket),
-		ConfigMap:      cm,
-		ConfigMapFile:  configMapFile,
-		StorageClient:  stiface.AdaptClient(gsclient),
-		PubsubClient:   cs.psclient,
-		SubscriberID:   fmt.Sprintf("toolchain-config-%s-%s", server.ClusterName(ctx), server.HostName(ctx)),
-		RemoteexecAddr: *remoteexecAddr,
-	}
-	cs.w = cs.configmap.Watcher(ctx)
-	cs.loader = &command.ConfigMapLoader{
-		ConfigMap: cs.configmap,
-		ConfigLoader: command.ConfigLoader{
-			StorageClient:  stiface.AdaptClient(gsclient),
-			EnableParallel: *fetchConfigParallel,
-		},
-	}
-	return cs, nil
-}
-
-func (cs *configServer) configure(ctx context.Context, force bool) error {
-	logger := log.FromContext(ctx)
-	id, err := configureByLoader(ctx, cs.loader, cs.inventory, force)
-	if errors.Is(err, context.Canceled) {
-		logger.Errorf("canceled to configure: %v", err)
-		return err
-	}
-	if err != nil {
-		if err != command.ErrNoUpdate {
-			recordConfigUpdate(ctx, err)
-		}
-		logger.Errorf("failed to configure: %v", err)
-		return err
-	}
-	logger.Infof("configure %s", id)
-	recordConfigUpdate(ctx, nil)
-	return nil
-}
-
-func (cs *configServer) ListenAndServe() error {
-	ctx, cancel := context.WithCancel(context.Background())
-	cs.cancel = cancel
-	logger := log.FromContext(ctx)
-	var backoff *gax.Backoff
-	for {
-		wctx := ctx
-		cancel := func() {}
-		var timeout time.Duration
-		if backoff != nil {
-			timeout = backoff.Pause()
-			wctx, cancel = context.WithTimeout(wctx, timeout)
-		}
-		logger.Infof("waiting for config update... timeout:%s", timeout)
-		err := cs.w.Next(wctx)
-		cancel()
-		if err != nil {
-			logger.Errorf("watch failed %v", err)
-			if backoff == nil {
-				return err
-			}
-			if errors.Is(err, command.ErrWatcherClosed) {
-				return err
-			}
-			// if backoff != nil, Next may return context.Canceled
-			// or so due to timeout in wctx.  Try loading anyway.
-		}
-		force := backoff != nil
-		err = cs.configure(ctx, force)
-		if errors.Is(err, context.Canceled) {
-			// configServer was shutted down.
-			return err
-		}
-		if err == command.ErrNoUpdate {
-			backoff = nil
-			continue
-		}
-		if err != nil {
-			// loader  may not get all objects matched around storage@v1.15.0
-			// https://github.com/googleapis/google-cloud-go/issues/4676
-			logger.Errorf("config failed: %v", err)
-			if backoff == nil {
-				backoff = &gax.Backoff{
-					Initial: time.Minute,
-					Max:     time.Hour,
-				}
-			}
-			continue
-		}
-		backoff = nil
-	}
-}
-
-func (cs *configServer) Shutdown(ctx context.Context) error {
-	defer errorreporter.Do(nil, nil)
-	defer func() {
-		if cs.psclient != nil {
-			cs.psclient.Close()
-		}
-	}()
-	if cs.cancel != nil {
-		cs.cancel()
-	}
-	return cs.w.Close()
-}
-
-func newDigestCache(ctx context.Context) remoteexec.DigestCache {
-	logger := log.FromContext(ctx)
-	addr, err := redis.AddrFromEnv()
-	if err != nil {
-		logger.Warnf("redis disabled for gomafile-digest: %v", err)
-		return digest.NewCache(nil, *maxDigestCacheEntries)
-	}
-	logger.Infof("redis enabled for gomafile-digest: %v idle=%d active=%d", addr, *redisMaxIdleConns, *redisMaxActiveConns)
-	return digest.NewCache(redis.NewClient(ctx, addr, redis.Opts{
-		Prefix:         "gomafile-digest:",
-		MaxIdleConns:   *redisMaxIdleConns,
-		MaxActiveConns: *redisMaxActiveConns,
-	}), *maxDigestCacheEntries)
-}
-
-func main() {
-	spanTimeout := remoteexec.DefaultSpanTimeout
-	flag.DurationVar(&spanTimeout.Inventory, "exec-inventory-timeout", spanTimeout.Inventory, "timeout of exec-inventory")
-	flag.DurationVar(&spanTimeout.InputTree, "exec-input-tree-timeout", spanTimeout.InputTree, "timeout of exec-iput-tree")
-	flag.DurationVar(&spanTimeout.Setup, "exec-setup-timeout", spanTimeout.Setup, "timeout of exec-setup")
-	flag.DurationVar(&spanTimeout.CheckCache, "exec-check-cache-timeout", spanTimeout.CheckCache, "timeout of exec-check-cache")
-	flag.DurationVar(&spanTimeout.CheckMissing, "exec-check-missing-timeout", spanTimeout.CheckMissing, "timeout of exec-check-missing")
-	flag.DurationVar(&spanTimeout.UploadBlobs, "exec-upload-blobs-timeout", spanTimeout.UploadBlobs, "timeout of exec-upload-blobs")
-	flag.DurationVar(&spanTimeout.Execute, "exec-execute-timeout", spanTimeout.Execute, "timeout of exec-execute")
-	flag.DurationVar(&spanTimeout.Response, "exec-response-timeout", spanTimeout.Response, "timeout of exec-response")
-	flag.Parse()
-	rand.Seed(time.Now().UnixNano())
-
-	ctx := context.Background()
-
-	profiler.Setup(ctx)
-
-	logger := log.FromContext(ctx)
-	defer logger.Sync()
-
-	if (*toolchainConfigBucket == "" || *configMapFile == "") && *configMap == "" {
-		logger.Fatalf("--toolchain-config-bucket,--configmap_file or --configmap must be given")
-	}
-	if *remoteexecAddr == "" {
-		logger.Fatalf("--remoteexec-addr must be given")
-	}
-
-	err := server.Init(ctx, *traceProjectID, "exec_server")
-	if err != nil {
-		logger.Fatal(err)
-	}
-
-	err = view.Register(configViews...)
-	if err != nil {
-		logger.Fatal(err)
-	}
-	err = view.Register(command.DefaultViews...)
-	if err != nil {
-		logger.Fatal(err)
-	}
-	err = view.Register(exec.DefaultViews...)
-	if err != nil {
-		logger.Fatal(err)
-	}
-	err = view.Register(remoteexec.DefaultViews...)
-	if err != nil {
-		logger.Fatal(err)
-	}
-	err = view.Register(digest.DefaultViews...)
-	if err != nil {
-		logger.Fatal(err)
-	}
-	trace.ApplyConfig(trace.Config{
-		DefaultSampler: server.NewLimitedSampler(server.DefaultTraceFraction, server.DefaultTraceQPS),
-	})
-
-	s, err := server.NewGRPC(*port,
-		grpc.MaxSendMsgSize(exec.DefaultMaxRespMsgSize),
-		grpc.MaxRecvMsgSize(exec.DefaultMaxReqMsgSize))
-	if err != nil {
-		logger.Fatal(err)
-	}
-
-	fileConn, err := server.DialContext(ctx, *fileAddr,
-		grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(file.DefaultMaxMsgSize), grpc.MaxCallSendMsgSize(file.DefaultMaxMsgSize)))
-	if err != nil {
-		logger.Fatalf("dial %s: %v", *fileAddr, err)
-	}
-	defer fileConn.Close()
-
-	var gsclient *storage.Client
-	var opts []option.ClientOption
-	if *toolchainConfigBucket != "" || *cmdFilesBucket != "" {
-		logger.Infof("toolchain-config-bucket or cmd-files-bucket is specified. use cloud storage")
-		if *serviceAccountFile != "" {
-			opts = append(opts, option.WithServiceAccountFile(*serviceAccountFile))
-		}
-		gsclient, err = storage.NewClient(ctx, opts...)
-		if err != nil {
-			logger.Fatalf("storage client failed: %v", err)
-		}
-		defer gsclient.Close()
-	} else {
-		logger.Infof("configmap_uri nor cmd-files-bucket is not specified. don't use cloud storage")
-	}
-
-	logger.Infof("use remoteexec API: %s", *remoteexecAddr)
-	reConn, err := grpc.DialContext(ctx, *remoteexecAddr,
-		grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{})),
-		grpc.WithStatsHandler(&ocgrpc.ClientHandler{}))
-	if err != nil {
-		logger.Fatalf("dial %s: %v", *remoteexecAddr, err)
-	}
-	defer reConn.Close()
-
-	if *remoteInstancePrefix == "" {
-		logger.Fatalf("--remote-instance-prefix must be given for remoteexec API")
-	}
-
-	if *fileLookupConcurrency == 0 {
-		*fileLookupConcurrency = 1
-	}
-	casBlobLookupConcurrency := 20
-	outputFileConcurrency := 20
-	logger.Infof("span timeout = %#v", spanTimeout)
-	re := &remoteexec.Adapter{
-		InstancePrefix:   *remoteInstancePrefix,
-		InstanceBaseName: *remoteInstanceBaseName,
-		ExecTimeout:      *execActionTimeout,
-		SpanTimeout:      spanTimeout,
-		Client: remoteexec.Client{
-			ClientConn: reConn,
-			Retry: rpc.Retry{
-				MaxRetry: *execMaxRetryCount,
-			},
-		},
-		GomaFile:    filepb.NewFileServiceClient(fileConn),
-		DigestCache: newDigestCache(ctx),
-		ToolDetails: &rpb.ToolDetails{
-			ToolName:    "goma/exec-server",
-			ToolVersion: "0.0.0-experimental",
-		},
-		FileLookupSema:    make(chan struct{}, *fileLookupConcurrency),
-		CASBlobLookupSema: make(chan struct{}, casBlobLookupConcurrency),
-		OutputFileSema:    make(chan struct{}, outputFileConcurrency),
-		HardeningRatio:    *experimentHardeningRatio,
-		NsjailRatio:       *experimentNsjailRatio,
-		DisableHardenings: strings.Split(*disableHardenings, ","),
-		MissingInputLimit: *execMissingInputLimit,
-	}
-	logger.Infof("hardeniong=%f nsjail=%f", re.HardeningRatio, re.NsjailRatio)
-
-	if *cmdFilesBucket == "" {
-		logger.Warnf("--cmd-files-bucket is not given. support only ARBITRARY_TOOLCHAIN_SUPPORT enabled client")
-	} else {
-		logger.Infof("use gs://%s for cmd files", *cmdFilesBucket)
-		re.CmdStorage = cmdStorageBucket{
-			Bucket: gsclient.Bucket(*cmdFilesBucket),
-		}
-	}
-
-	inventory := &re.Inventory
-
-	// expose bytestream proxy.
-	bs := &remoteexec.ByteStream{
-		Adapter:      re,
-		InstanceName: re.Instance(),
-		// TODO: Create bytestreams for multiple instances.
-	}
-	bspb.RegisterByteStreamServer(s.Server, bs)
-
-	var confServer server.Server
-	ready := make(chan error)
-	switch {
-	case *configMap != "":
-		go func() {
-			cm := &cmdpb.ConfigMap{}
-			err := prototext.Unmarshal([]byte(*configMap), cm)
-			if err != nil {
-				ready <- fmt.Errorf("parse configmap %q: %v", *configMap, err)
-				return
-			}
-			resp := configMapToConfigResp(ctx, cm)
-			err = inventory.Configure(ctx, resp)
-			if err != nil {
-				ready <- err
-				return
-			}
-			logger.Infof("configure %s", resp.VersionId)
-			ready <- nil
-		}()
-		confServer = nullServer{ch: make(chan error)}
-
-	case *toolchainConfigBucket != "":
-		cm := &cmdpb.ConfigMap{}
-		if *configMap != "" {
-			err := prototext.Unmarshal([]byte(*configMap), cm)
-			if err != nil {
-				ready <- fmt.Errorf("parse configmap %q: %v", *configMap, err)
-				return
-			}
-		}
-		cs, err := newConfigServer(ctx, inventory, *toolchainConfigBucket, *configMapFile, cm, gsclient, opts...)
-		if err != nil {
-			logger.Fatalf("configServer: %v", err)
-		}
-		go func() {
-			ready <- cs.configure(ctx, true)
-		}()
-		confServer = cs
-	}
-	http.Handle("/configz", inventory)
-	pb.RegisterExecServiceServer(s.Server, re)
-
-	// as of Dec 14 2018, it takes about 45 seconds to be ready.
-	// so wait 90-110 seconds with buffer.  b/120394151
-	// assume initialDelaySeconds: 120.
-	// TODO: split toolchain config server and exec server? b/120115232
-	timeout := 90*time.Second + time.Duration(float64(20*time.Second)*rand.Float64())
-	logger.Infof("wait %s to be ready", timeout)
-	start := time.Now()
-	select {
-	case err = <-ready:
-		if err != nil {
-			logger.Errorf("configure: %v", err)
-			confServer.Shutdown(ctx)
-			server.Flush()
-			logger.Fatalf("initial config failed: %v", err)
-		}
-		logger.Infof("exec-server ready in %s", time.Since(start))
-	case <-time.After(timeout):
-		logger.Errorf("initial loading timed out")
-		confServer.Shutdown(ctx)
-		server.Flush()
-		logger.Fatalf("no configs available in %s", timeout)
-	}
-	hs := server.NewHTTP(*mport, nil)
-	zpages.Handle(http.DefaultServeMux, "/debug")
-	server.Run(ctx, s, hs, confServer)
-}
diff --git a/cmd/execlog_server/main.go b/cmd/execlog_server/main.go
deleted file mode 100644
index 0bc6449..0000000
--- a/cmd/execlog_server/main.go
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright 2017 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.
-
-/*
-Binary execlog_server provides goma execlog service via gRPC.
-*/
-package main
-
-import (
-	"context"
-	"flag"
-	"net/http"
-
-	"go.opencensus.io/stats/view"
-	"go.opencensus.io/trace"
-	"go.opencensus.io/zpages"
-	"google.golang.org/grpc"
-
-	"go.chromium.org/goma/server/execlog"
-	"go.chromium.org/goma/server/log"
-	"go.chromium.org/goma/server/profiler"
-	pb "go.chromium.org/goma/server/proto/execlog"
-	"go.chromium.org/goma/server/server"
-)
-
-var (
-	port  = flag.Int("port", 5050, "rpc port")
-	mport = flag.Int("mport", 8081, "monitor port")
-
-	projectID = flag.String("project-id", "", "project id")
-)
-
-func main() {
-	flag.Parse()
-
-	ctx := context.Background()
-
-	profiler.Setup(ctx)
-
-	logger := log.FromContext(ctx)
-	defer logger.Sync()
-
-	err := server.Init(ctx, *projectID, "execlog_server")
-	if err != nil {
-		logger.Fatal(err)
-	}
-	err = view.Register(execlog.DefaultViews...)
-	if err != nil {
-		logger.Fatal(err)
-	}
-
-	trace.ApplyConfig(trace.Config{
-		DefaultSampler: server.NewLimitedSampler(server.DefaultTraceFraction, server.DefaultTraceQPS),
-	})
-
-	s, err := server.NewGRPC(*port,
-		grpc.MaxRecvMsgSize(execlog.DefaultMaxReqMsgSize))
-	if err != nil {
-		logger.Fatal(err)
-	}
-	els := &execlog.Service{}
-	pb.RegisterLogServiceServer(s.Server, els)
-
-	hs := server.NewHTTP(*mport, nil)
-	zpages.Handle(http.DefaultServeMux, "/debug")
-	server.Run(ctx, s, hs)
-}
diff --git a/cmd/file_server/client.go b/cmd/file_server/client.go
deleted file mode 100644
index cb30a86..0000000
--- a/cmd/file_server/client.go
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright 2017 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.
-//go:build ignore
-// +build ignore
-
-// client.go is sample client of file server.
-//
-// $ go run client.go 'host:port' store 'path'
-// $ go run client.go 'host:port' lookup 'hash' 'path'
-package main
-
-import (
-	"context"
-	"fmt"
-	"os"
-
-	"go.opencensus.io/plugin/ocgrpc"
-	"go.opencensus.io/trace"
-	"google.golang.org/grpc"
-
-	"go.chromium.org/goma/server/file"
-	"go.chromium.org/goma/server/log"
-	filepb "go.chromium.org/goma/server/proto/file"
-)
-
-func main() {
-	if len(os.Args) < 4 {
-		fmt.Fprintf(os.Stderr, "usage: go run client.go 'target' store 'path'\n")
-		fmt.Fprintf(os.Stderr, "       go run client.go 'target' lookup 'hash' 'path'\n")
-		os.Exit(1)
-	}
-
-	ctx := context.Background()
-	logger := log.FromContext(ctx)
-	trace.SetDefaultSampler(trace.AlwaysSample())
-	target := os.Args[1]
-
-	conn, err := grpc.Dial(target, grpc.WithBlock(), grpc.WithInsecure(), grpc.WithStatsHandler(&ocgrpc.ClientHandler{}))
-	if err != nil {
-		logger.Fatal(err)
-	}
-	defer conn.Close()
-	disk := file.Disk{
-		Client: filepb.NewFileServiceClient(conn),
-	}
-	cmd := os.Args[2]
-	switch cmd {
-	case "store":
-		fname := os.Args[3]
-		spec := &file.BlobSpec{}
-		_, err := disk.FromLocal(ctx, client, fname, spec)
-		if err != nil {
-			logger.Fatal(err)
-		}
-		fmt.Printf("%s: %s size=%d n=%d <= %s\n", spec.HashKey, spec.Blob.GetBlobType(), spec.Blob.GetFileSize(), len(spec.Blob.GetHashKey()), fname)
-
-	case "lookup":
-		if len(os.Args) < 5 {
-			fmt.Fprintf(os.Stderr, "       go run client.go 'target' lookup 'hash' 'path'\n")
-			os.Exit(1)
-		}
-		hashKey := os.Args[3]
-		fname := os.Args[4]
-		spec := &file.BlobSpec{
-			HashKey: hashKey,
-		}
-		err := disk.ToLocal(ctx, spec, fname)
-		fmt.Printf("%s: %s size=%d n=%d => %s\n", spec.HashKey, spec.Blob.GetBlobType(), spec.Blob.GetFileSize(), len(spec.Blob.GetHashKey(), fname))
-	default:
-		fmt.Fprintf(os.Stderr, "usage: go run client.go 'target' store 'path'\n")
-		fmt.Fprintf(os.Stderr, "       go run client.go 'target' lookup 'hash' 'path'\n")
-		os.Exit(1)
-	}
-}
diff --git a/cmd/file_server/main.go b/cmd/file_server/main.go
deleted file mode 100644
index e00d0af..0000000
--- a/cmd/file_server/main.go
+++ /dev/null
@@ -1,158 +0,0 @@
-// Copyright 2017 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.
-
-/*
-Binary file_server provides goma file service via gRPC.
-*/
-package main
-
-import (
-	"context"
-	"flag"
-	"fmt"
-
-	"cloud.google.com/go/storage"
-	"go.opencensus.io/trace"
-	k8sapi "golang.org/x/build/kubernetes/api"
-	"google.golang.org/api/option"
-	"google.golang.org/grpc"
-	"google.golang.org/grpc/codes"
-	"google.golang.org/grpc/status"
-
-	"go.chromium.org/goma/server/cache"
-	"go.chromium.org/goma/server/cache/gcs"
-	"go.chromium.org/goma/server/cache/redis"
-	"go.chromium.org/goma/server/file"
-	"go.chromium.org/goma/server/log"
-	"go.chromium.org/goma/server/profiler"
-	"go.chromium.org/goma/server/server"
-	"go.chromium.org/goma/server/server/healthz"
-
-	cachepb "go.chromium.org/goma/server/proto/cache"
-	pb "go.chromium.org/goma/server/proto/file"
-)
-
-var (
-	port      = flag.Int("port", 5050, "rpc port")
-	mport     = flag.Int("mport", 8081, "monitor port")
-	cacheAddr = flag.String("file-cache-addr", "", "cache server address")
-	bucket    = flag.String("bucket", "", "backing store bucket")
-
-	traceProjectID = flag.String("trace-project-id", "", "project id for cloud tracing")
-
-	serviceAccountFile = flag.String("service-account-file", "", "service account json file")
-
-	redisMaxIdleConns   = flag.Int("redis-max-idle-conns", redis.DefaultMaxIdleConns, "maximum number of idle connections to redis.")
-	redisMaxActiveConns = flag.Int("redis-max-active-conns", redis.DefaultMaxActiveConns, "maximum number of active connections to redis.")
-)
-
-type admissionController struct {
-	limit int64
-}
-
-func (a admissionController) AdmitPut(ctx context.Context, in *cachepb.PutReq) error {
-	logger := log.FromContext(ctx)
-	if a.limit <= 0 {
-		return nil
-	}
-	rss := server.ResidentMemorySize()
-	s := int64(len(in.Kv.Key) + len(in.Kv.Value))
-	if rss+2*s <= a.limit {
-		return nil
-	}
-	newRSS := server.GC(ctx)
-	if newRSS+2*s <= a.limit {
-		logger.Infof("GC reduced memory size to %d", newRSS)
-		return nil
-	}
-	// TODO: with retryinfo?
-	msg := fmt.Sprintf("memory size %d + req:%d > limit %d: gc->%d", rss, s, a.limit, newRSS)
-	healthz.SetUnhealthy(msg)
-	return status.Error(codes.ResourceExhausted, msg)
-}
-
-func main() {
-	flag.Parse()
-
-	ctx := context.Background()
-
-	profiler.Setup(ctx)
-
-	logger := log.FromContext(ctx)
-	defer logger.Sync()
-
-	err := server.Init(ctx, *traceProjectID, "file_server")
-	if err != nil {
-		logger.Fatal(err)
-	}
-	trace.ApplyConfig(trace.Config{
-		DefaultSampler: server.NewLimitedSampler(server.DefaultTraceFraction, server.DefaultTraceQPS),
-	})
-
-	s, err := server.NewGRPC(*port,
-		grpc.MaxSendMsgSize(file.DefaultMaxMsgSize),
-		grpc.MaxRecvMsgSize(file.DefaultMaxMsgSize))
-	if err != nil {
-		logger.Fatal(err)
-	}
-
-	var cclient cachepb.CacheServiceClient
-	addr, err := redis.AddrFromEnv()
-	switch {
-	case err == nil:
-		logger.Infof("redis enabled for gomafile: %s  idle=%d active=%d", addr, *redisMaxIdleConns, *redisMaxActiveConns)
-		c := redis.NewClient(ctx, addr, redis.Opts{
-			Prefix:         "gomafile:",
-			MaxIdleConns:   *redisMaxIdleConns,
-			MaxActiveConns: *redisMaxActiveConns,
-		})
-		defer c.Close()
-		cclient = c
-
-	case *cacheAddr != "":
-		logger.Infof("use cache server: %s", *cacheAddr)
-		c := cache.NewClient(ctx, *cacheAddr,
-			append([]grpc.DialOption{
-				grpc.WithDefaultCallOptions(grpc.FailFast(false)),
-			}, server.DefaultDialOption()...)...)
-		defer c.Close()
-		cclient = c
-
-	case *bucket != "":
-		logger.Infof("use cloud storage bucket: %s", *bucket)
-		var opts []option.ClientOption
-		if *serviceAccountFile != "" {
-			opts = append(opts, option.WithServiceAccountFile(*serviceAccountFile))
-		}
-		gsclient, err := storage.NewClient(ctx, opts...)
-		if err != nil {
-			logger.Fatalf("storage client failed: %v", err)
-		}
-		defer gsclient.Close()
-		c := gcs.New(gsclient.Bucket(*bucket))
-		limit, err := server.MemoryLimit()
-		if err != nil {
-			logger.Errorf("unknown memory limit: %v", err)
-		} else {
-			margin := int64(2 * file.DefaultMaxMsgSize)
-			a := admissionController{
-				limit: limit - margin,
-			}
-			c.AdmissionController = a
-			limitq := k8sapi.NewQuantity(limit, k8sapi.BinarySI)
-			marginq := k8sapi.NewQuantity(margin, k8sapi.BinarySI)
-			logger.Infof("memory check threshold: limit:%s - mergin:%s = %d", limitq, marginq, a.limit)
-		}
-		cclient = cache.LocalClient{CacheServiceServer: c}
-
-	default:
-		logger.Fatal("no cache server")
-	}
-	fs := &file.Service{
-		Cache: cclient,
-	}
-	pb.RegisterFileServiceServer(s.Server, fs)
-	hs := server.NewHTTP(*mport, nil)
-	server.Run(ctx, s, hs)
-}
diff --git a/cmd/frontend/main.go b/cmd/frontend/main.go
deleted file mode 100644
index 4b7a644..0000000
--- a/cmd/frontend/main.go
+++ /dev/null
@@ -1,216 +0,0 @@
-// Copyright 2017 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.
-
-/*
-Binary frontend is goma frontend.
-
-	$ frontend --port $port
-*/
-package main
-
-import (
-	"context"
-	"flag"
-	"fmt"
-	"net/http"
-	"path/filepath"
-
-	"go.opencensus.io/stats/view"
-	"go.opencensus.io/trace"
-	"go.opencensus.io/zpages"
-	k8sapi "golang.org/x/build/kubernetes/api"
-	"google.golang.org/grpc"
-	"google.golang.org/grpc/codes"
-	"google.golang.org/grpc/status"
-	"google.golang.org/protobuf/encoding/prototext"
-
-	"go.chromium.org/goma/server/auth"
-	"go.chromium.org/goma/server/backend"
-	"go.chromium.org/goma/server/frontend"
-	"go.chromium.org/goma/server/log"
-	"go.chromium.org/goma/server/profiler"
-	"go.chromium.org/goma/server/server"
-	"go.chromium.org/goma/server/server/healthz"
-
-	authpb "go.chromium.org/goma/server/proto/auth"
-	bepb "go.chromium.org/goma/server/proto/backend"
-	execpb "go.chromium.org/goma/server/proto/exec"
-	execlogpb "go.chromium.org/goma/server/proto/execlog"
-	filepb "go.chromium.org/goma/server/proto/file"
-)
-
-var (
-	port  = flag.Int("port", 80, "listening port (goma api endpoints)")
-	gport = flag.Int("gport", 5050, "grpc port")
-	mport = flag.Int("mport", 8081, "monitor port")
-
-	authAddr = flag.String("auth-addr", "passthrough:///auth-server:5050",
-		"auth server address")
-
-	backendConfig = flag.String("backend-config", "", "backend config. text proto of backend.BackendConfig")
-
-	configDir = flag.String("config-dir", "/etc/goma", "config directory")
-
-	// TODO set these value using kubernetes api
-	namespace = flag.String("namespace", "", "cluster namespace for trace prefix and label")
-
-	traceProjectID = flag.String("trace-project-id", "", "project id for cloud tracing")
-
-	serviceAccountFile = flag.String("service-account-file", "", "service account json file")
-
-	memoryMargin = flag.String("memory-margin",
-		k8sapi.NewQuantity(maxMsgSize, k8sapi.BinarySI).String(),
-		`accepts incoming requests if memory is available more than margin (bytes), if this value is positive.  can be kubernetes quantity string. e.g. "100Mi".  will be used if -memory-threshold is not specified.`)
-)
-
-const maxMsgSize = 64 * 1024 * 1024
-
-type memoryCheck struct {
-	hardThreshold int64
-	softThreshold int64
-}
-
-// Admit checks we can accept new request.
-// if memory usage is less than mc.softThreshold, it will accept.
-// Otherwise, it will try to run GC to release memory.
-// if memory usage is [mc.softThreshold, mc.hardThreshold), it returns
-// Unavailable error.
-// if memory usage is more than mc.hardThreshold, it returns ResourceExausted.
-func (mc memoryCheck) Admit(req *http.Request) error {
-	if mc.softThreshold <= 0 {
-		return nil
-	}
-	rss := server.ResidentMemorySize()
-	if rss <= mc.softThreshold {
-		return nil
-	}
-	ctx := req.Context()
-	logger := log.FromContext(ctx)
-	logger.Warnf("memory size %d > soft threshold:%d", rss, mc.softThreshold)
-	rss = server.GC(ctx)
-	if rss <= mc.softThreshold {
-		logger.Infof("GC reduced memory size to %d", rss)
-		return nil
-	}
-	m := fmt.Sprintf("memory size %d > soft threshold:%d: over=%d", rss, mc.softThreshold, rss-mc.softThreshold)
-	healthz.SetUnhealthy(m)
-	logger.Errorf("GC couldn't reduce memory size: %s", m)
-	if mc.hardThreshold > 0 && rss > mc.hardThreshold {
-		return status.Errorf(codes.ResourceExhausted, "server resource exhausted")
-	}
-	return status.Errorf(codes.Unavailable, "server unavailable")
-}
-
-func newMainServer(mux *http.ServeMux) server.Server {
-	hsMain := server.NewHTTP(*port, mux)
-	if *port != 443 {
-		return hsMain
-	}
-	certpem := filepath.Join(*configDir, "cert/cert.pem")
-	keypem := filepath.Join(*configDir, "cert/key.pem")
-	return server.NewHTTPS(hsMain, certpem, keypem)
-}
-
-func main() {
-	flag.Parse()
-
-	ctx := context.Background()
-
-	profiler.Setup(ctx)
-
-	logger := log.FromContext(ctx)
-	defer logger.Sync()
-
-	err := server.Init(ctx, *traceProjectID, "frontend")
-	if err != nil {
-		logger.Fatal(err)
-	}
-	err = view.Register(frontend.DefaultViews...)
-	if err != nil {
-		logger.Fatal(err)
-	}
-	err = view.Register(auth.DefaultViews...)
-	if err != nil {
-		logger.Fatal(err)
-	}
-	trace.ApplyConfig(trace.Config{
-		DefaultSampler: server.NewLimitedSampler(server.DefaultTraceFraction, server.DefaultTraceQPS),
-	})
-
-	s, err := server.NewGRPC(*gport,
-		grpc.MaxSendMsgSize(maxMsgSize),
-		grpc.MaxRecvMsgSize(maxMsgSize))
-	if err != nil {
-		logger.Fatal(err)
-	}
-
-	authConn, err := server.DialContext(ctx, *authAddr)
-	if err != nil {
-		logger.Fatalf("dial %s: %v", *authAddr, err)
-	}
-	defer authConn.Close()
-
-	beCfg := &bepb.BackendConfig{}
-	err = prototext.Unmarshal([]byte(*backendConfig), beCfg)
-	if err != nil {
-		logger.Fatal(err)
-	}
-	be, done, err := backend.FromProto(ctx, beCfg, backend.Option{
-		Auth: &auth.Auth{
-			Client: authpb.NewAuthServiceClient(authConn),
-		},
-		APIKeyDir: filepath.Join(*configDir, "api-keys"),
-	})
-	if err != nil {
-		logger.Fatal(err)
-	}
-	defer done()
-
-	mux := http.NewServeMux()
-	var memoryChecker memoryCheck
-	if *memoryMargin != "" {
-		q, err := k8sapi.ParseQuantity(*memoryMargin)
-		if err != nil {
-			logger.Fatal(err)
-		}
-		limit, err := server.MemoryLimit()
-		if err != nil {
-			logger.Errorf("unknown memory limit: %v", err)
-		} else {
-			memoryChecker.hardThreshold = limit - q.Value()
-			memoryChecker.softThreshold = limit - 2*q.Value()
-			limitq := k8sapi.NewQuantity(limit, k8sapi.BinarySI)
-			logger.Infof("memory check threshold: limit:%s - margin:%s = hard:%d, soft:%d", limitq, q, memoryChecker.hardThreshold, memoryChecker.softThreshold)
-		}
-	}
-
-	fe := frontend.Frontend{
-		AC:          memoryChecker,
-		Backend:     be,
-		TraceLabels: map[string]string{
-			// want to use this to compare between clusters,
-			// but not availble yet. http://b/77931512
-		},
-	}
-	frontend.Register(mux, fe)
-
-	if be, ok := be.(backend.GRPC); ok {
-		logger.Infof("register grpc server")
-		execpb.RegisterExecServiceServer(s.Server, be.ExecServer)
-		filepb.RegisterFileServiceServer(s.Server, be.FileServer)
-		execlogpb.RegisterLogServiceServer(s.Server, be.ExeclogServer)
-		// TODO: expose bytestream?
-	}
-
-	// This is for healthcheck from cloud load balancer.
-	// TODO: Do not allow access from other than load balancer.
-	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
-		w.Write([]byte("ok"))
-	})
-
-	hsMain := newMainServer(mux)
-	hsMonitoring := server.NewHTTP(*mport, nil)
-	zpages.Handle(http.DefaultServeMux, "/debug")
-	server.Run(ctx, s, hsMain, hsMonitoring)
-}
diff --git a/cmd/frontend/main_test.go b/cmd/frontend/main_test.go
deleted file mode 100644
index 5c6ef24..0000000
--- a/cmd/frontend/main_test.go
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2018 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.
-
-package main
-
-import (
-	"testing"
-
-	"go.chromium.org/goma/server/exec"
-	"go.chromium.org/goma/server/execlog"
-	"go.chromium.org/goma/server/file"
-)
-
-func TestMaxMsgSize(t *testing.T) {
-	if maxMsgSize < file.DefaultMaxMsgSize {
-		t.Errorf("%d < %d (file)", maxMsgSize, file.DefaultMaxMsgSize)
-	}
-	if maxMsgSize < exec.DefaultMaxReqMsgSize {
-		t.Errorf("%d < %d (exec)", maxMsgSize, exec.DefaultMaxReqMsgSize)
-	}
-	if maxMsgSize < execlog.DefaultMaxReqMsgSize {
-		t.Errorf("%d < %d (execlog)", maxMsgSize, execlog.DefaultMaxReqMsgSize)
-	}
-}
diff --git a/cmd/goma_grpc_client/main.go b/cmd/goma_grpc_client/main.go
deleted file mode 100644
index f186046..0000000
--- a/cmd/goma_grpc_client/main.go
+++ /dev/null
@@ -1,225 +0,0 @@
-// Copyright 2018 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.
-
-/*
-Binary goma_grpc_client is a simple gRPC client of goma api.
-
-	$ goma_grpc_client [-api_key <key>] <addr> <service>.<method> <request>
-*/
-package main
-
-import (
-	"context"
-	"crypto/tls"
-	"crypto/x509"
-	"flag"
-	"fmt"
-	"io/ioutil"
-	"os"
-	"path/filepath"
-	"strings"
-
-	"go.uber.org/zap"
-	"google.golang.org/grpc"
-	"google.golang.org/grpc/credentials"
-	"google.golang.org/grpc/metadata"
-	"google.golang.org/protobuf/encoding/prototext"
-	"google.golang.org/protobuf/proto"
-
-	"go.chromium.org/goma/server/auth/account"
-	"go.chromium.org/goma/server/auth/enduser"
-	"go.chromium.org/goma/server/log"
-	gomapb "go.chromium.org/goma/server/proto/api"
-	execpb "go.chromium.org/goma/server/proto/exec"
-	execlogpb "go.chromium.org/goma/server/proto/execlog"
-	filepb "go.chromium.org/goma/server/proto/file"
-)
-
-var (
-	// https://cloud.google.com/endpoints/docs/grpc/restricting-api-access-with-api-keys-grpc
-	apiKey = flag.String("api_key", "", "api_key")
-
-	enduserEmail  = flag.String("enduser_email", "", "enduser email")
-	enduserGroup  = flag.String("enduser_group", "", "enduser group")
-	enduserSAJSON = flag.String("enduser_service_account_json", "", "enduser service account json file")
-
-	tlsVerify = flag.Bool("tls_verify", true, "verifies the server's certificate chain and hostname.")
-	insecure  = flag.Bool("insecure", false, "insecure connection, i.e. no TLS")
-	verbose   = flag.Bool("v", false, "verbose flag")
-)
-
-type desc struct {
-	method func(context.Context, proto.Message) (proto.Message, error)
-	req    proto.Message
-}
-
-func protoDesc(ctx context.Context, conn *grpc.ClientConn, servMethod string) (desc, error) {
-	// TODO: use proto descriptor?
-	switch servMethod {
-	case "devtools_goma.ExecService.Exec":
-		client := execpb.NewExecServiceClient(conn)
-		return desc{
-			method: func(ctx context.Context, req proto.Message) (proto.Message, error) {
-				return client.Exec(ctx, req.(*gomapb.ExecReq))
-			},
-			req: &gomapb.ExecReq{},
-		}, nil
-
-	case "devtools_goma.FileService.StoreFile":
-		client := filepb.NewFileServiceClient(conn)
-		return desc{
-			method: func(ctx context.Context, req proto.Message) (proto.Message, error) {
-				return client.StoreFile(ctx, req.(*gomapb.StoreFileReq))
-			},
-			req: &gomapb.StoreFileReq{},
-		}, nil
-
-	case "devtools_goma.FileService.LookupFile":
-		client := filepb.NewFileServiceClient(conn)
-		return desc{
-			method: func(ctx context.Context, req proto.Message) (proto.Message, error) {
-				return client.LookupFile(ctx, req.(*gomapb.LookupFileReq))
-			},
-			req: &gomapb.LookupFileReq{},
-		}, nil
-
-	case "devtools_goma.LogService.SaveLog":
-		client := execlogpb.NewLogServiceClient(conn)
-		return desc{
-			method: func(ctx context.Context, req proto.Message) (proto.Message, error) {
-				return client.SaveLog(ctx, req.(*gomapb.SaveLogReq))
-			},
-			req: &gomapb.SaveLogReq{},
-		}, nil
-	default:
-		return desc{}, fmt.Errorf("unknown service: %s", servMethod)
-	}
-}
-
-func setEnduser(ctx context.Context) context.Context {
-	if *enduserEmail == "" || *enduserGroup == "" || *enduserSAJSON == "" {
-		return ctx
-	}
-	logger := log.FromContext(ctx)
-	p := account.JSONDir{
-		Dir: filepath.Dir(*enduserSAJSON),
-		Scopes: []string{
-			"https://www.googleapis.com/auth/cloud-build-service",
-		},
-	}
-	name := filepath.Base(*enduserSAJSON)
-	name = strings.TrimRight(name, ".json")
-	a, err := p.New(name)
-	if err != nil {
-		logger.Errorf("service account %s: %v", name, err)
-		return ctx
-	}
-	token, err := a.Token(ctx)
-	if err != nil {
-		logger.Errorf("token %s: %v", name, err)
-		return ctx
-	}
-	logger.Infof("enduser email=%q group=%q token=%v", *enduserEmail, *enduserGroup, token)
-	return enduser.NewContext(ctx, enduser.New(*enduserEmail, *enduserGroup, token))
-}
-
-func main() {
-	flag.Usage = func() {
-		w := flag.CommandLine.Output()
-		fmt.Fprintf(w, "Usage of %s:\n", os.Args[0])
-		fmt.Fprintf(w, "%s <address> <service>.<method> <request>\n", os.Args[0])
-		fmt.Fprintf(w, " <address>; host:port\n")
-		fmt.Fprintf(w, " <service>; exported service name\n")
-		fmt.Fprintf(w, "          ; e.g. devtools_goma.ExecService\n")
-		fmt.Fprintf(w, " <method> ; method name. e.g Exec\n")
-		fmt.Fprintf(w, " <request>; text protobuffer for request\n")
-		flag.PrintDefaults()
-	}
-	flag.Parse()
-	ctx := context.Background()
-
-	if !*verbose {
-		log.SetZapLogger(zap.NewNop())
-	}
-	logger := log.FromContext(ctx)
-	fatalf := func(format string, args ...interface{}) {
-		if !*verbose {
-			// logger.Fatalf won't print if we set zap.NewNop...
-			fmt.Fprintf(os.Stderr, format+"\n", args...)
-		}
-		logger.Fatalf(format, args...)
-	}
-	if flag.NArg() < 3 {
-		flag.Usage()
-		os.Exit(2)
-	}
-	address := flag.Arg(0)
-	servMethod := flag.Arg(1)
-	requestMsg := flag.Arg(2)
-
-	certPool, err := x509.SystemCertPool()
-	if err != nil {
-		fatalf("system cert pool: %v", err)
-	}
-	if certPath := os.Getenv("GOMA_SSL_EXTRA_CERT"); certPath != "" {
-		buf, err := ioutil.ReadFile(certPath)
-		if err != nil {
-			fatalf("cert file %q: %v", certPath, err)
-		}
-		ok := certPool.AppendCertsFromPEM(buf)
-		if !ok {
-			fatalf("set cert from $GOMA_SSL_EXTRA_CERT")
-		}
-	}
-	if certData := os.Getenv("GOMA_SSL_EXTRA_CERT_DATA"); certData != "" {
-		logger.Infof("using GOMA_SSL_EXTRA_CERT_DATA:\n%s", certData)
-		ok := certPool.AppendCertsFromPEM([]byte(certData))
-		if !ok {
-			fatalf("set cert from $GOMA_SSL_EXTRA_CERT_DATA")
-		}
-	}
-	insecureSkipVerify := !*tlsVerify
-	if insecureSkipVerify {
-		logger.Warnf("insecure skip verify. accepts any certificate")
-	}
-
-	opts := []grpc.DialOption{
-		grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{
-			RootCAs:            certPool,
-			InsecureSkipVerify: insecureSkipVerify,
-		})),
-	}
-	if *insecure {
-		logger.Warnf("insecure connection")
-		opts[0] = grpc.WithInsecure()
-	}
-	conn, err := grpc.DialContext(ctx, address, opts...)
-	if err != nil {
-		fatalf("dial: %v", err)
-	}
-	defer conn.Close()
-
-	desc, err := protoDesc(ctx, conn, servMethod)
-	if err != nil {
-		fatalf("desc: %v", err)
-	}
-	err = prototext.Unmarshal([]byte(requestMsg), desc.req)
-	if err != nil {
-		fatalf("request: %v", err)
-	}
-
-	if *apiKey != "" {
-		logger.Infof("Using api_key: %s", *apiKey)
-		ctx = metadata.NewOutgoingContext(ctx, metadata.Pairs("x-api-key", *apiKey))
-	}
-	ctx = setEnduser(ctx)
-	md, _ := metadata.FromOutgoingContext(ctx)
-	logger.Debugf("outgoing metatada: %v", md)
-
-	resp, err := desc.method(ctx, desc.req)
-	if err != nil {
-		fatalf("call: %v", err)
-	}
-	fmt.Println("response:\n", prototext.Format(resp))
-}
diff --git a/cmd/goma_replay/main.go b/cmd/goma_replay/main.go
deleted file mode 100644
index f55d008..0000000
--- a/cmd/goma_replay/main.go
+++ /dev/null
@@ -1,475 +0,0 @@
-// Copyright 2019 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.
-
-/*
-Binary goma_replay is a simple goma client for load testing etc.
-
-"dump exec_req" on goma client, and run
-
-	$ goma_replay -n 100 -l 100
-*/
-package main
-
-import (
-	"context"
-	"encoding/json"
-	"errors"
-	"flag"
-	"fmt"
-	"io/ioutil"
-	"net"
-	"net/http"
-	"os"
-	"os/signal"
-	"os/user"
-	"path/filepath"
-	"strconv"
-	"strings"
-	"sync"
-	"sync/atomic"
-	"syscall"
-	"time"
-
-	"go.uber.org/zap"
-	"golang.org/x/oauth2"
-	"golang.org/x/oauth2/google"
-	googleoauth2 "google.golang.org/api/oauth2/v2"
-	"google.golang.org/protobuf/proto"
-
-	"go.chromium.org/goma/server/httprpc"
-	"go.chromium.org/goma/server/log"
-
-	gomapb "go.chromium.org/goma/server/proto/api"
-)
-
-var (
-	dataSourceDir = flag.String("data_source_dir", gomaTmpDir(), "data source directory")
-
-	repeat  = flag.Int("n", 1, "N repeat request")
-	limit   = flag.Int("l", 1, "limit at most N request")
-	buildID = flag.String("build_id", "", "overrides build_id")
-
-	asInternal = flag.Bool("as_internal", true, "use oauth2 for internal client")
-	verbose    = flag.Bool("v", false, "verbose flag")
-)
-
-func gomaTmpDir() string {
-	// client/mypath.cc GetGomaTmpDir
-	if v := os.Getenv("GOMA_TMP_DIR"); v != "" {
-		return v
-	}
-	u, err := user.Current()
-	if err != nil {
-		panic(err)
-	}
-	return filepath.Join("/run/user", u.Uid, "goma_"+u.Username)
-}
-
-func clearInputs(req *gomapb.ExecReq) {
-	for i := range req.Input {
-		req.Input[i].Content = nil
-	}
-}
-
-func loadRequestData(ctx context.Context, dir string) ([]*gomapb.ExecReq, error) {
-	var reqs []*gomapb.ExecReq
-	logger := log.FromContext(ctx)
-	logger.Infof("loading from %s", dir)
-	err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
-		if err != nil {
-			return err
-		}
-		logger.Debugf("datasource: %s", path)
-		if filepath.Base(path) != "exec_req.data" {
-			return nil
-		}
-		b, err := ioutil.ReadFile(path)
-		if err != nil {
-			return err
-		}
-		req := &gomapb.ExecReq{}
-		err = proto.Unmarshal(b, req)
-		if err != nil {
-			return fmt.Errorf("%s: %v", path, err)
-		}
-		clearInputs(req)
-		if *buildID != "" {
-			req.GetRequesterInfo().BuildId = proto.String(*buildID)
-		}
-		reqs = append(reqs, req)
-		fmt.Printf("req[%d]=%s\n", len(reqs)-1, path)
-		logger.Debugf("req[%d]=%s", len(reqs)-1, req)
-		return nil
-	})
-	logger.Infof("loaded %d req(s): err=%v", len(reqs), err)
-	return reqs, err
-}
-
-func oauth2Client(ctx context.Context, fname string) (*http.Client, string, error) {
-	logger := log.FromContext(ctx)
-	b, err := ioutil.ReadFile(fname)
-	if err != nil {
-		return nil, "", err
-	}
-	var config struct {
-		ClientID     string `json:"client_id"`
-		ClientSecret string `json:"client_secret"`
-		RedirectURL  string `json:"redirect_uri"`
-		Scope        string `json:"scope"`
-		AuthURI      string `json:"auth_uri"`
-		TokenURI     string `json:"token_uri"`
-		RefreshToken string `json:"refresh_token"`
-	}
-	err = json.Unmarshal(b, &config)
-	if err != nil {
-		return nil, "", fmt.Errorf("oauth2 config %s: %v", fname, err)
-	}
-	c := &oauth2.Config{
-		ClientID:     config.ClientID,
-		ClientSecret: config.ClientSecret,
-		Endpoint: oauth2.Endpoint{
-			AuthURL:  config.AuthURI,
-			TokenURL: config.TokenURI,
-		},
-		RedirectURL: config.RedirectURL,
-		Scopes:      []string{config.Scope},
-	}
-	t := &oauth2.Token{
-		TokenType:    "Bearer",
-		RefreshToken: config.RefreshToken,
-	}
-	token, err := c.TokenSource(ctx, t).Token()
-	if err != nil {
-		return nil, "", fmt.Errorf("oauth2 token %s: %v", fname, err)
-	}
-	logger.Debugf("access token: %s", token.AccessToken)
-
-	s, err := googleoauth2.NewService(ctx)
-	if err != nil {
-		return nil, "", err
-	}
-	tokeninfo, err := s.Tokeninfo().Context(ctx).AccessToken(token.AccessToken).Do()
-	if err != nil {
-		return nil, "", err
-	}
-	return c.Client(ctx, token), tokeninfo.Email, nil
-}
-
-func oauth2ServiceAccountClient(ctx context.Context, fname string) (*http.Client, string, error) {
-	b, err := ioutil.ReadFile(fname)
-	if err != nil {
-		return nil, "", err
-	}
-	c, err := google.JWTConfigFromJSON(b, googleoauth2.UserinfoEmailScope)
-	if err != nil {
-		return nil, "", err
-	}
-	return c.Client(ctx), c.Email, nil
-}
-
-func newClient(ctx context.Context) (*http.Client, string, error) {
-	logger := log.FromContext(ctx)
-
-	// client http_init.cc: InitHttpClientOptions
-	for _, ca := range []struct {
-		desc      string
-		newClient func(context.Context) (*http.Client, string, error)
-	}{
-		// TODO: GOMA_HTTP_AUTHORIZATION_FILE
-		{
-			desc: "GOMA_OAUTH2_CONFIG_FILE",
-			newClient: func(ctx context.Context) (*http.Client, string, error) {
-				fname := os.Getenv("GOMA_OAUTH2_CONFIG_FILE")
-				if fname == "" {
-					return nil, "", errors.New("no GOMA_OAUTH2_CONFIG_FILE")
-				}
-				logger.Infof("oauth2 config: %s", fname)
-				return oauth2Client(ctx, fname)
-			},
-		},
-		{
-			desc: "GOMA_SERVICE_ACCOUNT_JSON_FILE",
-			newClient: func(ctx context.Context) (*http.Client, string, error) {
-				fname := os.Getenv("GOMA_SERVICE_ACCOUNT_JSON_FILE")
-				if fname == "" {
-					return nil, "", errors.New("no GOMA_SERVICE_ACCOUNT_JSON_FILE")
-				}
-				logger.Infof("service account: %s", fname)
-				return oauth2ServiceAccountClient(ctx, fname)
-			},
-		},
-		// TODO: GOMA_USE_GCE_SERVICE_ACCOUNT
-		// TODO: LUCI_CONTEXT
-		{
-			desc: "default goma oauth2 config file",
-			newClient: func(ctx context.Context) (*http.Client, string, error) {
-				fname := os.ExpandEnv("$HOME/.goma_client_oauth2_config")
-				if *asInternal {
-					fname = os.ExpandEnv("$HOME/.goma_oauth2_config")
-				}
-				logger.Infof("oauth2 config: %s", fname)
-				return oauth2Client(ctx, fname)
-			},
-		},
-	} {
-		logger.Infof("client auth %s", ca.desc)
-		c, email, err := ca.newClient(ctx)
-		if err != nil {
-			logger.Warnf("client auth %s: %v", ca.desc, err)
-			continue
-		}
-		return c, email, nil
-	}
-	return nil, "", errors.New("no goma auth avaialble")
-}
-
-type target struct {
-	host        string
-	port        int
-	useSSL      bool
-	extraParams string
-}
-
-func (t target) String() string {
-	scheme := "http"
-	if t.useSSL {
-		scheme = "https"
-	}
-	if (scheme == "http" && t.port == 80) ||
-		(scheme == "https" && t.port == 443) {
-		return fmt.Sprintf("%s://%s/cxx-compiler-service/e%s", scheme, t.host, t.extraParams)
-	}
-	return fmt.Sprintf("%s://%s:%d/cxx-compiler-service/e/%s", scheme, t.host, t.port, t.extraParams)
-}
-
-func newTargetFromEnv(ctx context.Context) (target, error) {
-	u, err := user.Current()
-	if err != nil {
-		return target{}, err
-	}
-	t := target{
-		host:   fmt.Sprintf("rbe-dev.endpoints.goma-%s.cloud.goog", u.Username),
-		port:   443,
-		useSSL: true,
-	}
-	if v := os.Getenv("GOMA_SERVER_HOST"); v != "" {
-		t.host = v
-	}
-	if v := os.Getenv("GOMA_SERVER_PORT"); v != "" {
-		n, err := strconv.Atoi(v)
-		if err != nil {
-			return t, fmt.Errorf("GOMA_SERVER_PORT=%s; %v", v, err)
-		}
-		t.port = n
-	}
-	if v := os.Getenv("GOMA_USE_SSL"); v != "" {
-		// client/env_flags.cc GOMA_EnvToBool
-		t.useSSL = strings.ContainsAny(v, "tTyY1")
-	}
-	if v := os.Getenv("GOMA_RPC_EXTRA_PARAMS"); v != "" {
-		t.extraParams = v
-	}
-	// proxy, url_path_prefix?
-	return t, nil
-}
-
-type dialer struct {
-	d     *net.Dialer
-	mu    sync.Mutex
-	addrs map[string]string
-}
-
-func (d *dialer) resolve(ctx context.Context, host string) (string, error) {
-	if a, ok := d.addrs[host]; ok {
-		return a, nil
-	}
-	logger := log.FromContext(ctx)
-	addrs, err := net.LookupHost(host)
-	if err != nil {
-		logger.Warnf("resolve failed %q: %q", host, err)
-		return "", err
-	}
-	logger.Infof("resolve %q => %q", host, addrs[0])
-	d.mu.Lock()
-	d.addrs[host] = addrs[0]
-	d.mu.Unlock()
-	return addrs[0], nil
-}
-
-func (d *dialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
-	host, port, err := net.SplitHostPort(address)
-	if err != nil {
-		return nil, err
-	}
-	addr, err := d.resolve(ctx, host)
-	if err != nil {
-		return nil, err
-	}
-	for i := 0; i < 5; i++ {
-		conn, err := d.d.DialContext(ctx, network, net.JoinHostPort(addr, port))
-		if err != nil {
-			continue
-		}
-		return conn, nil
-	}
-	return nil, fmt.Errorf("too many retry to dial to %s:%s", network, address)
-}
-
-func main() {
-	flag.Parse()
-	ctx := context.Background()
-
-	if !*verbose {
-		log.SetZapLogger(zap.NewNop())
-	}
-	logger := log.FromContext(ctx)
-	fatalf := func(format string, args ...interface{}) {
-		if !*verbose {
-			// logger.Fatalf won't print if we set zap.NewNop...
-			fmt.Fprintf(os.Stderr, format+"\n", args...)
-		}
-		logger.Fatalf(format, args...)
-	}
-	errorf := func(format string, args ...interface{}) {
-		if !*verbose {
-			fmt.Fprintf(os.Stderr, format+"\n", args...)
-		}
-		logger.Errorf(format, args...)
-	}
-
-	d := &dialer{
-		d: &net.Dialer{
-			Timeout:   30 * time.Second,
-			KeepAlive: 30 * time.Second,
-		},
-		addrs: make(map[string]string),
-	}
-
-	// https://golang.org/pkg/net/http/#RoundTripper
-	http.DefaultTransport = &http.Transport{
-		Proxy:                 http.ProxyFromEnvironment,
-		DialContext:           d.DialContext,
-		MaxIdleConns:          100,
-		IdleConnTimeout:       90 * time.Second,
-		TLSHandshakeTimeout:   10 * time.Second,
-		ExpectContinueTimeout: 1 * time.Second,
-	}
-
-	c, email, err := newClient(ctx)
-	if err != nil {
-		fatalf("client: %v", err)
-	}
-	fmt.Println("client:", email)
-	targ, err := newTargetFromEnv(ctx)
-	if err != nil {
-		fatalf("target: %v", err)
-	}
-	fmt.Println("target:", targ)
-	// Warm up the resolver cache
-	_, err = d.resolve(ctx, targ.host)
-	if err != nil {
-		fatalf("resolver warm up: %v", err)
-	}
-	reqs, err := loadRequestData(ctx, *dataSourceDir)
-	if err != nil {
-		fatalf("request data: %v", err)
-	}
-	if *buildID != "" {
-		fmt.Println("build_id:", *buildID)
-	}
-
-	sigch := make(chan os.Signal, 1)
-	signal.Notify(sigch, syscall.SIGTERM, syscall.SIGINT)
-	ctx, cancel := context.WithCancel(ctx)
-	defer cancel()
-	go func() {
-		for {
-			<-sigch
-			fmt.Println("interrupted")
-			if ctx.Err() != nil {
-				fmt.Println("interrupted again. exiting...")
-				os.Exit(1)
-			}
-			cancel()
-		}
-	}()
-
-	t0 := time.Now()
-	var wg sync.WaitGroup
-	nreq, nrep := len(reqs), *repeat
-	// atomic counters
-	var (
-		running, finished int32
-		httpErrors        int32
-		gomaExecErrors    int32
-		gomaMissingInputs int32
-		gomaOtherErrors   int32
-	)
-	wg.Add(1)
-	go func() {
-		defer wg.Done()
-		sema := make(chan bool, *limit)
-	Loop:
-		for i, req := range reqs {
-			for j := 0; j < *repeat; j++ {
-				if ctx.Err() != nil {
-					nreq = i
-					nrep = j
-					break Loop
-				}
-				wg.Add(1)
-				sema <- true
-				go func(i, j int, req *gomapb.ExecReq) {
-					defer wg.Done()
-					defer func() {
-						atomic.AddInt32(&running, -1)
-						atomic.AddInt32(&finished, 1)
-						<-sema
-					}()
-					atomic.AddInt32(&running, 1)
-					resp := &gomapb.ExecResp{}
-					t := time.Now()
-					err = httprpc.Call(ctx, c, targ.String(), req, resp)
-					if err != nil {
-						if ctx.Err() != nil {
-							return
-						}
-						atomic.AddInt32(&httpErrors, 1)
-						errorf("req[%d]%d %v", i, j, err)
-					}
-					if e := resp.GetError(); e != gomapb.ExecResp_OK {
-						atomic.AddInt32(&gomaExecErrors, 1)
-						errorf("req[%d]%d ExecError %s %s", i, j, e, time.Since(t))
-						logger.Debugf("%s", resp)
-					}
-					if len(resp.MissingInput) > 0 {
-						atomic.AddInt32(&gomaMissingInputs, 1)
-						errorf("req[%d]%d missing inputs=%d %s", i, j, len(resp.MissingInput), time.Since(t))
-					}
-					if len(resp.ErrorMessage) > 0 {
-						atomic.AddInt32(&gomaOtherErrors, 1)
-						errorf("req[%d]%d error %q %s", i, j, resp.ErrorMessage, time.Since(t))
-					}
-					logger.Infof("req[%d]%d %s %s", i, j, resp.GetError(), time.Since(t))
-				}(i, j, req)
-				fmt.Printf("req[%d]%d/%d %d/%d error %d/%d/%d/%d %s\r", i, j, *repeat,
-					atomic.LoadInt32(&running),
-					atomic.LoadInt32(&finished),
-					atomic.LoadInt32(&httpErrors),
-					atomic.LoadInt32(&gomaExecErrors),
-					atomic.LoadInt32(&gomaMissingInputs),
-					atomic.LoadInt32(&gomaOtherErrors),
-					time.Since(t0))
-			}
-		}
-	}()
-	wg.Wait()
-	fmt.Printf("%s %d/%d reqs * %d/%d (limit:%d) finished=%d error http=%d/exec=%d/missing=%d/other=%d in %s\n", targ, nreq, len(reqs), nrep, *repeat, *limit,
-		atomic.LoadInt32(&finished),
-		atomic.LoadInt32(&httpErrors),
-		atomic.LoadInt32(&gomaExecErrors),
-		atomic.LoadInt32(&gomaMissingInputs),
-		atomic.LoadInt32(&gomaOtherErrors),
-		time.Since(t0))
-}
diff --git a/cmd/remoteexec_proxy/README.md b/cmd/remoteexec_proxy/README.md
deleted file mode 100644
index 0663717..0000000
--- a/cmd/remoteexec_proxy/README.md
+++ /dev/null
@@ -1,105 +0,0 @@
-# Goma server - remoteexec_proxy
-
-# How to deploy it on Google App Engine Flexible Environment
-
-`remoteexec_proxy` can run on Google App Engine Flexible Environment.
-
-## Dependencies
-
-Need to setup [cloud memorystore](https://cloud.google.com/memorystore/)
-and [cloud storage](https://cloud.google.com/storage/).
-
-### Cloud Memorystore
-
-Cloud memorystore is used for digest cache.
-You need to create an instance in the same location as App Engine apps.
-
-NOTE If different location, your server will get many `context deadline exceeded` errors.
-
-If not created, run `gcloud app create` can create App Engine app in
-the specific region in the cloud project.
-
-```
-$ gcloud app create --region=$REGION
-```
-
-If it is already created, you can check it by `gcloud app describe`
-
-```
-$ gcloud app describe --format='get(locationId)'
-```
-
-It is sufficient with 1GB size, but you might want to set
-`maxmemory-policy: allkeys-lru`.
-
-```
-$ gcloud redis instances create $REDIS_INSTANCE_NAME \
- --region=$REGION \
- --redis-config='maxmemory-poclicy=allkeys-lru'
-```
-
-### Cloud Storage
-
-Cloud storage is used to store file cache.
-
-```
-$ gsutil mb gs://${PROJECT_ID}-file-cache
-$ gsutil lifecycle set '{"rule": [{"action": {"type": "Delete"}, "condition": {"age": 1}}]}' gs://${PROJECT_ID}-file-cache
-```
-
-## Deploy
-
-To deploy the server, create a workspace
-
-```
-$ mkdir /path/to/workspace
-$ cd /path/to/workspace
-$ TOPDIR=$(pwd)
-$ git clone https://chromium.googlesource.com/infra/goma/server
-$ cd server
-$ GO111MODULE=on go mod vendor
-$ mkdir ../gopath/src
-$ export GOPATH=$(TOPDIR)/gopath
-$ mv vendor/* ../gopath/src
-$ mkdir app
-$ cd app
-$ cp ../server/cmd/remoteexec_server/* .
-$ REDISHOST=$(gcloud redis instances describe $REDIS_INSTANCE_NAME \
-   --region $REGION --format='get(host)')
-$ REDISPORT=$(gcloud redis instances describe $REDIS_INSTANCE_NAME \
-   --region $REGION --format='get(port)')
-$ cat > app.yaml <<EOF
-runtime: go
-env: flex
-
-network:
-  name: default
-
-liveness_check:
-  path: "/healthz"
-
-readiness_check:
-  path: "/healthz"
-
-env_variables:
-  REDISHOST: "$REDISHOST"
-  REDISPORT: "$REDISPORT"
-EOF
-$ cat > flag.go << EOF
-package main
-
-func init() {
-	*port = 8080
-	*remoteexecAddr = "remotebuildexecution.googleapis.com:443"
-	*remoteInstanceName = "projects/$PROJECT_ID/instances/default_instance"
-	*allowedUsers = "<comma separated allowed-users-email-address>"
-	*platformContainerImage = "docker://gcr.io/..."
-	*fileCacheBucket = "$PROJECT_ID-file-cache"
-}
-EOF
-$ gcloud app deploy
-```
-
-Then, you can use `$PROJECT_ID.appspot.com` as `$GOMA_SERVER_HOST`.
-You need to specify `GOMA_ARBTRARY_TOOLCHAIN_SUPPORT=true`.
-
diff --git a/cmd/remoteexec_proxy/main.go b/cmd/remoteexec_proxy/main.go
deleted file mode 100644
index b36ef3a..0000000
--- a/cmd/remoteexec_proxy/main.go
+++ /dev/null
@@ -1,553 +0,0 @@
-// Copyright 2019 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.
-
-/*
-Binary remoteexec-proxy is a proxy server between Goma client and Remote Execution API.
-*/
-package main
-
-import (
-	"context"
-	"crypto/tls"
-	"crypto/x509"
-	"flag"
-	"fmt"
-	"html/template"
-	"io/ioutil"
-	"net/http"
-	"os"
-	"os/user"
-	"path"
-	"path/filepath"
-	"strings"
-	"time"
-
-	"cloud.google.com/go/storage"
-	rpb "github.com/bazelbuild/remote-apis/build/bazel/remote/execution/v2"
-	"go.opencensus.io/plugin/ocgrpc"
-	"go.opencensus.io/trace"
-	"google.golang.org/api/option"
-	"google.golang.org/grpc"
-	"google.golang.org/grpc/credentials"
-	"google.golang.org/protobuf/encoding/prototext"
-
-	"go.chromium.org/goma/server/auth"
-	"go.chromium.org/goma/server/auth/account"
-	"go.chromium.org/goma/server/auth/acl"
-	"go.chromium.org/goma/server/cache"
-	"go.chromium.org/goma/server/cache/gcs"
-	"go.chromium.org/goma/server/cache/redis"
-	"go.chromium.org/goma/server/file"
-	"go.chromium.org/goma/server/frontend"
-	"go.chromium.org/goma/server/httprpc"
-	execrpc "go.chromium.org/goma/server/httprpc/exec"
-	execlogrpc "go.chromium.org/goma/server/httprpc/execlog"
-	filerpc "go.chromium.org/goma/server/httprpc/file"
-	"go.chromium.org/goma/server/log"
-	"go.chromium.org/goma/server/profiler"
-	gomapb "go.chromium.org/goma/server/proto/api"
-	authpb "go.chromium.org/goma/server/proto/auth"
-	cachepb "go.chromium.org/goma/server/proto/cache"
-	cmdpb "go.chromium.org/goma/server/proto/command"
-	execpb "go.chromium.org/goma/server/proto/exec"
-	execlogpb "go.chromium.org/goma/server/proto/execlog"
-	filepb "go.chromium.org/goma/server/proto/file"
-	"go.chromium.org/goma/server/remoteexec"
-	"go.chromium.org/goma/server/remoteexec/digest"
-	"go.chromium.org/goma/server/rpc"
-	"go.chromium.org/goma/server/server"
-)
-
-var (
-	port = flag.Int("port", 8090, "listening port (goma api endpoints)")
-
-	remoteexecAddr           = flag.String("remoteexec-addr", "", "remoteexec API endpoint")
-	remoteInstanceName       = flag.String("remote-instance-name", "", "remote instance name")
-	allowedUsers             = flag.String("allowed-users", "", "comma separated list of allowed users. `*@domain` will match any user in domain. if empty, current user is allowed.")
-	serviceAccountJSON       = flag.String("service-account-json", "", "service account json, used to talk to RBE and cloud storage (if --file-cache-bucket is used)")
-	platformContainerImage   = flag.String("platform-container-image", "", "docker uri of platform container image")
-	insecureRemoteexec       = flag.Bool("insecure-remoteexec", false, "insecure grpc for remoteexec API")
-	insecureSkipVerify       = flag.Bool("insecure-skip-verify", false, "insecure skip verifying the server certificate")
-	additionalTLSCertificate = flag.String("additional-tls-certificate", "", "additional TLS root certificate for verifying the server certificate")
-	execMaxRetryCount        = flag.Int("exec-max-retry-count", 5, "max retry count for exec call. 0 is unlimited count, but bound to ctx timtout. Use small number for powerful clients to run local fallback quickly. Use large number for powerless clients to use remote more than local.")
-	execMissingInputLimit    = flag.Int("exec-missing-input-limit", 100, "max missing inputs per exec call response. 0 is unlimited, meaning the client will be told about all missing inputs.")
-
-	fileCacheBucket = flag.String("file-cache-bucket", "", "file cache bucking store bucket")
-
-	execConfigFile = flag.String("exec-config-file", "", "exec inventory config file")
-
-	maxDigestCacheEntries = flag.Int("max-digest-cache-entries", 2e6, "maximum entries in in-memory digest cache")
-
-	traceProjectID = flag.String("trace-project-id", "", "project id for cloud tracing")
-	traceFraction  = flag.Float64("trace-sampling-fraction", 1.0, "sampling fraction for stackdriver trace")
-	traceQPS       = flag.Float64("trace-sampling-qps-limit", 1.0, "sampling qps limit for stackdriver trace")
-
-	redisMaxIdleConns   = flag.Int("redis-max-idle-conns", redis.DefaultMaxIdleConns, "maximum number of idle connections to redis.")
-	redisMaxActiveConns = flag.Int("redis-max-active-conns", redis.DefaultMaxActiveConns, "maximum number of active connections to redis.")
-)
-
-func myEmail(ctx context.Context) string {
-	logger := log.FromContext(ctx)
-	username := os.Getenv("USER")
-	if username == "" {
-		u, err := user.Current()
-		if err != nil {
-			logger.Fatalf("failed to get username: need --allowed-users: %v", err)
-		}
-		username = u.Username
-	}
-	buf, err := ioutil.ReadFile("/etc/mailname")
-	if err != nil {
-		logger.Fatalf("failed to get email: need --allowed-users: %v", err)
-	}
-	return fmt.Sprintf("%s@%s", username, strings.TrimSpace(string(buf)))
-}
-
-type authClient struct {
-	Service *auth.Service
-}
-
-func (c authClient) Auth(ctx context.Context, req *authpb.AuthReq, opts ...grpc.CallOption) (*authpb.AuthResp, error) {
-	return c.Service.Auth(ctx, req)
-}
-
-type fileClient struct {
-	Service filepb.FileServiceServer
-}
-
-func (c fileClient) StoreFile(ctx context.Context, req *gomapb.StoreFileReq, opts ...grpc.CallOption) (*gomapb.StoreFileResp, error) {
-	return c.Service.StoreFile(ctx, req)
-}
-
-func (c fileClient) LookupFile(ctx context.Context, req *gomapb.LookupFileReq, opts ...grpc.CallOption) (*gomapb.LookupFileResp, error) {
-	return c.Service.LookupFile(ctx, req)
-}
-
-type execlogService struct {
-	execlogpb.UnimplementedLogServiceServer
-}
-
-func (c execlogService) SaveLog(ctx context.Context, req *gomapb.SaveLogReq) (*gomapb.SaveLogResp, error) {
-	return &gomapb.SaveLogResp{}, nil
-}
-
-type cacheClient struct {
-	Service cachepb.CacheServiceServer
-}
-
-func (c cacheClient) Get(ctx context.Context, req *cachepb.GetReq, opts ...grpc.CallOption) (*cachepb.GetResp, error) {
-	return c.Service.Get(ctx, req)
-}
-
-func (c cacheClient) Put(ctx context.Context, req *cachepb.PutReq, opts ...grpc.CallOption) (*cachepb.PutResp, error) {
-	return c.Service.Put(ctx, req)
-}
-
-const gomaClientClientID = "687418631491-r6m1c3pr0lth5atp4ie07f03ae8omefc.apps.googleusercontent.com"
-
-type defaultACL struct {
-	allowedUser    []string
-	allowedDomains []string
-}
-
-func (a defaultACL) Load(ctx context.Context) (*authpb.ACL, error) {
-	serviceAccount := "default"
-	if *serviceAccountJSON != "" {
-		serviceAccount = strings.TrimSuffix(filepath.Base(*serviceAccountJSON), ".json")
-	}
-
-	return &authpb.ACL{
-		Groups: []*authpb.Group{
-			{
-				Id:             "user",
-				Audience:       gomaClientClientID,
-				Emails:         a.allowedUser,
-				Domains:        a.allowedDomains,
-				ServiceAccount: serviceAccount,
-			},
-		},
-	}, nil
-}
-
-type reExecServer struct {
-	execpb.UnimplementedExecServiceServer
-	re *remoteexec.Adapter
-}
-
-func (r reExecServer) Exec(ctx context.Context, req *gomapb.ExecReq) (*gomapb.ExecResp, error) {
-	ctx, id := rpc.TagID(ctx, req.GetRequesterInfo())
-	logger := log.FromContext(ctx)
-	logger.Infof("call exec %s", id)
-	return r.re.Exec(ctx, req)
-}
-
-type reFileServer struct {
-	filepb.UnimplementedFileServiceServer
-	s filepb.FileServiceServer
-}
-
-func (r reFileServer) StoreFile(ctx context.Context, req *gomapb.StoreFileReq) (*gomapb.StoreFileResp, error) {
-	ctx, id := rpc.TagID(ctx, req.GetRequesterInfo())
-	logger := log.FromContext(ctx)
-	logger.Infof("call storefile %s", id)
-	return r.s.StoreFile(ctx, req)
-}
-
-func (r reFileServer) LookupFile(ctx context.Context, req *gomapb.LookupFileReq) (*gomapb.LookupFileResp, error) {
-	ctx, id := rpc.TagID(ctx, req.GetRequesterInfo())
-	logger := log.FromContext(ctx)
-	logger.Infof("call lookupfile %s", id)
-	return r.s.LookupFile(ctx, req)
-}
-
-type localBackend struct {
-	ExecService execpb.ExecServiceServer
-	FileService filepb.FileServiceServer
-	Auth        httprpc.Auth
-}
-
-func (b localBackend) Ping() http.Handler {
-	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
-		ctx := req.Context()
-		logger := log.FromContext(ctx)
-		ctx, err := b.Auth.Auth(ctx, req)
-		if err != nil {
-			http.Error(w, fmt.Sprintf("auth failed: %v", err), http.StatusUnauthorized)
-			logger.Errorf("ping unauthorized: %v", err)
-			return
-		}
-		w.Header().Set("Accept-Encoding", "gzip, deflate")
-		fmt.Fprintln(w, "ok")
-	})
-}
-
-func (b localBackend) Exec() http.Handler {
-	return execrpc.Handler(b.ExecService, httprpc.Timeout(5*time.Minute), httprpc.WithAuth(b.Auth))
-}
-
-func (b localBackend) ByteStream() http.Handler {
-	return http.HandlerFunc(http.NotFound)
-}
-
-func (b localBackend) StoreFile() http.Handler {
-	return filerpc.StoreHandler(b.FileService, httprpc.Timeout(1.*time.Minute), httprpc.WithAuth(b.Auth))
-}
-
-func (b localBackend) LookupFile() http.Handler {
-	return filerpc.LookupHandler(b.FileService, httprpc.Timeout(1*time.Minute), httprpc.WithAuth(b.Auth))
-}
-
-func (b localBackend) Execlog() http.Handler {
-	return execlogrpc.Handler(execlogService{}, httprpc.Timeout(1*time.Minute), httprpc.WithAuth(b.Auth))
-}
-
-func readConfigResp(fname string) (*cmdpb.ConfigResp, error) {
-	b, err := ioutil.ReadFile(fname)
-	if err != nil {
-		return nil, err
-	}
-	resp := &cmdpb.ConfigResp{}
-	err = prototext.Unmarshal(b, resp)
-	if err != nil {
-		return nil, err
-	}
-	// fix target address etc.
-	for _, c := range resp.Configs {
-		if c.Target == nil {
-			c.Target = &cmdpb.Target{}
-		}
-		c.Target.Addr = *remoteexecAddr
-		if c.BuildInfo == nil {
-			c.BuildInfo = &cmdpb.BuildInfo{}
-		}
-	}
-	return resp, nil
-}
-
-func main() {
-	spanTimeout := remoteexec.DefaultSpanTimeout
-	flag.DurationVar(&spanTimeout.Inventory, "exec-inventory-timeout", spanTimeout.Inventory, "timeout of exec-inventory")
-	flag.DurationVar(&spanTimeout.InputTree, "exec-input-tree-timeout", spanTimeout.InputTree, "timeout of exec-iput-tree")
-	flag.DurationVar(&spanTimeout.Setup, "exec-setup-timeout", spanTimeout.Setup, "timeout of exec-setup")
-	flag.DurationVar(&spanTimeout.CheckCache, "exec-check-cache-timeout", spanTimeout.CheckCache, "timeout of exec-check-cache")
-	flag.DurationVar(&spanTimeout.CheckMissing, "exec-check-missing-timeout", spanTimeout.CheckMissing, "timeout of exec-check-missing")
-	flag.DurationVar(&spanTimeout.UploadBlobs, "exec-upload-blobs-timeout", spanTimeout.UploadBlobs, "timeout of exec-upload-blobs")
-	flag.DurationVar(&spanTimeout.Execute, "exec-execute-timeout", spanTimeout.Execute, "timeout of exec-execute")
-	flag.DurationVar(&spanTimeout.Response, "exec-response-timeout", spanTimeout.Response, "timeout of exec-response")
-
-	flag.Parse()
-	ctx := context.Background()
-
-	profiler.Setup(ctx)
-
-	logger := log.FromContext(ctx)
-	defer logger.Sync()
-
-	if *allowedUsers == "" {
-		*allowedUsers = myEmail(ctx)
-	}
-	var allowed []string
-	var allowedDomains []string
-	for _, u := range strings.Split(*allowedUsers, ",") {
-		u = strings.TrimSpace(u)
-		if strings.HasPrefix(u, "*@") {
-			allowedDomains = append(allowedDomains, strings.TrimPrefix(u, "*@"))
-		} else {
-			allowed = append(allowed, u)
-		}
-	}
-	logger.Infof("allow access for %q / domains %q", allowed, allowedDomains)
-
-	err := server.Init(ctx, *traceProjectID, "remoteexec-proxy")
-	if err != nil {
-		logger.Fatal(err)
-	}
-
-	trace.ApplyConfig(trace.Config{
-		DefaultSampler: server.NewLimitedSampler(*traceFraction, *traceQPS),
-	})
-
-	saDir := "/"
-	if *serviceAccountJSON != "" {
-		logger.Infof("using service account: %s", *serviceAccountJSON)
-		saDir = filepath.Dir(*serviceAccountJSON)
-	} else {
-		logger.Infof("using default service account")
-	}
-	aclCheck := acl.ACL{
-		Loader: defaultACL{
-			allowedUser:    allowed,
-			allowedDomains: allowedDomains,
-		},
-		Checker: acl.Checker{
-			Pool: account.JSONDir{
-				Dir: saDir,
-				Scopes: []string{
-					"https://www.googleapis.com/auth/cloud-build-service",
-				},
-			},
-		},
-	}
-	err = aclCheck.Update(ctx)
-	if err != nil {
-		logger.Fatal(err)
-	}
-
-	authService := &auth.Service{
-		CheckToken: aclCheck.CheckToken,
-	}
-
-	var cclient cachepb.CacheServiceClient
-	if *fileCacheBucket != "" {
-		logger.Infof("use cloud storage bucket: %s", *fileCacheBucket)
-		var opts []option.ClientOption
-		if *serviceAccountJSON != "" {
-			opts = append(opts, option.WithServiceAccountFile(*serviceAccountJSON))
-		}
-		gsclient, err := storage.NewClient(ctx, opts...)
-		if err != nil {
-			logger.Fatalf("storage client failed: %v", err)
-		}
-		defer gsclient.Close()
-		cclient = cache.LocalClient{
-			CacheServiceServer: gcs.New(gsclient.Bucket(*fileCacheBucket)),
-		}
-	} else {
-		cacheService, err := cache.New(cache.Config{
-			MaxBytes: 1 * 1024 * 1024 * 1024,
-		})
-		if err != nil {
-			logger.Fatal(err)
-		}
-		cclient = cacheClient{
-			Service: cacheService,
-		}
-	}
-
-	fileServiceClient := fileClient{
-		Service: &file.Service{
-			Cache: cclient,
-		},
-	}
-
-	certPool, err := x509.SystemCertPool()
-	if err != nil {
-		logger.Fatal(err)
-	}
-	if certPool == nil {
-		logger.Fatal("got nil certPool")
-	}
-	if *additionalTLSCertificate != "" {
-		caCert, err := ioutil.ReadFile(*additionalTLSCertificate)
-		if err != nil {
-			logger.Fatal(err)
-		}
-		if ok := certPool.AppendCertsFromPEM(caCert); !ok {
-			logger.Fatal("No certificates loaded from %s", *additionalTLSCertificate)
-		}
-	}
-	tlsConfig := &tls.Config{
-		InsecureSkipVerify: *insecureSkipVerify,
-		RootCAs:            certPool,
-	}
-	opts := []grpc.DialOption{
-		grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig)),
-		grpc.WithStatsHandler(&ocgrpc.ClientHandler{}),
-	}
-	if *insecureRemoteexec {
-		opts[0] = grpc.WithInsecure()
-		logger.Warnf("use insecrure remoteexec API")
-	}
-
-	reConn, err := grpc.DialContext(ctx, *remoteexecAddr, opts...)
-	if err != nil {
-		logger.Fatal(err)
-	}
-	defer reConn.Close()
-
-	var digestCache remoteexec.DigestCache
-	redisAddr, err := redis.AddrFromEnv()
-	if err != nil {
-		logger.Warnf("redis disabled for gomafile-digest: %v", err)
-		digestCache = digest.NewCache(nil, *maxDigestCacheEntries)
-	} else {
-		logger.Infof("redis enabled for gomafile-digest: %v idle=%d active=%d", redisAddr, *redisMaxIdleConns, *redisMaxActiveConns)
-		digestCache = digest.NewCache(redis.NewClient(ctx, redisAddr, redis.Opts{
-			Prefix:         "gomafile-digest:",
-			MaxIdleConns:   *redisMaxIdleConns,
-			MaxActiveConns: *redisMaxActiveConns,
-		}), *maxDigestCacheEntries)
-	}
-
-	re := &remoteexec.Adapter{
-		InstancePrefix: path.Dir(*remoteInstanceName),
-		ExecTimeout:    15 * time.Minute,
-		SpanTimeout:    spanTimeout,
-		Client: remoteexec.Client{
-			ClientConn: reConn,
-			Retry: rpc.Retry{
-				MaxRetry: *execMaxRetryCount,
-			},
-		},
-		InsecureClient: *insecureRemoteexec,
-		GomaFile:       fileServiceClient,
-		DigestCache:    digestCache,
-		ToolDetails: &rpb.ToolDetails{
-			ToolName:    "remoteexec_proxy",
-			ToolVersion: "0.0.0-experimental",
-		},
-		FileLookupSema:    make(chan struct{}, 2),
-		CASBlobLookupSema: make(chan struct{}, 20),
-		MissingInputLimit: *execMissingInputLimit,
-	}
-
-	configResp := &cmdpb.ConfigResp{
-		VersionId: time.Now().UTC().Format(time.RFC3339),
-		Configs: []*cmdpb.Config{
-			{
-				Target: &cmdpb.Target{
-					Addr: *remoteexecAddr,
-				},
-				BuildInfo: &cmdpb.BuildInfo{},
-				Dimensions: []string{
-					"os:linux",
-				},
-				RemoteexecPlatform: &cmdpb.RemoteexecPlatform{
-					RbeInstanceBasename: path.Base(*remoteInstanceName),
-					Properties: []*cmdpb.RemoteexecPlatform_Property{
-						{
-							Name:  "container-image",
-							Value: *platformContainerImage,
-						}, {
-							Name:  "OSFamily",
-							Value: "Linux",
-						},
-					},
-				},
-			},
-		},
-	}
-	// TODO: document config example?
-	if *execConfigFile != "" {
-		c, err := readConfigResp(*execConfigFile)
-		if err != nil {
-			logger.Fatal(err)
-		}
-		configResp = c
-	}
-	err = re.Inventory.Configure(ctx, configResp)
-	if err != nil {
-		logger.Fatal(err)
-	}
-	mux := http.DefaultServeMux
-	frontend.Register(mux, frontend.Frontend{
-		Backend: localBackend{
-			ExecService: reExecServer{re: re},
-			FileService: reFileServer{s: fileServiceClient.Service},
-			Auth: &auth.Auth{
-				Client: authClient{Service: authService},
-			},
-		},
-	})
-
-	mux.Handle("/healthz", http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
-		fmt.Fprintln(w, "ok")
-	}))
-	tmpl := template.Must(template.New("index").Parse(`
-<html>
-<head>
- <title>Goma remoteexec_proxy at {{.Port}}</title>
-</head>
-<body>
-<h1>Goma remoteexec_proxy</h1>
-
-<p><b>remoteexec-addr:</b> {{.RemoteexecAddr}}</p>
-<p><b>remote-instance-name:</b> {{.RemoteInstanceName}}</p>
-<p><b>allowed-users:</b> {{.AllowedUsers}}</p>
-<p><b>service-account-json:</b> <a href="file://{{.ServiceAccountJSON}}">{{.ServiceAccountJSON}}</a></p>
-<p><b>platform-container-image:</b> {{.PlatformContainerImage}}</p>
-<p><b>redis:</b> {{.RedisAddr}}</p>
-<p><b>file-cache-bucket:</b> {{.FileCacheBucket}}</p>
-
-<p><b>config:</b>
-<pre>{{.Config}}</pre>
-
-<hr>
-<p>
-<a href="/debug/tracez">/debug/tracez</a> |
-<a href="/debug/rpcz">/debug/rpcz</a> |
-<a href="/healthz">/healthz - for health check</a>
-</body>
-</html>`))
-
-	mux.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
-		err := tmpl.Execute(w, struct {
-			Port                   int
-			RemoteexecAddr         string
-			RemoteInstanceName     string
-			AllowedUsers           []string
-			ServiceAccountJSON     string
-			PlatformContainerImage string
-			RedisAddr              string
-			FileCacheBucket        string
-			Config                 *cmdpb.ConfigResp
-		}{
-			Port:                   *port,
-			RemoteexecAddr:         *remoteexecAddr,
-			RemoteInstanceName:     *remoteInstanceName,
-			AllowedUsers:           allowed,
-			ServiceAccountJSON:     *serviceAccountJSON,
-			PlatformContainerImage: *platformContainerImage,
-			RedisAddr:              redisAddr,
-			FileCacheBucket:        *fileCacheBucket,
-			Config:                 configResp,
-		})
-		if err != nil {
-			logger := log.FromContext(ctx)
-			logger.Errorf("index template: %v", err)
-		}
-	}))
-	hsMain := server.NewHTTP(*port, mux)
-	server.Run(ctx, hsMain)
-}
diff --git a/command/configmap.go b/command/configmap.go
deleted file mode 100644
index e83e9a1..0000000
--- a/command/configmap.go
+++ /dev/null
@@ -1,815 +0,0 @@
-// Copyright 2018 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.
-
-package command
-
-import (
-	"bytes"
-	"context"
-	"errors"
-	"fmt"
-	"io/ioutil"
-	"math/rand"
-	"path"
-	"runtime"
-	"sort"
-	"strings"
-	"time"
-
-	"cloud.google.com/go/pubsub"
-	"cloud.google.com/go/storage"
-	"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"
-	"google.golang.org/grpc/codes"
-	"google.golang.org/grpc/status"
-	"google.golang.org/protobuf/encoding/prototext"
-	"google.golang.org/protobuf/proto"
-	"google.golang.org/protobuf/types/known/timestamppb"
-
-	"go.chromium.org/goma/server/log"
-	cmdpb "go.chromium.org/goma/server/proto/command"
-	"go.chromium.org/goma/server/rpc"
-)
-
-var (
-	ErrWatcherClosed = errors.New("watcher closed")
-
-	pubsubErrors = stats.Int64(
-		"go.chromium.org/goma/command/configmap.pubsub-error",
-		"configmap pubsub error",
-		stats.UnitDimensionless)
-
-	// DefaultViews are the default views provided by this package.
-	// You need to register the view for data to actually be collected.
-	DefaultViews = []*view.View{
-		{
-			Description: "configmap pubsub error",
-			Measure:     pubsubErrors,
-			Aggregation: view.Count(),
-		},
-	}
-)
-
-// ConfigMapLoader loads toolchain_config config map.
-//
-// ConfigMap provides Watcher, Seqs, Bucket and RuntimeConfigs.
-//
-// if seq is updated from last load, it will load CmdDescriptor
-// from <bucket>/<runtime>/<prebuilt_item>/descriptors/<descriptorHash>.
-type ConfigMapLoader struct {
-	ConfigMap    ConfigMap
-	ConfigLoader ConfigLoader
-	ConfigStore  ConfigStore
-}
-
-// ConfigMap is an interface to access toolchain config map.
-type ConfigMap interface {
-	// Watcher returns config map watcher.
-	Watcher(ctx context.Context) ConfigMapWatcher
-
-	// Seqs returns a map of config name to sequence.
-	Seqs(ctx context.Context) (map[string]string, error)
-
-	// Bucket returns toolchain-config bucket.
-	Bucket(ctx context.Context) (string, error)
-
-	// RuntimeConfigs returns a map of RuntimeConfigs.
-	RuntimeConfigs(ctx context.Context) (map[string]*cmdpb.RuntimeConfig, error)
-}
-
-// ConfigMapWatcher is an interface to watch config map.
-type ConfigMapWatcher interface {
-	// Next waits for some updates in config map.
-	Next(ctx context.Context) error
-
-	// Close closes the watcher.
-	Close() error
-}
-
-// ConfigMapBucket access config on cloud storage bucket.
-//
-// <bucket> is <project>-toolchain-config.
-// in the <bucket>
-//
-//	<runtime>/
-//	         seq: text, sequence number.
-//	         <prebuilt-item>/descriptors/<descriptorHash>: proto CmdDescriptor
-//
-// Watcher watches */seq files via default notification topic on the bucket.
-// Seqs and RuntimeConfigs will read ConfigMapFile everytime.
-type ConfigMapBucket struct {
-	// URI of config data.
-	// gs://<bucket>/
-	// e.g. gs://$project-toolchain-config/
-	URI string
-
-	ConfigMap     *cmdpb.ConfigMap
-	ConfigMapFile string
-
-	PubsubClient *pubsub.Client
-
-	// StorageClient is an interface for accessing Cloud Storage. It can
-	// be a Cloud Storage client or a fake for testing.
-	StorageClient stiface.Client
-
-	// SubscriberID should be unique per each server instance
-	// to get notification in every server instance.
-	SubscriberID string
-
-	// Remoteexec API address, if RBE API is used.
-	// Otherwise, use service_addr in RuntimeConfig proto.
-	RemoteexecAddr string
-}
-
-type configMapBucketWatcher struct {
-	s      *pubsub.Subscription
-	cancel func()
-	ch     chan *pubsub.Message
-}
-
-func (w configMapBucketWatcher) run(ctx context.Context) {
-	logger := log.FromContext(ctx)
-	logger.Infof("watch start")
-	err := rpc.Retry{}.Do(ctx, func() error {
-		err := w.s.Receive(ctx, func(ctx context.Context, msg *pubsub.Message) {
-			logger.Debugf("receive message: %s", msg.ID)
-			w.ch <- msg
-		})
-		if err != nil {
-			logger.Errorf("configMapBucketWatcher.run: %v", err)
-		}
-		if status.Code(err) == codes.Unknown {
-			// crbug.com/1226107
-			return rpc.RetriableError{
-				Err: err,
-			}
-		}
-		return err
-	})
-	logger.Errorf("configMapBucketWatcher.run retry finished: %v", err)
-	close(w.ch)
-	logger.Infof("watch finished")
-}
-
-func (w configMapBucketWatcher) Next(ctx context.Context) error {
-	logger := log.FromContext(ctx)
-	for {
-		var msg *pubsub.Message
-		var ok bool
-		select {
-		case msg, ok = <-w.ch:
-			if !ok {
-				return ErrWatcherClosed
-			}
-		case <-ctx.Done():
-			return ctx.Err()
-		}
-		// https://cloud.google.com/storage/docs/pubsub-notifications#attributes
-		eventType := msg.Attributes["eventType"]
-		objectId := msg.Attributes["objectId"]
-		objectGeneration := msg.Attributes["objectGeneration"]
-		eventTime := msg.Attributes["eventTime"]
-		publishTime := msg.PublishTime
-		logger.Debugf("handle message: %s eventType:%s objectId:%s", msg.ID, eventType, objectId)
-
-		msg.Ack()
-
-		if eventType != storage.ObjectFinalizeEvent {
-			continue
-		}
-		if path.Base(objectId) != "seq" {
-			continue
-		}
-		logger.Infof("%s was updated gen:%s at %s (published:%s)", objectId, objectGeneration, eventTime, publishTime)
-		// drain pending messages. these messages were generated
-		// before we call Seqs or Data, so we won't need to handle
-		// them later.
-		for {
-			select {
-			case msg := <-w.ch:
-				logger.Debugf("drain message: %s", msg.ID)
-				msg.Ack()
-			default:
-				return nil
-			}
-		}
-	}
-}
-
-func (w configMapBucketWatcher) Close() error {
-	ctx := context.Background()
-	logger := log.FromContext(ctx)
-	logger.Infof("watcher close")
-	w.cancel() // finish w.s.Receive in run.
-	// drain ch
-	go func() {
-		for msg := range w.ch {
-			// ok to ack because we use notification as trigger only.
-			logger.Debugf("drain message: %s", msg.ID)
-			msg.Ack()
-		}
-	}()
-	logger.Infof("delete subscription: %s", w.s)
-	return w.s.Delete(ctx)
-}
-
-type configMapBucketPoller struct {
-	baseDelay time.Duration
-	done      chan bool
-}
-
-func (w configMapBucketPoller) Next(ctx context.Context) error {
-	logger := log.FromContext(ctx)
-	dur := time.Duration(float64(w.baseDelay) * (1 + 0.2*(rand.Float64()*2-1)))
-	logger.Infof("poll wait %s", dur)
-	select {
-	case <-ctx.Done():
-		return ctx.Err()
-	case <-w.done:
-		return errors.New("poller closed")
-	case <-time.After(dur):
-		// trigger to load seqs, but loader might detect no seq updates.
-		return nil
-	}
-}
-
-func (w configMapBucketPoller) Close() error {
-	ctx := context.Background()
-	logger := log.FromContext(ctx)
-	logger.Infof("poller close")
-	close(w.done)
-	return nil
-}
-
-func (c ConfigMapBucket) configMap(ctx context.Context) (*cmdpb.ConfigMap, error) {
-	if c.ConfigMapFile == "" {
-		return proto.Clone(c.ConfigMap).(*cmdpb.ConfigMap), nil
-	}
-	buf, err := ioutil.ReadFile(c.ConfigMapFile)
-	if err != nil {
-		return nil, err
-	}
-	err = prototext.Unmarshal(buf, c.ConfigMap)
-	if err != nil {
-		return nil, err
-	}
-	return proto.Clone(c.ConfigMap).(*cmdpb.ConfigMap), nil
-}
-
-func cloudStorageNotification(ctx context.Context, s stiface.Client, bucket string) (*storage.Notification, error) {
-	bkt := s.Bucket(bucket)
-	nm, err := bkt.Notifications(ctx)
-	if err != nil {
-		return nil, err
-	}
-	var notification *storage.Notification
-	for _, n := range nm {
-		// use default topic, created by
-		//  $ gsutil notification create -f json <bucket>
-		// json payload will be:
-		// https://cloud.google.com/storage/docs/json_api/v1/objects#resource-representations
-		// we don't use json payload, so '-f none' is ok too.
-		if n.TopicID == bucket {
-			notification = n
-			break
-		}
-	}
-	if notification == nil {
-		return nil, fmt.Errorf("notification:%s not found in %v", bucket, nm)
-	}
-	return notification, nil
-}
-
-var storageNotification = cloudStorageNotification
-
-func (c ConfigMapBucket) Watcher(ctx context.Context) ConfigMapWatcher {
-	logger := log.FromContext(ctx)
-	w, err := c.pubsubWatcher(ctx)
-	if err == nil {
-		stats.Record(ctx, pubsubErrors.M(0))
-		logger.Infof("use pubsub watcher")
-		return w
-	}
-	stats.Record(ctx, pubsubErrors.M(1))
-	logger.Errorf("failed to use pubsub watcher: %v", err)
-	return configMapBucketPoller{
-		baseDelay: 1 * time.Hour,
-		done:      make(chan bool),
-	}
-}
-
-func (c ConfigMapBucket) pubsubWatcher(ctx context.Context) (ConfigMapWatcher, error) {
-	bucket, _, err := splitGCSPath(c.URI)
-	if err != nil {
-		return nil, err
-	}
-	logger := log.FromContext(ctx)
-	notification, err := storageNotification(ctx, c.StorageClient, bucket)
-	if err != nil {
-		return nil, err
-	}
-	logger.Infof("topic: %s in %s", notification.TopicID, notification.TopicProjectID)
-	topic := c.PubsubClient.TopicInProject(notification.TopicID, notification.TopicProjectID)
-	ok, err := topic.Exists(ctx)
-	if !ok || err != nil {
-		return nil, fmt.Errorf("notification topic:%s (notification:%#v): not exist: %v", topic, notification, err)
-	}
-	if c.SubscriberID == "" {
-		return nil, errors.New("SubscriberID is not specified")
-	}
-	subscription := c.PubsubClient.Subscription(c.SubscriberID)
-	ok, err = subscription.Exists(ctx)
-	if err != nil {
-		return nil, fmt.Errorf("subscription:%s err:%v", c.SubscriberID, err)
-	}
-	if ok {
-		sc, err := subscription.Config(ctx)
-		if err != nil {
-			return nil, fmt.Errorf("subscription config:%s err:%v", c.SubscriberID, err)
-		}
-		if sc.Topic.String() != topic.String() {
-			return nil, fmt.Errorf("topic mismatch? %s != %s. delete subscription:%s", sc.Topic, topic, c.SubscriberID)
-		}
-	} else {
-		logger.Infof("subscriber:%s not found. creating", c.SubscriberID)
-		subscription, err = c.PubsubClient.CreateSubscription(ctx, c.SubscriberID, pubsub.SubscriptionConfig{
-			Topic: topic,
-			// experimental config.
-			// minimum is 1 day
-			// +12 hours margin, to cover summar time switch (+1 hour)
-			// b/112820308
-			ExpirationPolicy: 36 * time.Hour,
-		})
-		if err != nil {
-			return nil, fmt.Errorf("create subscription:%s err:%v", c.SubscriberID, err)
-		}
-	}
-	ctx, cancel := context.WithCancel(context.Background())
-	// TODO: watch configMapFile.
-	w := configMapBucketWatcher{
-		s:      subscription,
-		cancel: cancel,
-		ch:     make(chan *pubsub.Message),
-	}
-	go w.run(ctx)
-	return w, nil
-}
-
-func (c ConfigMapBucket) Seqs(ctx context.Context) (map[string]string, error) {
-	logger := log.FromContext(ctx)
-	bucket, _, err := splitGCSPath(c.URI)
-	if err != nil {
-		return nil, err
-	}
-	cm, err := c.configMap(ctx)
-	if err != nil {
-		return nil, err
-	}
-	m := map[string]string{}
-	for _, r := range cm.Runtimes {
-		obj := path.Join(r.Name, "seq")
-		buf, err := storageReadAll(ctx, c.StorageClient, bucket, obj)
-		if err == storage.ErrObjectNotExist {
-			logger.Infof("ignore %s: %v", obj, err)
-			continue
-		}
-		if err != nil {
-			return nil, err
-		}
-		m[r.Name] = string(buf)
-	}
-	return m, nil
-}
-
-func (c ConfigMapBucket) Bucket(ctx context.Context) (string, error) {
-	bucket, _, err := splitGCSPath(c.URI)
-	return bucket, err
-}
-
-func (c ConfigMapBucket) RuntimeConfigs(ctx context.Context) (map[string]*cmdpb.RuntimeConfig, error) {
-	cm, err := c.configMap(ctx)
-	if err != nil {
-		return nil, err
-	}
-	m := make(map[string]*cmdpb.RuntimeConfig)
-	for _, rt := range cm.Runtimes {
-		if rt.ServiceAddr == "" {
-			rt.ServiceAddr = c.RemoteexecAddr
-		}
-		m[rt.Name] = rt
-	}
-	return m, nil
-}
-
-// ConfigLoader loads toolchain_config from cloud storage.
-type ConfigLoader struct {
-	StorageClient  stiface.Client
-	EnableParallel bool
-
-	// for test
-	versionID func() string
-}
-
-// ConfigStore holds latest config.
-type ConfigStore struct {
-	lastConfigs map[string]configs // key: toolchain_runtime_name
-
-	// for test
-	versionID func() string
-}
-
-type configs struct {
-	seq     string
-	configs []*cmdpb.Config
-}
-
-// ErrNoUpdate indicates no update in configmap, returned by ConfigMapLoader.Load.
-var ErrNoUpdate = errors.New("toolchain: configmap no update")
-
-// Load loads toolchain config.
-// It will return ErrNoUpdate if there is no seq change when force=false.
-func (c *ConfigMapLoader) Load(ctx context.Context, force bool) (*cmdpb.ConfigResp, error) {
-	logger := log.FromContext(ctx)
-	defer logger.Sync()
-
-	updated := make(map[string]string)
-	deleted := make(map[string]bool)
-	for _, k := range c.ConfigStore.List() {
-		deleted[k] = true
-	}
-	seqs, err := c.ConfigMap.Seqs(ctx)
-	if err != nil {
-		return nil, err
-	}
-	for name, seq := range seqs {
-		delete(deleted, name)
-		oseq := c.ConfigStore.Seq(name)
-		if oseq != seq {
-			updated[name] = seq
-		}
-	}
-	if len(updated) == 0 && len(deleted) == 0 {
-		if !force {
-			return nil, ErrNoUpdate
-		}
-		logger.Infof("configmap no update, but force to load")
-	}
-	for name := range deleted {
-		logger.Infof("delete config for %s", name)
-		c.ConfigStore.Delete(name)
-	}
-	bucket, err := c.ConfigMap.Bucket(ctx)
-	if err != nil {
-		return nil, err
-	}
-	runtimeConfigs, err := c.ConfigMap.RuntimeConfigs(ctx)
-	if err != nil {
-		return nil, err
-	}
-	logger.Infof("RuntimeConfigs: %v", runtimeConfigs)
-
-	for name, seq := range updated {
-		logger.Infof("update config for %s", name)
-		uri := fmt.Sprintf("gs://%s/%s", bucket, name)
-		runtime := runtimeConfigs[name]
-		if runtime == nil {
-			return nil, fmt.Errorf("runtime config %s not found", name)
-		}
-		addr := runtime.ServiceAddr
-		if addr == "" {
-			logger.Warnf("no addr for %s. ignoring", name)
-			continue
-		}
-		confs, err := c.ConfigLoader.Load(ctx, uri, runtime)
-		if err != nil {
-			return nil, err
-		}
-		c.ConfigStore.Set(name, seq, confs)
-	}
-	resp := c.ConfigStore.ConfigResp()
-	logger.Infof("config version: %s", resp.VersionId)
-	return resp, nil
-}
-
-// merge platform's properties into rbePlatform's properties,
-// unless property exists in rbePlatform,
-func mergePlatformProperties(rbePlatform *cmdpb.RemoteexecPlatform, platform *cmdpb.Platform) {
-	if platform == nil {
-		return
-	}
-	m := make(map[string]bool)
-	for _, p := range rbePlatform.Properties {
-		m[p.Name] = true
-	}
-	for _, p := range platform.Properties {
-		if m[p.Name] {
-			continue
-		}
-		rbePlatform.Properties = append(rbePlatform.Properties, &cmdpb.RemoteexecPlatform_Property{
-			Name:  p.Name,
-			Value: p.Value,
-		})
-	}
-}
-
-// Load loads toolchain config from <uri>.
-// 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,
-			Value: p.Value,
-		})
-	}
-	platform.HasNsjail = rc.GetPlatformRuntimeConfig().GetHasNsjail()
-
-	confs, err := loadConfigs(ctx, c.StorageClient, uri, rc, platform, parallel)
-	if err != nil {
-		return nil, err
-	}
-
-	// If this runtime config can support arbitrary toolchain support,
-	// also add a config for that. Just define RemoteexecPlatform here.
-	// CmdDescriptor will be dynamically generated by a compile request.
-	if rc.PlatformRuntimeConfig != nil {
-		confs = append(confs, &cmdpb.Config{
-			RemoteexecPlatform: platform,
-			Dimensions:         rc.PlatformRuntimeConfig.Dimensions,
-			Acl:                rc.Acl,
-		})
-	}
-
-	return confs, nil
-}
-
-// List returns a list of config names.
-func (c *ConfigStore) List() []string {
-	var names []string
-	for k := range c.lastConfigs {
-		names = append(names, k)
-	}
-	sort.Strings(names)
-	return names
-}
-
-// Set sets name's confs with seq.
-func (c *ConfigStore) Set(name, seq string, confs []*cmdpb.Config) {
-	if c.lastConfigs == nil {
-		c.lastConfigs = make(map[string]configs)
-	}
-	c.lastConfigs[name] = configs{
-		seq:     seq,
-		configs: confs,
-	}
-}
-
-// Seq returns seq of name's config.
-func (c *ConfigStore) Seq(name string) string {
-	return c.lastConfigs[name].seq
-}
-
-// Delete deletes name's config.
-func (c *ConfigStore) Delete(name string) {
-	delete(c.lastConfigs, name)
-}
-
-func versionID() string {
-	return time.Now().UTC().Format(time.RFC3339)
-}
-
-// ConfigResp returns current ConfigResp.
-func (c *ConfigStore) ConfigResp() *cmdpb.ConfigResp {
-	if c.versionID == nil {
-		c.versionID = versionID
-	}
-
-	var names []string
-	for name := range c.lastConfigs {
-		names = append(names, name)
-	}
-	sort.Strings(names)
-	resp := &cmdpb.ConfigResp{
-		VersionId: c.versionID(),
-	}
-	for _, name := range names {
-		confs := c.lastConfigs[name]
-		// TODO: dedup?
-		resp.Configs = append(resp.Configs, confs.configs...)
-	}
-	return resp
-}
-
-func splitGCSPath(uri string) (string, string, error) {
-	if !strings.HasPrefix(uri, "gs://") {
-		return "", "", fmt.Errorf("not gs: URI: %q", uri)
-	}
-	p := strings.SplitN(uri[len("gs://"):], "/", 2)
-	if len(p) != 2 {
-		return p[0], "", nil
-	}
-	return p[0], p[1], nil
-}
-
-func storageReadAll(ctx context.Context, client stiface.Client, bucket, name string) ([]byte, error) {
-	bkt := client.Bucket(bucket)
-	if bkt == nil {
-		return nil, fmt.Errorf("could not find bucket %s", bucket)
-	}
-	obj := bkt.Object(name)
-	if obj == nil {
-		return nil, fmt.Errorf("could not find object %s/%s", bucket, name)
-	}
-	rd, err := obj.NewReader(ctx)
-	if err != nil {
-		return nil, err
-	}
-	defer rd.Close()
-	var buf bytes.Buffer
-	buf.Grow(int(rd.Size()))
-	_, err = buf.ReadFrom(rd)
-	if err != nil {
-		return nil, err
-	}
-	return buf.Bytes(), nil
-}
-
-func loadDescriptor(ctx context.Context, client stiface.Client, bucket, name string) (*cmdpb.CmdDescriptor, error) {
-	buf, err := storageReadAll(ctx, client, bucket, name)
-	if err != nil {
-		return nil, fmt.Errorf("load %s: %v", name, err)
-	}
-	d := &cmdpb.CmdDescriptor{}
-	err = proto.Unmarshal(buf, d)
-	if err != nil {
-		return nil, fmt.Errorf("parse %s: %v", name, err)
-	}
-	return d, nil
-}
-
-func checkPrebuilt(rc *cmdpb.RuntimeConfig, objName string) error {
-	// objName will be <runtime>/<prebuilts>/descriptors/<hash>
-	i := strings.Index(objName, "/descriptors")
-	if i < 0 {
-		return fmt.Errorf("no prebuilt dir: %s", objName)
-	}
-	prebuiltName := path.Base(objName[:i])
-	for _, prefix := range rc.DisallowedPrebuilts {
-		if strings.HasPrefix(prebuiltName, prefix) {
-			return fmt.Errorf("disallowed prebuilt %s: by %s", objName, prefix)
-		}
-	}
-	if len(rc.AllowedPrebuilts) == 0 {
-		return nil
-	}
-	allowed := false
-	for _, prefix := range rc.AllowedPrebuilts {
-		if strings.HasPrefix(prebuiltName, prefix) {
-			allowed = true
-			break
-		}
-	}
-	if !allowed {
-		return fmt.Errorf("not allowed prebuilt %s", objName)
-	}
-	return nil
-}
-
-func checkSelector(rc *cmdpb.RuntimeConfig, sel *cmdpb.Selector) error {
-	if sel == nil {
-		return errors.New("no selector specified")
-	}
-	for _, s := range rc.DisallowedCommands {
-		if s.Name != "" && s.Name == sel.Name {
-			return fmt.Errorf("%s: disallowed by name: %s", sel, s.Name)
-		}
-		if s.Version != "" && s.Version == sel.Version {
-			return fmt.Errorf("%s: disallowed by version: %s", sel, s.Version)
-		}
-		if s.Target != "" && s.Target == sel.Target {
-			return fmt.Errorf("%s: disallowed by target: %s", sel, s.Target)
-		}
-		if s.BinaryHash != "" && s.BinaryHash == sel.BinaryHash {
-			return fmt.Errorf("%s: disallowed by binary hash: %s", sel, s.BinaryHash)
-		}
-	}
-	return nil
-}
-
-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 {
-		return nil, err
-	}
-
-	bkt := client.Bucket(bucket)
-	if bkt == nil {
-		return nil, fmt.Errorf("could not find storage bucket %s", bucket)
-	}
-	iter := bkt.Objects(ctx, &storage.Query{
-		Prefix: obj,
-	})
-
-	// pagination?
-	var confs []*cmdpb.Config
-	logger.Infof("load from %s prefix:%s", bucket, obj)
-	start := time.Now()
-	var attrsList []*storage.ObjectAttrs
-	for {
-		// iter does not have an API to read all, so just iterate everything.
-		// iter may not get all objects matched around storage@v1.15.0
-		// https://github.com/googleapis/google-cloud-go/issues/4676
-		attrs, err := iter.Next()
-		if err == iterator.Done {
-			break
-		}
-		if err != nil {
-			return nil, err
-		}
-		// 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
-		}
-
-		if path.Base(path.Dir(attrs.Name)) != "descriptors" {
-			logger.Infof("ignore %s", attrs.Name)
-			continue
-		}
-		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 := timestamppb.New(attrs.Updated)
-			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,
-				Acl:                rc.Acl,
-			}
-			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
-		}
-		confs = append(confs, conf)
-		logger.Infof("%s/%s: %s", bucket, attrs.Name, conf.CmdDescriptor.GetSelector())
-	}
-	logger.Infof("loaded from %s prefix:%s: %d configs using %v", bucket, obj, len(confs), time.Since(start))
-	return confs, nil
-}
diff --git a/command/descriptor/descriptor.go b/command/descriptor/descriptor.go
deleted file mode 100644
index 13725c9..0000000
--- a/command/descriptor/descriptor.go
+++ /dev/null
@@ -1,756 +0,0 @@
-// Copyright 2017 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.
-
-package descriptor
-
-import (
-	"bufio"
-	"bytes"
-	"errors"
-	"fmt"
-	"os"
-	"path/filepath"
-	"regexp"
-	"strings"
-
-	"go.chromium.org/goma/server/hash"
-	pb "go.chromium.org/goma/server/proto/command"
-)
-
-var (
-	// subprogs is subprograms for compiler.
-	// CompilerInfoBuilder::LookupAndUpdate.
-	subprogs = []string{
-		"cpp", "cc1", "cc1plus", "as", "objcopy",
-		"ld", "nm", "strip", "cc1obj", "collect2",
-		"lto1", "lto-wrapper",
-	}
-
-	clexeVersionRegexp = regexp.MustCompile(`Compiler Version ([0-9\.]+) for`)
-	clexeTargetRegexp  = regexp.MustCompile(`Compiler Version [0-9\.]+ for (x\d+)`)
-
-	clangclExpectedVersionRegexp = regexp.MustCompile(`clang\s+version\s+\d+\.\d+\.\d+\S*\s+\([^)]*\)`)
-	clangclExpectedTargetRegexp  = regexp.MustCompile(`Target:\s+\.*`)
-)
-
-// Descriptor represents a command descriptor.
-type Descriptor struct {
-	// fname is command filename in server path. relative to cwd (prefix).
-	fname string
-	*pb.CmdDescriptor
-
-	Runner       Runner
-	ToClientPath func(string) (string, error)
-	PathType     pb.CmdDescriptor_PathType
-
-	cwd    string // current working directory in server path.
-	cmddir string // abs command directory in server path.
-	seen   map[string]bool
-}
-
-// Config used for setting up cmd descriptor.
-type Config struct {
-	Key                    string // Key used for selection.
-	Filename               string // relative to pwd (prefix) in server path
-	AbsoluteBinaryHashFrom string // use Filename for binary hash if emtpy
-	Target                 string // cross target if specified
-
-	Runner       Runner
-	ToClientPath func(string) (string, error)
-	PathType     pb.CmdDescriptor_PathType
-
-	// compiler specific
-	// https://clang.llvm.org/docs/CrossCompilation.html#target-triple
-	ClangNeedTarget bool // true if -target is needed.
-	WindowsCross    bool
-}
-
-// Runner runs command and get combined output.
-type Runner func(cmds ...string) ([]byte, error)
-
-func (c Config) binaryHashFrom() string {
-	if c.AbsoluteBinaryHashFrom != "" {
-		return c.AbsoluteBinaryHashFrom
-	}
-	return c.Filename
-}
-
-// canGetTargetFromFilename returns true if target can be get from c.Filename.
-// if c.AbsoluteBinaryHashFrom is given, we suppose that target cannot be get
-// from either of Filename or AbsoluteBinaryHashFrom, the function returns
-// false.
-func (c Config) canGetTargetFromFilename() bool {
-	return c.AbsoluteBinaryHashFrom == ""
-}
-
-func (c Config) isValid() bool {
-	if c.Filename == "" {
-		return false
-	}
-	// if Target is set, we expect the config is for cross compiling.
-	// then, we require the config have AbsoluteBinaryHashFrom.
-	// subprogram/plugin may have AbsoluteBinaryHashFrom but they do not
-	// have Target.
-	return c.Target == "" || c.AbsoluteBinaryHashFrom != ""
-}
-
-func cmdDescriptor(c Config) (*pb.CmdDescriptor, error) {
-	sel, err := selector(c)
-	if err != nil {
-		return nil, err
-	}
-	cd := &pb.CmdDescriptor{
-		Selector:      sel,
-		Cross:         &pb.CmdDescriptor_Cross{},
-		EmulationOpts: &pb.CmdDescriptor_EmulationOpts{},
-	}
-
-	if c.ClangNeedTarget {
-		switch sel.Name {
-		case "clang", "clang++", "clang-cl":
-			cd.Cross.ClangNeedTarget = true
-		default:
-			return nil, fmt.Errorf("need_target=true for non clang: %s", sel.Name)
-		}
-	}
-	if c.WindowsCross {
-		switch sel.Name {
-		case "clang-cl", "clang", "clang++":
-			// clang, clang++ for pnacl-clang
-			cd.Cross.WindowsCross = true
-		default:
-			// gcc, g++ for nacl-gcc but we disable them in client side due to ELF-32
-			return nil, fmt.Errorf("windows_cross=true is not supported for %s", sel.Name)
-		}
-	}
-
-	// When filename starts with "/", it means the binary is specified with absolute path.
-	// It implies the binary is not relocatable.
-	// In this case, we need to respect include paths sent from the goma client,
-	// since the system include paths can be different (it's often based on compiler path).
-	if strings.HasPrefix(c.Filename, "/") {
-		cd.EmulationOpts.RespectClientIncludePaths = true
-	}
-
-	return cd, nil
-}
-
-// New creates cmd descriptor for c.
-func New(c Config) (*Descriptor, error) {
-	if !c.isValid() {
-		return nil, fmt.Errorf("invalid config %v", c)
-	}
-	cd, err := cmdDescriptor(c)
-	if err != nil {
-		return nil, err
-	}
-
-	return &Descriptor{
-		fname:         c.Filename,
-		CmdDescriptor: cd,
-		Runner:        c.Runner,
-		ToClientPath:  c.ToClientPath,
-		PathType:      c.PathType,
-		seen:          make(map[string]bool),
-	}, nil
-}
-
-// filespec is file spec represetned in server path.
-type filespec struct {
-	Path         string // in server path.
-	Symlink      string
-	Hash         string
-	Size         int64
-	IsExecutable bool
-}
-
-// newFilespec creates filespec from fname in server path.
-func newFilespec(fname string) (filespec, error) {
-	fi, err := os.Lstat(fname)
-	if err != nil {
-		return filespec{}, err
-	}
-	if fi.Mode()&os.ModeSymlink == os.ModeSymlink {
-		linkname, err := os.Readlink(fname)
-		if err != nil {
-			return filespec{}, err
-		}
-		return filespec{
-			Path:    fname,
-			Symlink: linkname,
-		}, nil
-	}
-	sha256, err := hash.SHA256File(fname)
-	if err != nil {
-		return filespec{}, err
-	}
-	return filespec{
-		Path:         fname,
-		Hash:         sha256,
-		Size:         fi.Size(),
-		IsExecutable: fi.Mode()&0111 != 0,
-	}, nil
-}
-
-func (d *Descriptor) filespecProto(fs filespec) (*pb.FileSpec, error) {
-	cpath, err := d.ToClientPath(fs.Path)
-	if err != nil {
-		return nil, err
-	}
-	return &pb.FileSpec{
-		Path:         cpath,
-		Symlink:      fs.Symlink,
-		Hash:         fs.Hash,
-		Size:         fs.Size,
-		IsExecutable: fs.IsExecutable,
-	}, nil
-}
-
-// appendFileSpec appends filespec to Setup.Files.
-func (d *Descriptor) appendFileSpec(fs filespec) error {
-	p := fs.Path
-	if !filepath.IsAbs(p) {
-		p = filepath.Join(d.cmddir, p)
-	}
-	if d.seen[p] {
-		return nil
-	}
-	cfs, err := d.filespecProto(fs)
-	if err != nil {
-		return err
-	}
-	d.Setup.Files = append(d.Setup.Files, cfs)
-	d.seen[p] = true
-	return nil
-}
-
-// CmdSetup sets up command descriptor.
-func (d *Descriptor) CmdSetup() error {
-	if !filepath.IsAbs(d.fname) {
-		return d.relocCmdSetup()
-	}
-	fs, err := newFilespec(d.fname)
-	if err != nil {
-		return err
-	}
-	if fs.Symlink == "" && !fs.IsExecutable {
-		return fmt.Errorf("not executable: %s", d.fname)
-	}
-	cfs, err := d.filespecProto(fs)
-	if err != nil {
-		return err
-	}
-	d.Setup = &pb.CmdDescriptor_Setup{
-		CmdFile:  cfs,
-		PathType: d.PathType,
-	}
-	d.seen[fs.Path] = true
-	if fs.Symlink != "" {
-		err = d.resolveSymlinks("", fs)
-		if err != nil {
-			return err
-		}
-	}
-	return nil
-}
-
-func (d *Descriptor) relocCmdSetup() error {
-	cwd, err := os.Getwd()
-	if err != nil {
-		return err
-	}
-	d.cwd = cwd
-	fs, err := newFilespec(d.fname)
-	if err != nil {
-		return err
-	}
-	if fs.Symlink == "" && !fs.IsExecutable {
-		return fmt.Errorf("not executable: %s", d.fname)
-	}
-	cfs, err := d.filespecProto(fs)
-	if err != nil {
-		return err
-	}
-	ccwd, err := d.ToClientPath(cwd)
-	if err != nil {
-		return err
-	}
-	d.Setup = &pb.CmdDescriptor_Setup{
-		CmdFile:  cfs,
-		CmdDir:   ccwd,
-		PathType: d.PathType,
-	}
-	fname := filepath.Join(cwd, d.fname)
-	d.seen[fname] = true
-	d.cmddir = filepath.Dir(fname)
-
-	fs.Path = fname
-	err = d.resolveSymlinks(d.cmddir, fs)
-	if err != nil {
-		return err
-	}
-	// ignore error, since fname may not be ELF executable.
-	_ = d.relocatableLibraries(fname)
-
-	if d.Setup.PathType == pb.CmdDescriptor_WINDOWS {
-		// don't detect subprograms for windows clang
-		// (e.g. pnacl-clang), because compilePath doesn't work.
-		// set them in files explicitly.
-		// TODO: fix?
-		return nil
-	}
-
-	// Find subprograms.
-	var langopt string
-	switch d.CmdDescriptor.Selector.Name {
-	case "gcc", "clang":
-		langopt = "-xc"
-	case "g++", "clang++":
-		langopt = "-xc++"
-	default:
-		return nil
-	}
-	cpaths, err := compilerPath(d.fname, langopt, d.Runner)
-	if err != nil {
-		return err
-	}
-	for _, sp := range subprogs {
-		spath, err := lookpath(sp, cpaths)
-		if err == nil {
-			// relative to cmd
-			fs, err := newFilespec(spath)
-			if err != nil {
-				return err
-			}
-			rel, err := filepath.Rel(d.cmddir, fs.Path)
-			if err != nil {
-				return err
-			}
-			fs.Path = rel
-			err = d.appendFileSpec(fs)
-			if err != nil {
-				return err
-			}
-			err = d.resolveSymlinks(d.cmddir, fs)
-			if err != nil {
-				return err
-			}
-			err = d.relocatableLibraries(spath)
-			if err != nil {
-				return err
-			}
-		}
-
-		// TODO: don't put system default subprograms
-		// in cmd descriptor, but put in runtime container image?
-		spath, err = lookpath(sp, []string{"/usr/bin", "/bin"})
-		if err == nil {
-			// abspath
-			fs, err := newFilespec(spath)
-			if err != nil {
-				return err
-			}
-			err = d.appendFileSpec(fs)
-			if err != nil {
-				return err
-			}
-			err = d.resolveSymlinks("", fs)
-			if err != nil {
-				return err
-			}
-			// assume libraries are in system library dir
-			// so no need to setup at each compilation.
-		}
-	}
-	return nil
-}
-
-func (d *Descriptor) resolveSymlinks(basedir string, fs filespec) error {
-	if fs.Symlink == "" {
-		return nil
-	}
-	if basedir != "" && !filepath.IsAbs(fs.Path) {
-		fs.Path = filepath.Join(basedir, fs.Path)
-	}
-	_, err := os.Stat(fs.Path)
-	if err != nil {
-		return err
-	}
-	for fs.Symlink != "" {
-		sname := fs.Symlink
-		if !filepath.IsAbs(fs.Symlink) {
-			sname = filepath.Join(filepath.Dir(fs.Path), fs.Symlink)
-		}
-		fs, err = newFilespec(sname)
-		if err != nil {
-			return err
-		}
-		if basedir != "" {
-			rel, err := filepath.Rel(basedir, fs.Path)
-			if err != nil {
-				return err
-			}
-			fs.Path = rel
-		}
-		err = d.appendFileSpec(fs)
-		if err != nil {
-			return err
-		}
-		fs.Path = filepath.Join(basedir, fs.Path)
-	}
-	return nil
-}
-
-func (d *Descriptor) relocatableLibraries(cmdpath string) error {
-	lpaths, err := relocatableLibraries(cmdpath)
-	if err != nil {
-		return err
-	}
-	for _, lpath := range lpaths {
-		fs, err := newFilespec(lpath)
-		if err != nil {
-			return err
-		}
-		rel, err := filepath.Rel(d.cmddir, fs.Path)
-		if err != nil {
-			return err
-		}
-		fs.Path = rel
-		err = d.appendFileSpec(fs)
-		if err != nil {
-			return err
-		}
-		err = d.resolveSymlinks(d.cmddir, fs)
-		if err != nil {
-			return err
-		}
-	}
-	return nil
-}
-
-// Add adds fname for command.
-// fname should be absolute path, or relative to cwd.
-// It will emit relative path to command if cmd is relocatable.
-// It just adds fname as is. i.e. not resolve symlink, resolve shared obj etc.
-func (d *Descriptor) Add(fname string) error {
-	if !filepath.IsAbs(fname) {
-		fname = filepath.Join(d.cwd, fname)
-	}
-	fs, err := newFilespec(fname)
-	if err != nil {
-		return err
-	}
-	if !filepath.IsAbs(d.fname) {
-		// relocatable. fname should be relative to cmd dir.
-		rel, err := filepath.Rel(d.cmddir, fs.Path)
-		if err != nil {
-			return err
-		}
-		fs.Path = rel
-	}
-	return d.appendFileSpec(fs)
-}
-
-func lookpath(fname string, paths []string) (string, error) {
-	for _, path := range paths {
-		name := filepath.Join(path, fname)
-		_, err := os.Stat(name)
-		if err != nil {
-			continue
-		}
-		return name, nil
-	}
-	return "", fmt.Errorf("not found %s in %v", fname, paths)
-}
-
-func compilerPath(fname, langopt string, runner Runner) ([]string, error) {
-	cmds := []string{fname, "-E", "-v", langopt,
-		"/dev/null", "-o", "/dev/null"}
-	out, err := runner(cmds...)
-	if err != nil {
-		return nil, fmt.Errorf("compilerPath: %v: %v", cmds, err)
-	}
-	s := bufio.NewScanner(bytes.NewReader(out))
-	for s.Scan() {
-		line := s.Text()
-		if strings.HasPrefix(line, "COMPILER_PATH=") {
-			line = strings.TrimPrefix(line, "COMPILER_PATH=")
-			return strings.Split(line, ":"), nil
-		}
-	}
-	if err := s.Err(); err != nil {
-		return nil, err
-	}
-	return nil, err
-}
-
-// selector returns selector for c.
-func selector(c Config) (*pb.Selector, error) {
-	sha256, err := hash.SHA256File(c.binaryHashFrom())
-	if err != nil {
-		return nil, fmt.Errorf("binary hash for %s: %v", c.binaryHashFrom(), err)
-	}
-	var v, t string
-	switch c.Key {
-	case "gcc", "g++", "clang", "clang++":
-		v, err = version(c.Filename, c.Runner)
-		if err != nil {
-			return nil, fmt.Errorf("version %s [%s]: %v", c.Filename, sha256, err)
-		}
-		if !c.canGetTargetFromFilename() {
-			if c.Target == "" {
-				return nil, fmt.Errorf("target not given %v", c)
-			}
-			t = c.Target
-		} else {
-			t, err = target(c.Filename, c.Runner)
-			if err != nil {
-				return nil, fmt.Errorf("target %s [%s]: %v", c.Filename, sha256, err)
-			}
-		}
-	case "javac": // TODO: also java?
-		v, err = javaVersion(c.Filename, c.Runner)
-		if err != nil {
-			return nil, fmt.Errorf("version %s [%s]: %v", c.Filename, sha256, err)
-		}
-		// javac's target is set to java in goma client.
-		t = "java"
-	case "cl.exe":
-		v, err = clexeVersion(c.Filename, c.Runner)
-		if err != nil {
-			return nil, fmt.Errorf("version %s [%s]: %v", c.Filename, sha256, err)
-		}
-		t, err = clexeTarget(c.Filename, c.Runner)
-		if err != nil {
-			return nil, fmt.Errorf("target %s [%s]: %v", c.Filename, sha256, err)
-		}
-	case "clang-cl":
-		v, err = clangclVersion(c.Filename, c.Runner)
-		if err != nil {
-			return nil, fmt.Errorf("version %s [%s]: %v", c.Filename, sha256, err)
-		}
-		if !c.canGetTargetFromFilename() {
-			if c.Target == "" {
-				return nil, fmt.Errorf("target not given %v", c)
-			}
-			t = c.Target
-		} else {
-			t, err = clangclTarget(c.Filename, c.Runner)
-			if err != nil {
-				return nil, fmt.Errorf("target %s [%s]: %v", c.Filename, sha256, err)
-			}
-		}
-	case "dartanalyzer":
-		v, err = dartAnalyzerVersion(c.Filename, c.Runner)
-		if err != nil {
-			return nil, err
-		}
-		if c.Target == "" {
-			return nil, errors.New("missing target configuration for dartanalyzer")
-		}
-		t = c.Target
-
-	default:
-		// subprogram would not have version,target (?)
-	}
-	return &pb.Selector{
-		Name:       c.Key,
-		Version:    v,
-		Target:     t,
-		BinaryHash: sha256,
-	}, nil
-}
-
-func version(cmd string, runner Runner) (string, error) {
-	dout, err := runner(cmd, "-dumpversion")
-	if err != nil {
-		return "", fmt.Errorf("failed to take %s dump version: %v", cmd, err)
-	}
-	vout, err := runner(cmd, "--version")
-	if err != nil {
-		return "", fmt.Errorf("failed to take %s version: %v", cmd, err)
-	}
-	return Version(dout, vout), nil
-}
-
-// Version returns version field for command spec/selector
-// from -dumpversion output and --version output.
-func Version(dout, vout []byte) string {
-	dout = firstLine(dout)
-	vout = normalize(firstLine(vout))
-	return fmt.Sprintf("%s[%s]", dout, vout)
-}
-
-func normalize(v []byte) []byte {
-	i := bytes.IndexByte(v, '(')
-	if i < 0 {
-		return v
-	}
-	progname := v[:i]
-	if bytes.Contains(progname, []byte("clang")) {
-		return v
-	}
-	if !bytes.Contains(progname, []byte("g++")) && !bytes.Contains(progname, []byte("gcc")) {
-		return v
-	}
-	return v[i:]
-}
-
-func target(cmd string, runner Runner) (string, error) {
-	out, err := runner(cmd, "-dumpmachine")
-	if err != nil {
-		return "", fmt.Errorf("failed to take %s dump machine: %v", cmd, err)
-	}
-	return Target(out), nil
-}
-
-// Target returns target field of command spec/selector
-// from -dumpmachine output.
-func Target(out []byte) string {
-	return string(firstLine(out))
-}
-
-func firstLine(b []byte) []byte {
-	i := bytes.IndexAny(b, "\r\n")
-	if i >= 0 {
-		b = b[:i]
-	}
-	return b
-}
-
-func javaVersion(cmd string, runner Runner) (string, error) {
-	out, err := runner(cmd, "-version")
-	if err != nil {
-		return "", fmt.Errorf("failed to get version: %s: %s %v", cmd, out, err)
-	}
-	return JavaVersion(out)
-}
-
-// JavaVersion returns version string of javac.
-func JavaVersion(out []byte) (string, error) {
-	javac := []byte("javac ")
-	if !bytes.HasPrefix(out, javac) {
-		return "", fmt.Errorf("not starts with javac: %s", out)
-	}
-	return string(bytes.TrimPrefix(firstLine(out), javac)), nil
-}
-
-func clexeVersion(cmd string, runner Runner) (string, error) {
-	out, err := runner(cmd)
-	if err != nil {
-		return "", fmt.Errorf("failed to take cl.exe version: %v", err)
-	}
-	return ClexeVersion(out)
-}
-
-// ClexeVersion returns version string of CL.exe.
-func ClexeVersion(out []byte) (string, error) {
-	match := clexeVersionRegexp.FindStringSubmatch(string(out))
-	if match == nil {
-		return "", fmt.Errorf("could not find version string: %s", out)
-	}
-	if len(match) < 2 {
-		return "", fmt.Errorf("unexpected version string match: %s", out)
-	}
-	return match[1], nil
-}
-
-func clexeTarget(cmd string, runner Runner) (string, error) {
-	out, err := runner(cmd)
-	if err != nil {
-		return "", fmt.Errorf("failed to take cl.exe target: %v", err)
-	}
-	return ClexeTarget(out)
-}
-
-// ClexeTarget returns target string of CL.exe.
-func ClexeTarget(out []byte) (string, error) {
-	match := clexeTargetRegexp.FindStringSubmatch(string(out))
-	if match == nil {
-		return "", fmt.Errorf("could not find target string: %s", out)
-	}
-	if len(match) < 2 {
-		return "", fmt.Errorf("unexpected target string match: %s", out)
-	}
-	return match[1], nil
-}
-
-// ClangClVersion returns clang version from output of `clang-cl.exe -###`.
-//
-// `clang-cl -###` output is like the following:
-//
-//	clang version 3.5.0 (trunk 225621)
-//	Target: i686-pc-windows-msvc
-//
-//	clang version 6.0.0 (trunk 308728)
-//	Target: x86_64-pc-windows-msvc
-//
-//	clang version 9.0.0 (https://github.com/llvm/llvm-project/ 67510fac36d27b2e22c7cd955fc167136b737b93)
-//	Target: x86_64-pc-windows-msvc
-//	Thread model: posix
-//	InstalledDir: /var/tmp/clang/third_party/llvm-build/Release+Asserts/bin
-//
-//	clang version 18.0.0git (https://chromium.googlesource.com/a/external/github.com/llvm/llvm-project 7de53a8cfe45f60334dc3765c0bfc94beaf09883)
-//	Target: x86_64-pc-windows-msvc
-//	Thread model: posix
-//	InstalledDir: /var/tmp/clang/third_party/llvm-build/Release+Asserts/bin
-//
-// Note that endline might be CRLF.
-func ClangClVersion(out []byte) (string, error) {
-	lines := bytes.SplitN(out, []byte("\n"), 2)
-	if len(lines) >= 1 && clangclExpectedVersionRegexp.Match(lines[0]) {
-		return string(bytes.TrimSpace(lines[0])), nil
-	}
-
-	return "", fmt.Errorf("unexpected clang-cl output: %s", out)
-}
-
-func clangclVersion(cmd string, runner Runner) (string, error) {
-	out, err := runner(cmd, "-###")
-	if err != nil {
-		return "", fmt.Errorf("failed to take clang-cl version: %v", err)
-	}
-	return ClangClVersion(out)
-}
-
-// ClangClTarget returns clang target from output of `clang-cl.exe -###`
-// See ClangClVersion about the expected input.
-func ClangClTarget(out []byte) (string, error) {
-	lines := bytes.SplitN(out, []byte("\n"), 3)
-	if len(lines) >= 2 && clangclExpectedTargetRegexp.Match(lines[1]) {
-		s := lines[1][len("Target: "):]
-		return string(bytes.TrimSpace(s)), nil
-	}
-
-	return "", fmt.Errorf("unexpected clang-cl output: %s", out)
-}
-
-func clangclTarget(cmd string, runner Runner) (string, error) {
-	out, err := runner(cmd, "-###")
-	if err != nil {
-		return "", fmt.Errorf("failed to take clang-cl target: %v", err)
-	}
-	return ClangClTarget(out)
-}
-
-// DartAnalyzerVersion returns the dartanalyzer's version from
-// output of `dartanalyzer --version`
-func DartAnalyzerVersion(out []byte) (string, error) {
-	// output should be like
-	// `dartanalyzer version 2.1.1-dev.1.0`.
-	const dartanalyzerPrefix = "dartanalyzer version "
-	output := strings.TrimSpace(string(out))
-	if !strings.HasPrefix(output, dartanalyzerPrefix) {
-		return "", fmt.Errorf("failed to parse dartanalyzer version: %q", output)
-	}
-	return output[len(dartanalyzerPrefix):], nil
-}
-
-func dartAnalyzerVersion(cmd string, runner Runner) (string, error) {
-	out, err := runner(cmd, "--version")
-	if err != nil {
-		return "", fmt.Errorf("failed to take dartanalyzer version: %v", err)
-	}
-	return DartAnalyzerVersion(out)
-}
diff --git a/command/descriptor/descriptor_test.go b/command/descriptor/descriptor_test.go
deleted file mode 100644
index 54542b1..0000000
--- a/command/descriptor/descriptor_test.go
+++ /dev/null
@@ -1,434 +0,0 @@
-// Copyright 2017 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.
-
-package descriptor
-
-import (
-	"io/ioutil"
-	"os"
-	"path/filepath"
-	"reflect"
-	"testing"
-
-	pb "go.chromium.org/goma/server/proto/command"
-)
-
-func TestVersion(t *testing.T) {
-	for _, tc := range []struct {
-		dout, vout string
-		want       string
-	}{
-		{
-			dout: "4.8\n",
-			vout: `gcc (Ubuntu 4.8.4-2ubuntu1~14.04.3) 4.8.4
-Copyright (C) 2013 Free Software Foundation, Inc.
-This is free software; see the source for copying conditions.  There is NO
-warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-
-`,
-			want: "4.8[(Ubuntu 4.8.4-2ubuntu1~14.04.3) 4.8.4]",
-		},
-		{
-			dout: "4.2.1\n",
-			vout: `clang version 5.0.0 (trunk 305462)
-Target: x86_64-unknown-linux-gnu
-Thread model: posix
-InstalledDir: /home/goma/src/chromium-git/src/./third_party/llvm-build/Release+Asserts/bin
-
-  Registered Targets:
-    aarch64    - AArch64 (little endian)
-    aarch64_be - AArch64 (big endian)
-    amdgcn     - AMD GCN GPUs
-    arm        - ARM
-    arm64      - ARM64 (little endian)
-    armeb      - ARM (big endian)
-    bpf        - BPF (host endian)
-    bpfeb      - BPF (big endian)
-    bpfel      - BPF (little endian)
-    hexagon    - Hexagon
-    lanai      - Lanai
-    mips       - Mips
-    mips64     - Mips64 [experimental]
-    mips64el   - Mips64el [experimental]
-    mipsel     - Mipsel
-    msp430     - MSP430 [experimental]
-    nvptx      - NVIDIA PTX 32-bit
-    nvptx64    - NVIDIA PTX 64-bit
-    ppc32      - PowerPC 32
-    ppc64      - PowerPC 64
-    ppc64le    - PowerPC 64 LE
-    r600       - AMD GPUs HD2XXX-HD6XXX
-    riscv32    - 32-bit RISC-V
-    riscv64    - 64-bit RISC-V
-    sparc      - Sparc
-    sparcel    - Sparc LE
-    sparcv9    - Sparc V9
-    systemz    - SystemZ
-    thumb      - Thumb
-    thumbeb    - Thumb (big endian)
-    x86        - 32-bit X86: Pentium-Pro and above
-    x86-64     - 64-bit X86: EM64T and AMD64
-    xcore      - XCore
-
-`,
-			want: "4.2.1[clang version 5.0.0 (trunk 305462)]",
-		},
-		{
-			dout: "4.2.1\n",
-			vout: `clang version 3.7.0 (https://chromium.googlesource.com/a/native_client/pnacl-clang.git e34aaa1fd835c13131bf82b16b768253e14e9247) (https://chromium.googlesource.com/a/native_client/pnacl-llvm.git 63a5544b9bf92b8ed6af03eae595947de1761fef) nacl-version=efa3f5d8ef135ed2463a75ac4630d1c448021400
-Target: le32-unknown-nacl
-Thread model: posix
-
-`,
-			want: "4.2.1[clang version 3.7.0 (https://chromium.googlesource.com/a/native_client/pnacl-clang.git e34aaa1fd835c13131bf82b16b768253e14e9247) (https://chromium.googlesource.com/a/native_client/pnacl-llvm.git 63a5544b9bf92b8ed6af03eae595947de1761fef) nacl-version=efa3f5d8ef135ed2463a75ac4630d1c448021400]",
-		},
-	} {
-		got := Version([]byte(tc.dout), []byte(tc.vout))
-		if got != tc.want {
-			t.Errorf("Version(%q, %q)=%q; want=%q", tc.dout, tc.vout, got, tc.want)
-		}
-	}
-}
-
-func TestTarget(t *testing.T) {
-	for _, tc := range []struct {
-		out  string
-		want string
-	}{
-		{
-			out:  "x86_64-linux-gnu\n",
-			want: "x86_64-linux-gnu",
-		},
-		{
-			out:  "x86_64-unknown-linux-gnu\n",
-			want: "x86_64-unknown-linux-gnu",
-		},
-		{
-			out:  "le32-unknown-nacl\n",
-			want: "le32-unknown-nacl",
-		},
-	} {
-		got := Target([]byte(tc.out))
-		if got != tc.want {
-			t.Errorf("Target(%q)=%q; want=%q", tc.out, got, tc.want)
-		}
-	}
-}
-
-func TestJavaVersion(t *testing.T) {
-	for _, tc := range []struct {
-		out       string
-		want      string
-		wantError bool
-	}{
-		{
-			out:       "not javac",
-			wantError: true,
-		},
-		{
-			out:  "javac 1.8.0_45-internal\n",
-			want: "1.8.0_45-internal",
-		},
-	} {
-		got, err := JavaVersion([]byte(tc.out))
-		if tc.wantError && err == nil {
-			t.Errorf("JavaVersion(%q)=_,nil; want error", tc.out)
-		}
-		if !tc.wantError && err != nil {
-			t.Errorf("JavaVersion(%q)=_,%v; want nil", tc.out, err)
-		}
-		if got != tc.want {
-			t.Errorf("JavaVersion(%q)=%q; want=%q", tc.out, got, tc.want)
-		}
-	}
-}
-
-func TestClexeVersion(t *testing.T) {
-	// success case
-	for _, tc := range []struct {
-		out  string
-		want string
-	}{
-		{
-			out: `Microsoft (R) C/C++ Optimizing Compiler Version 19.11.25505 for x64
-Copyright (C) Microsoft Corporation.  All rights reserved.`,
-			want: "19.11.25505",
-		},
-		{
-			out: `Microsoft (R) C/C++ Optimizing Compiler Version 19.00.24215.1 for x64
-Copyright (C) Microsoft Corporation.  All rights reserved.`,
-			want: "19.00.24215.1",
-		},
-	} {
-		got, err := ClexeVersion([]byte(tc.out))
-		if err != nil {
-			t.Errorf("ClexeVersion(%q)=_,%v; want nil", tc.out, err)
-		}
-		if got != tc.want {
-			t.Errorf("ClexeVersion(%q)=%q; want=%q", tc.out, got, tc.want)
-		}
-	}
-
-	// failure case
-	for _, tc := range []string{
-		"",
-		"Copyright (C) Microsoft Corporation.  All rights reserved.",
-		"Microsoft (R) C/C++ Optimizing Compiler Version 19x00x24215x1 for x64",
-		`clang version 3.7.0 (https://chromium.googlesource.com/a/native_client/pnacl-clang.git e34aaa1fd835c13131bf82b16b768253e14e9247) (https://chromium.googlesource.com/a/native_client/pnacl-llvm.git 63a5544b9bf92b8ed6af03eae595947de1761fef) nacl-version=efa3f5d8ef135ed2463a75ac4630d1c448021400
-Target: le32-unknown-nacl
-Thread model: posix`,
-	} {
-		_, err := ClexeVersion([]byte(tc))
-		if err == nil {
-			t.Errorf("ClexeVersion(%q)=_,nil; want error", tc)
-		}
-	}
-}
-
-func TestClexeTarget(t *testing.T) {
-	// success case
-	for _, tc := range []struct {
-		out  string
-		want string
-	}{
-		{
-			out: `Microsoft (R) C/C++ Optimizing Compiler Version 19.11.25505 for x64
-Copyright (C) Microsoft Corporation.  All rights reserved.`,
-			want: "x64",
-		},
-		{
-			out: `Microsoft (R) C/C++ Optimizing Compiler Version 19.11.25505 for x86
-Copyright (C) Microsoft Corporation.  All rights reserved.`,
-			want: "x86",
-		},
-		{
-			out: `Microsoft (R) C/C++ Optimizing Compiler Version 19.00.24215.1 for x64
-Copyright (C) Microsoft Corporation.  All rights reserved.`,
-			want: "x64",
-		},
-	} {
-		got, err := ClexeTarget([]byte(tc.out))
-		if err != nil {
-			t.Errorf("ClexeTarget(%q)=_,%v; want nil", tc.out, err)
-		}
-		if got != tc.want {
-			t.Errorf("ClexeTarget(%q)=%q; want=%q", tc.out, got, tc.want)
-		}
-	}
-
-	// failure case
-	for _, tc := range []string{
-		"",
-		"Copyright (C) Microsoft Corporation.  All rights reserved.",
-		"Microsoft (R) C/C++ Optimizing Compiler Version 19x00x24215x1 for x64",
-		`clang version 3.7.0 (https://chromium.googlesource.com/a/native_client/pnacl-clang.git e34aaa1fd835c13131bf82b16b768253e14e9247) (https://chromium.googlesource.com/a/native_client/pnacl-llvm.git 63a5544b9bf92b8ed6af03eae595947de1761fef) nacl-version=efa3f5d8ef135ed2463a75ac4630d1c448021400
-Target: le32-unknown-nacl
-Thread model: posix`,
-	} {
-		_, err := ClexeTarget([]byte(tc))
-		if err == nil {
-			t.Errorf("ClexeTarget(%q)=_,nil; want error", tc)
-		}
-	}
-}
-
-func TestClangClVersion(t *testing.T) {
-	// success case
-	for _, tc := range []struct {
-		out  string
-		want string
-	}{
-		{
-			out:  "clang version 6.0.0 (trunk 308728)\nTarget: x86_64-pc-windows-msvc\n",
-			want: "clang version 6.0.0 (trunk 308728)",
-		},
-		{
-			out:  "clang version 3.5.0 (trunk 225621)\r\nTarget: i686-pc-windows-msvc\r\n",
-			want: "clang version 3.5.0 (trunk 225621)",
-		},
-		{
-			out:  "clang version 3.5.0 (trunk 225621)\r\nTarget: i686-pc-windows-msvc",
-			want: "clang version 3.5.0 (trunk 225621)",
-		},
-		{
-			out: `clang version 9.0.0 (https://github.com/llvm/llvm-project/ 67510fac36d27b2e22c7cd955fc167136b737b93)
-Target: x86_64-pc-windows-msvc
-Thread model: posix
-InstalledDir: /var/tmp/clang/third_party/llvm-build/Release+Asserts/bin
-`,
-			want: "clang version 9.0.0 (https://github.com/llvm/llvm-project/ 67510fac36d27b2e22c7cd955fc167136b737b93)",
-		},
-		{
-			out:  "clang version 9.0.0 (https://github.com/llvm/llvm-project/ 67510fac36d27b2e22c7cd955fc167136b737b93)\r\nTarget: x86_64-pc-windows-msvc\r\nThread model: posix\r\nInstalledDir: /var/tmp/clang/third_party/llvm-build/Release+Asserts/bin\r\n",
-			want: "clang version 9.0.0 (https://github.com/llvm/llvm-project/ 67510fac36d27b2e22c7cd955fc167136b737b93)",
-		},
-	} {
-		got, err := ClangClVersion([]byte(tc.out))
-		if err != nil {
-			t.Errorf("ClangClVersion(%q)=_,%v; want nil", tc.out, err)
-		}
-		if got != tc.want {
-			t.Errorf("ClangClVersion(%q)=%q; want=%q", tc.out, got, tc.want)
-		}
-	}
-
-	// failure case
-	for _, tc := range []string{
-		"",
-		"clang version",
-		"Copyright (C) Microsoft Corporation.  All rights reserved.",
-		"Microsoft (R) C/C++ Optimizing Compiler Version 19x00x24215x1 for x64",
-	} {
-		_, err := ClangClVersion([]byte(tc))
-		if err == nil {
-			t.Errorf("ClangClVersion(%q)=_,nil; want error", tc)
-		}
-	}
-}
-
-func TestClangClTarget(t *testing.T) {
-	// success case
-	for _, tc := range []struct {
-		out  string
-		want string
-	}{
-		{
-			out:  "clang version 6.0.0 (trunk 308728)\nTarget: x86_64-pc-windows-msvc\n",
-			want: "x86_64-pc-windows-msvc",
-		},
-		{
-			out:  "clang version 3.5.0 (trunk 225621)\r\nTarget: i686-pc-windows-msvc\r\n",
-			want: "i686-pc-windows-msvc",
-		},
-		{
-			out: `clang version 9.0.0 (https://github.com/llvm/llvm-project/ 67510fac36d27b2e22c7cd955fc167136b737b93)
-Target: x86_64-pc-windows-msvc
-Thread model: posix
-InstalledDir: /var/tmp/clang/third_party/llvm-build/Release+Asserts/bin
-`,
-			want: "x86_64-pc-windows-msvc",
-		},
-		{
-			out:  "clang version 9.0.0 (https://github.com/llvm/llvm-project/ 67510fac36d27b2e22c7cd955fc167136b737b93)\r\nTarget: x86_64-pc-windows-msvc\r\nThread model: posix\r\nInstalledDir: /var/tmp/clang/third_party/llvm-build/Release+Asserts/bin\r\n",
-			want: "x86_64-pc-windows-msvc",
-		},
-	} {
-		got, err := ClangClTarget([]byte(tc.out))
-		if err != nil {
-			t.Errorf("ClangClTarget(%q)=_,%v; want nil", tc.out, err)
-		}
-		if got != tc.want {
-			t.Errorf("ClangClTarget(%q)=%q; want=%q", tc.out, got, tc.want)
-		}
-	}
-
-	// failure case
-	for _, tc := range []string{
-		"",
-		"clang version",
-		"clang version 3.5.0 (trunk 225621)\r\n",
-		"Copyright (C) Microsoft Corporation.  All rights reserved.",
-		"Microsoft (R) C/C++ Optimizing Compiler Version 19x00x24215x1 for x64",
-	} {
-		_, err := ClangClTarget([]byte(tc))
-		if err == nil {
-			t.Errorf("ClangClTarget(%q)=_,nil; want error", tc)
-		}
-	}
-}
-
-func TestDartAnalyzerVersion(t *testing.T) {
-	for _, tc := range []struct {
-		out     string
-		version string
-	}{
-		{
-			out:     "dartanalyzer version 2.1.1-dev.1.0\n",
-			version: "2.1.1-dev.1.0",
-		},
-		{
-			out:     "dartanalyzer version 2.5.0-edge.2b3336b51eda58fe049c8a8d81b7576aaa98c218\n",
-			version: "2.5.0-edge.2b3336b51eda58fe049c8a8d81b7576aaa98c218",
-		},
-	} {
-		version, err := DartAnalyzerVersion([]byte(tc.out))
-		if err != nil {
-			t.Errorf("expect no error from test case %v+, got an error: %v", tc, err)
-		}
-		if version != tc.version {
-			t.Errorf("expect version %q, got %q", tc.version, version)
-		}
-	}
-
-	// failure case
-	for _, tc := range []string{
-		"Dart VM version: 2.5.0-edge.2b3336b51eda58fe049c8a8d81b7576aaa98c218 (Wed Jul 17 09:11:15 2019 +0000) on \"linux_x64\"",
-	} {
-		_, err := DartAnalyzerVersion([]byte(tc))
-		if err == nil {
-			t.Errorf("expect parsing error in %q, but got nil", tc)
-		}
-	}
-}
-
-func TestResolveSymlinks(t *testing.T) {
-	d := &Descriptor{
-		fname: "dummy",
-		CmdDescriptor: &pb.CmdDescriptor{
-			Setup: &pb.CmdDescriptor_Setup{},
-		},
-		ToClientPath: func(p string) (string, error) { return p, nil },
-		PathType:     pb.CmdDescriptor_POSIX,
-		cwd:          "/dummy",
-		cmddir:       "/dummy",
-		seen:         make(map[string]bool),
-	}
-	dir, err := ioutil.TempDir("", "resolvesymlink")
-	if err != nil {
-		t.Fatalf("failed to create tmpdir: %v", err)
-	}
-	defer os.RemoveAll(dir)
-
-	// clang++ -> clang -> clang-6.0
-	err = os.MkdirAll(filepath.Join(dir, "bin"), 0755)
-	if err != nil {
-		t.Fatalf("failed to mkdir: %v", err)
-	}
-	err = ioutil.WriteFile(filepath.Join(dir, "bin", "clang-6.0"), []byte("dummy"), 0644)
-	if err != nil {
-		t.Fatalf("failed to create clang-6.0: %v", err)
-	}
-	err = os.Symlink("clang-6.0", filepath.Join(dir, "bin", "clang"))
-	if err != nil {
-		t.Fatalf("failed to create clang: %v", err)
-	}
-	err = os.Symlink("clang", filepath.Join(dir, "bin", "clang++"))
-	if err != nil {
-		t.Fatalf("failed to create clang++: %v", err)
-	}
-
-	fs, err := newFilespec(filepath.Join(dir, "bin", "clang++"))
-	if err != nil {
-		t.Fatalf("filespec(%s)=,%v; want nil error", filepath.Join(dir, "bin", "clang++"), err)
-	}
-	err = d.resolveSymlinks("", fs)
-	if err != nil {
-		t.Errorf("resolveSymlinks(%s, %v)=_,%v; want nil error", "", fs, err)
-	}
-	var expectedFiles []*pb.FileSpec
-	for _, f := range []string{"clang", "clang-6.0"} {
-		fsc, err := newFilespec(filepath.Join(dir, "bin", f))
-		if err != nil {
-			t.Fatalf("filespec(%s)=_,%v; want nil error", f, err)
-		}
-		cfsc, err := d.filespecProto(fsc)
-		if err != nil {
-			t.Fatalf("filespecProto(%v)=_, %v; want nil error", fsc, err)
-		}
-		expectedFiles = append(expectedFiles, cfsc)
-	}
-	if !reflect.DeepEqual(d.Setup.Files, expectedFiles) {
-		t.Errorf("d.Setup.Files=%q; want %q", d.Setup.Files, expectedFiles)
-	}
-}
diff --git a/command/descriptor/doc.go b/command/descriptor/doc.go
deleted file mode 100644
index b92df0d..0000000
--- a/command/descriptor/doc.go
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright 2017 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.
-
-/*
-Package descriptor provides command descriptor utilities.
-*/
-package descriptor
diff --git a/command/descriptor/elf.go b/command/descriptor/elf.go
deleted file mode 100644
index e114763..0000000
--- a/command/descriptor/elf.go
+++ /dev/null
@@ -1,130 +0,0 @@
-// Copyright 2017 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.
-
-package descriptor
-
-import (
-	"bufio"
-	"bytes"
-	"debug/elf"
-	"fmt"
-	"os/exec"
-	"path/filepath"
-	"strings"
-	"sync"
-)
-
-// removeAbsPaths removes paths that start with '/'.
-// order will be kept.
-func removeAbsPaths(rpaths []string) []string {
-	var newRpaths []string
-	for _, rpath := range rpaths {
-		if strings.HasPrefix(rpath, "/") {
-			continue
-		}
-		newRpaths = append(newRpaths, rpath)
-	}
-
-	return newRpaths
-}
-
-// relocateLibraries reads an elf binary to list dependent libraries.
-// If a dependent library exists in rpath or runpath, it's considered as
-// relocatable. However, rpath or runpath is in absolute form, the dependent
-// library is considered as unrelocatable.
-func relocatableLibraries(fname string) ([]string, error) {
-	f, err := elf.Open(fname)
-	if err != nil {
-		return nil, err
-	}
-	defer f.Close()
-	tokens := map[string]string{
-		"ORIGIN":   filepath.Dir(fname),
-		"LIB":      libname(f),
-		"PLATFORM": platform(),
-	}
-	rpaths, err := f.DynString(elf.DT_RPATH)
-	if err != nil {
-		return nil, err
-	}
-	runpaths, err := f.DynString(elf.DT_RUNPATH)
-	if err != nil {
-		return nil, err
-	}
-	rpaths = append(rpaths, runpaths...)
-
-	// When rpath is absolute path, it's not relocatable, so skip it.
-	// Since $ORIGIN can be absolute, these should be filtered
-	// before expanding tokens.
-	rpaths = removeAbsPaths(rpaths)
-	rpaths = expandRpaths(tokens, rpaths...)
-
-	libs, err := f.ImportedLibraries()
-	if err != nil {
-		return nil, err
-	}
-	var libpaths []string
-	for _, lib := range libs {
-		path, err := lookpath(lib, rpaths)
-		if err != nil {
-			continue
-		}
-		libpaths = append(libpaths, path)
-	}
-	return libpaths, nil
-}
-
-func libname(f *elf.File) string {
-	if f.FileHeader.Class == elf.ELFCLASS64 {
-		return "lib64"
-	}
-	return "lib"
-}
-
-var (
-	platformOnce sync.Once
-	platformName string
-)
-
-func platform() string {
-	platformOnce.Do(func() {
-		cmd := exec.Command("sleep", "0")
-		cmd.Env = []string{"LD_SHOW_AUXV=1"}
-		out, err := cmd.CombinedOutput()
-		if err != nil {
-			return
-		}
-		s := bufio.NewScanner(bytes.NewReader(out))
-		for s.Scan() {
-			cols := strings.Fields(s.Text())
-			if len(cols) != 2 {
-				continue
-			}
-			if cols[0] == "AT_PLATFORM:" {
-				platformName = cols[1]
-				break
-			}
-		}
-	})
-	return platformName
-}
-
-func expandRpaths(tokens map[string]string, rpaths ...string) []string {
-	var paths []string
-	for _, rpath := range rpaths {
-		for _, rpath := range strings.Split(rpath, ":") {
-			rpath = expand(tokens, rpath)
-			paths = append(paths, rpath)
-		}
-	}
-	return paths
-}
-
-func expand(tokens map[string]string, rpath string) string {
-	for tok, val := range tokens {
-		rpath = strings.Replace(rpath, fmt.Sprintf("$%s", tok), val, -1)
-		rpath = strings.Replace(rpath, fmt.Sprintf("${%s}", tok), val, -1)
-	}
-	return rpath
-}
diff --git a/command/descriptor/load.go b/command/descriptor/load.go
deleted file mode 100644
index acfccd3..0000000
--- a/command/descriptor/load.go
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2017 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.
-
-package descriptor
-
-import (
-	"io/ioutil"
-	"path/filepath"
-
-	"google.golang.org/protobuf/proto"
-
-	pb "go.chromium.org/goma/server/proto/command"
-)
-
-func Load(dir string) ([]*pb.CmdDescriptor, error) {
-	names, err := filepath.Glob(filepath.Join(dir, "descriptors", "*"))
-	if err != nil {
-		return nil, err
-	}
-	var descs []*pb.CmdDescriptor
-	for _, name := range names {
-		b, err := ioutil.ReadFile(name)
-		if err != nil {
-			return nil, err
-		}
-		d := &pb.CmdDescriptor{}
-		err = proto.Unmarshal(b, d)
-		if err != nil {
-			return nil, err
-		}
-		descs = append(descs, d)
-	}
-	return descs, nil
-}
diff --git a/command/descriptor/posixpath/posixpath.go b/command/descriptor/posixpath/posixpath.go
deleted file mode 100644
index ed835a3..0000000
--- a/command/descriptor/posixpath/posixpath.go
+++ /dev/null
@@ -1,204 +0,0 @@
-// Copyright 2017 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.
-
-// Package posixpath handles posix-path (Unix style; slash separeted path).
-package posixpath
-
-import (
-	"fmt"
-	"strings"
-)
-
-// FilePath provides posix filepath.
-type FilePath struct{}
-
-func (FilePath) String() string { return "posix.filepath" }
-
-func (FilePath) IsAbs(path string) bool     { return IsAbs(path) }
-func (FilePath) Base(path string) string    { return Base(path) }
-func (FilePath) Dir(path string) string     { return Dir(path) }
-func (FilePath) Join(elem ...string) string { return Join(elem...) }
-
-func (FilePath) Rel(basepath, targpath string) (string, error) {
-	return Rel(basepath, targpath)
-}
-
-func (FilePath) Clean(path string) string       { return Clean(path) }
-func (FilePath) SplitElem(path string) []string { return SplitElem(path) }
-func (FilePath) PathSep() string                { return "/" }
-
-// IsAbs returns true if fname is absolute path.
-func IsAbs(fname string) bool {
-	return strings.HasPrefix(fname, "/")
-}
-
-// Base returns the last element of fname.
-// If fname is empty, or ends with path separator, Base returns ".".
-// If fname is "/" only, Base returns "/".
-func Base(fname string) string {
-	elem := SplitElem(fname)
-	if len(elem) == 0 {
-		return "."
-	}
-	base := elem[len(elem)-1]
-	return base
-}
-
-// Dir returns all but the last element of path, typically the path's
-// directory.  Differnt from filepath.Dir, it won't clean ".." in the result.
-// If path is empty, Dir returns ".".
-func Dir(fname string) string {
-	elem := SplitElem(fname)
-	if len(elem) == 0 {
-		return "."
-	}
-	if len(elem) == 1 {
-		if elem[0] == "/" {
-			return "/"
-		}
-		return "."
-	}
-	elem = elem[:len(elem)-1]
-	return joinElem(elem)
-}
-
-// Join joins any number of path elements into a single path, adding a "/"
-// if necessary.  Different from filepath.Join, it won't clean ".." in
-// the result. All empty strings are ignored.
-func Join(elem ...string) string {
-	if len(elem) == 0 {
-		return ""
-	}
-	paths := make([]string, 0, len(elem))
-	for _, e := range elem {
-		if e == "" {
-			continue
-		}
-		paths = append(paths, e)
-	}
-	elem = SplitElem(strings.Join(paths, "/"))
-	if len(elem) > 0 && elem[len(elem)-1] == "." {
-		elem = elem[:len(elem)-1]
-	}
-	if len(elem) == 0 {
-		return ""
-	}
-	return joinElem(elem)
-}
-
-// Rel returns a relative path that is lexically equivalent to targpath when
-// joined to basepath with an intervening separator.
-func Rel(basepath, targpath string) (string, error) {
-	if IsAbs(basepath) != IsAbs(targpath) {
-		return "", fmt.Errorf("Rel: can't make %s relative to %s", targpath, basepath)
-	}
-
-	base := SplitElem(basepath)
-	base = cleanElem(base)
-	targ := SplitElem(targpath)
-	targ = cleanElem(targ)
-
-	// Find first different elem at i. (base[:i] equals targ[:i])
-	bl := len(base)
-	tl := len(targ)
-	var i int
-	for i < bl && i < tl {
-		if base[i] != targ[i] {
-			break
-		}
-		i++
-	}
-	var elem []string
-	if i != bl {
-		// Base elements left.
-		alldotdot := func(elem []string) bool {
-			for _, e := range elem {
-				if e != ".." {
-					return false
-				}
-			}
-			return true
-		}
-		if alldotdot(base[i:]) {
-			return "", fmt.Errorf("Rel: can't make %s relative to %s", targpath, basepath)
-		}
-		n := bl - i
-		for i := 0; i < n; i++ {
-			elem = append(elem, "..")
-		}
-	}
-	elem = append(elem, targ[i:]...)
-	return joinElem(elem), nil
-}
-
-// Clean returns the shortest path name equivalent to path by purely lexical processing.
-func Clean(path string) string {
-	elems := SplitElem(path)
-	elems = cleanElem(elems)
-	return joinElem(elems)
-}
-
-// SplitElem splits path into element, separated by "/".
-// If fname is absolute path, first element is "/".
-// If fname ends with "/" or "/.", last element is ".".
-// Empty string, "/" or "." won't be appeared in other elements.
-func SplitElem(fname string) []string {
-	if fname == "" {
-		return nil
-	}
-	if fname == "." {
-		return []string{"."}
-	}
-	ns := strings.Count(fname, "/")
-	if ns == len(fname) {
-		return []string{"/"}
-	}
-	elem := make([]string, 0, ns+1)
-	if strings.HasPrefix(fname, "/") {
-		elem = append(elem, "/")
-	}
-	for _, e := range strings.Split(fname, "/") {
-		if e == "" || e == "." {
-			continue
-		}
-		elem = append(elem, e)
-	}
-	if strings.HasSuffix(fname, "/") || strings.HasSuffix(fname, "/.") {
-		elem = append(elem, ".")
-	}
-	return elem
-}
-
-func joinElem(elem []string) string {
-	if len(elem) == 0 {
-		return "."
-	}
-	if len(elem) == 1 {
-		return elem[0]
-	}
-	if elem[0] == "/" {
-		elem[0] = ""
-	}
-	return strings.Join(elem, "/")
-}
-
-func cleanElem(elem []string) []string {
-	if len(elem) > 0 && elem[len(elem)-1] == "." {
-		elem = elem[:len(elem)-1]
-	}
-	r := make([]string, 0, len(elem))
-	for _, e := range elem {
-		if e == ".." {
-			if len(r) == 1 && r[0] == "/" {
-				continue
-			}
-			if len(r) > 0 && r[len(r)-1] != ".." {
-				r = r[:len(r)-1]
-				continue
-			}
-		}
-		r = append(r, e)
-	}
-	return r
-}
diff --git a/command/descriptor/posixpath/posixpath_test.go b/command/descriptor/posixpath/posixpath_test.go
deleted file mode 100644
index b881e21..0000000
--- a/command/descriptor/posixpath/posixpath_test.go
+++ /dev/null
@@ -1,309 +0,0 @@
-// Copyright 2017 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.
-
-package posixpath
-
-import (
-	"reflect"
-	"testing"
-)
-
-func TestIsAbs(t *testing.T) {
-	for _, tc := range []struct {
-		path  string
-		isAbs bool
-	}{
-		{"", false},
-		{"/", true},
-		{"/usr/bin/gcc", true},
-		{"..", false},
-		{"/a/../bb", true},
-		{".", false},
-		{"./", false},
-		{"lala", false},
-	} {
-		got := IsAbs(tc.path)
-		if got != tc.isAbs {
-			t.Errorf("IsAbs(%q)=%t; want=%t", tc.path, got, tc.isAbs)
-		}
-	}
-}
-
-func TestBase(t *testing.T) {
-	for _, tc := range []struct {
-		path, result string
-	}{
-		{"", "."},
-		{".", "."},
-		{"/.", "."},
-		{"/", "/"},
-		{"////", "/"},
-		{"x/", "."}, // different from filepath.Base
-		{"abc", "abc"},
-		{"abc/def", "def"},
-		{"a/b/.x", ".x"},
-		{"a/b/c.", "c."},
-		{"a/b/c.x", "c.x"},
-	} {
-		got := Base(tc.path)
-		if got != tc.result {
-			t.Errorf("Base(%q)=%q; want=%q", tc.path, got, tc.result)
-		}
-	}
-}
-
-func TestDir(t *testing.T) {
-	for _, tc := range []struct {
-		path, result string
-	}{
-		{"", "."},
-		{".", "."},
-		{"/.", "/"},
-		{"/", "/"},
-		{"////", "/"},
-		{"/foo", "/"},
-		{"x/", "x"},
-		{"abc", "."},
-		{"abc/def", "abc"},
-		{"a/b/.x", "a/b"},
-		{"a/b/c.", "a/b"},
-		{"a/b/c.x", "a/b"},
-	} {
-		got := Dir(tc.path)
-		if got != tc.result {
-			t.Errorf("Dir(%q)=%q; want=%q", tc.path, got, tc.result)
-		}
-	}
-}
-
-func TestJoin(t *testing.T) {
-	for _, tc := range []struct {
-		elem []string
-		path string
-	}{
-		// zero parameters.
-		{nil, ""},
-
-		// one parameter
-		{[]string{""}, ""},
-		{[]string{"/"}, "/"},
-		{[]string{"a"}, "a"},
-
-		// two parameters
-		{[]string{"a", "b"}, "a/b"},
-		{[]string{"a", ""}, "a"},
-		{[]string{"", "b"}, "b"},
-		{[]string{"/", "a"}, "/a"},
-		{[]string{"/", "a/b"}, "/a/b"},
-		{[]string{"/", ""}, "/"},
-		{[]string{"//", "a"}, "/a"},
-		{[]string{"/a", "b"}, "/a/b"},
-		{[]string{"a/", "b"}, "a/b"},
-		{[]string{"a/", ""}, "a"},
-		{[]string{"", ""}, ""},
-		// different from filepath.Join, it won't clean /../.
-		{[]string{"a", "../b"}, "a/../b"},
-
-		// tree parameters
-		{[]string{"/", "a", "b"}, "/a/b"},
-	} {
-		got := Join(tc.elem...)
-		if got != tc.path {
-			t.Errorf("Join(%q)=%q; want=%q", tc.elem, got, tc.path)
-		}
-	}
-}
-
-func TestRel(t *testing.T) {
-	for _, tc := range []struct {
-		root, path, want string
-	}{
-		{"a/b", "a/b", "."},
-		{"a/b/.", "a/b", "."},
-		{"a/b", "a/b/.", "."},
-		{"./a/b", "a/b", "."},
-		{"a/b", "./a/b", "."},
-		{"ab/cd", "ab/cde", "../cde"},
-		{"ab/cd", "ab/c", "../c"},
-		{"a/b", "a/b/c/d", "c/d"},
-		{"a/b", "a/b/../c", "../c"},
-		{"a/b/../c", "a/b", "../b"},
-		{"a/b/c", "a/c/d", "../../c/d"},
-		{"a/b", "c/d", "../../c/d"},
-		{"a/b/c/d", "a/b", "../.."},
-		{"a/b/c/d", "a/b/", "../.."},
-		{"a/b/c/d/", "a/b", "../.."},
-		{"a/b/c/d/", "a/b/", "../.."},
-		{"../../a/b", "../../a/b/c/d", "c/d"},
-		{"/a/b", "/a/b", "."},
-		{"/a/b/.", "/a/b", "."},
-		{"/a/b", "/a/b/.", "."},
-		{"/ab/cd", "/ab/cde", "../cde"},
-		{"/ab/cd", "/ab/c", "../c"},
-		{"/a/b", "/a/b/c/d", "c/d"},
-		{"/a/b", "/a/b/../c", "../c"},
-		{"/a/b/../c", "/a/b", "../b"},
-		{"/a/b/c", "/a/c/d", "../../c/d"},
-		{"/a/b", "/c/d", "../../c/d"},
-		{"/a/b/c/d", "/a/b", "../.."},
-		{"/a/b/c/d", "/a/b/", "../.."},
-		{"/a/b/c/d/", "/a/b", "../.."},
-		{"/a/b/c/d/", "/a/b/", "../.."},
-		{"/../../a/b", "/../../a/b/c/d", "c/d"},
-		{".", "a/b", "a/b"},
-		{".", "..", ".."},
-	} {
-		got, err := Rel(tc.root, tc.path)
-		if err != nil || got != tc.want {
-			t.Errorf("Rel(%q, %q)=%q, %v; want %q, nil", tc.root, tc.path, got, err, tc.want)
-		}
-	}
-
-	// can't do purely lexically
-	for _, tc := range []struct {
-		root, path string
-	}{
-		{"..", "."},
-		{"..", "a"},
-		{"../..", ".."},
-		{"a", "/a"},
-		{"/a", "a"},
-	} {
-		got, err := Rel(tc.root, tc.path)
-		if err == nil {
-			t.Errorf("Rel(%q, %q)=%q, nil; want error", tc.root, tc.path, got)
-		}
-	}
-}
-
-func TestClean(t *testing.T) {
-	for _, tc := range []struct {
-		path, result string
-	}{
-		// https://golang.org/src/path/filepath/path_test.go
-
-		// Already clean
-		{"abc", "abc"},
-		{"abc/def", "abc/def"},
-		{"a/b/c", "a/b/c"},
-		{".", "."},
-		{"..", ".."},
-		{"../..", "../.."},
-		{"../../abc", "../../abc"},
-		{"/abc", "/abc"},
-		{"/", "/"},
-
-		// Empty is current dir
-		{"", "."},
-
-		// Remove trailing slash
-		{"abc/", "abc"},
-		{"abc/def/", "abc/def"},
-		{"a/b/c/", "a/b/c"},
-		{"./", "."},
-		{"../", ".."},
-		{"../../", "../.."},
-		{"/abc/", "/abc"},
-
-		// Remove doubled slash
-		{"abc//def//ghi", "abc/def/ghi"},
-		{"//abc", "/abc"},
-		{"///abc", "/abc"},
-		{"//abc//", "/abc"},
-		{"abc//", "abc"},
-
-		// Remove . elements
-		{"abc/./def", "abc/def"},
-		{"/./abc/def", "/abc/def"},
-		{"abc/.", "abc"},
-
-		// Remove .. elements
-		{"abc/def/ghi/../jkl", "abc/def/jkl"},
-		{"abc/def/../ghi/../jkl", "abc/jkl"},
-		{"abc/def/..", "abc"},
-		{"abc/def/../..", "."},
-		{"/abc/def/../..", "/"},
-		{"abc/def/../../..", ".."},
-		{"/abc/def/../../..", "/"},
-		{"abc/def/../../../ghi/jkl/../../../mno", "../../mno"},
-		{"/../abc", "/abc"},
-
-		// Combinations
-		{"abc/./../def", "def"},
-		{"abc//./../def", "def"},
-		{"abc/../../././../def", "../../def"},
-	} {
-		got := Clean(tc.path)
-		if got != tc.result {
-			t.Errorf("Clean(%q)=%q; want=%q", tc.path, got, tc.result)
-		}
-	}
-}
-
-func TestSplitElem(t *testing.T) {
-	for _, tc := range []struct {
-		path string
-		elem []string
-	}{
-		{"", nil},
-
-		{"/", []string{"/"}},
-		{"a", []string{"a"}},
-		{".", []string{"."}},
-
-		{"/.", []string{"/", "."}},
-		{"////", []string{"/"}},
-		{"////.", []string{"/", "."}},
-		{".////", []string{"."}},
-
-		{"a/b", []string{"a", "b"}},
-		{"/a/b", []string{"/", "a", "b"}},
-		{"a/b/.", []string{"a", "b", "."}},
-		{"a/b/", []string{"a", "b", "."}},
-
-		{"a//b", []string{"a", "b"}},
-		{"//a", []string{"/", "a"}},
-		{"a//", []string{"a", "."}},
-		{"a//.", []string{"a", "."}},
-		{"a/./b", []string{"a", "b"}},
-		{"a/././b", []string{"a", "b"}},
-
-		{"a/../b", []string{"a", "..", "b"}},
-	} {
-		got := SplitElem(tc.path)
-		if !reflect.DeepEqual(got, tc.elem) {
-			t.Errorf("SplitElem(%q)=%q; want=%q", tc.path, got, tc.elem)
-		}
-	}
-}
-
-func BenchmarkCleanAlreadyClean(b *testing.B) {
-	for i := 0; i < b.N; i++ {
-		Clean("../abc/def/ghi")
-	}
-}
-
-func BenchmarkCleanAlreadyCleanAbs(b *testing.B) {
-	for i := 0; i < b.N; i++ {
-		Clean("/abc/def/ghi")
-	}
-}
-
-func BenchmarkCleanParentDir(b *testing.B) {
-	for i := 0; i < b.N; i++ {
-		Clean("abc/def/ghi/../jkl")
-	}
-}
-
-func BenchmarkCleanParentDirAbs(b *testing.B) {
-	for i := 0; i < b.N; i++ {
-		Clean("/abc/def/ghi/../jkl")
-	}
-}
-
-func BenchmarkJoin(b *testing.B) {
-	for i := 0; i < b.N; i++ {
-		Join("/b", "s", "w", "ir", "cache", "builder", "src", "out", "Release", "gen", "ipc", "ipc_buildflags.h")
-	}
-}
diff --git a/command/descriptor/relocate.go b/command/descriptor/relocate.go
deleted file mode 100644
index 6daa7de..0000000
--- a/command/descriptor/relocate.go
+++ /dev/null
@@ -1,198 +0,0 @@
-// Copyright 2017 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.
-
-package descriptor
-
-import (
-	"fmt"
-
-	"google.golang.org/protobuf/proto"
-
-	"go.chromium.org/goma/server/command/descriptor/posixpath"
-	"go.chromium.org/goma/server/command/descriptor/winpath"
-	pb "go.chromium.org/goma/server/proto/command"
-)
-
-// FilePath provides client's filepath.
-type FilePath interface {
-	// IsAbs reports whether the path is absolute.
-	IsAbs(path string) bool
-
-	// Base returns the last element of path, typically
-	// the filename.
-	Base(string) string
-
-	// Dir returns all but the last element of path, typically
-	// the path's directory.
-	// Different filepath.Dir, it won't clean "..".
-	Dir(string) string
-
-	// Join joins any number of path elements into a single path.
-	// Different filepath.Dir, it won't clean "..".
-	Join(...string) string
-
-	// Rel returns a relative path that is lexically equivalent
-	// to targpath when joined to basepath with an intervening separator.
-	Rel(basepath, targpath string) (string, error)
-
-	// Clean returns the shortest path name equivalent to path by purely lexical processing.
-	Clean(path string) string
-
-	// SplitElem splits path by path separator.
-	SplitElem(path string) []string
-
-	// PathSep returns the path separator.
-	PathSep() string
-}
-
-// FilePathOf returns FilePath of path type.
-func FilePathOf(pt pb.CmdDescriptor_PathType) (FilePath, error) {
-	switch pt {
-	case pb.CmdDescriptor_POSIX:
-		return posixpath.FilePath{}, nil
-
-	case pb.CmdDescriptor_WINDOWS:
-		return winpath.FilePath{}, nil
-	}
-	return nil, fmt.Errorf("bad path type: %s", pt)
-}
-
-// IsRelocatable returns true if this setup can be relocated to where client
-// put a compiler or a subprogram.
-// setup must have valid path type.
-func IsRelocatable(setup *pb.CmdDescriptor_Setup) (bool, error) {
-	filepath, err := FilePathOf(setup.GetPathType())
-	if err != nil {
-		return false, fmt.Errorf("setup %s: %v", setup, err)
-	}
-	if filepath.IsAbs(setup.CmdFile.Path) {
-		return false, nil
-	}
-	return true, nil
-}
-
-// AbsCmdPath returns absolute command path of the setup.
-func AbsCmdPath(setup *pb.CmdDescriptor_Setup) (string, error) {
-	filepath, err := FilePathOf(setup.GetPathType())
-	if err != nil {
-		return "", err
-	}
-	cmdpath := setup.CmdFile.Path
-	if !filepath.IsAbs(setup.CmdFile.Path) {
-		cmdpath = filepath.Join(setup.CmdDir, setup.CmdFile.Path)
-	}
-	return cmdpath, nil
-}
-
-// Relocate relocates cmdpath and setup files.
-func Relocate(setup *pb.CmdDescriptor_Setup, cmdpath string) (*pb.CmdDescriptor_Setup, error) {
-	filepath, err := FilePathOf(setup.GetPathType())
-	if err != nil {
-		return nil, err
-	}
-	s := proto.Clone(setup).(*pb.CmdDescriptor_Setup)
-	origin := filepath.Dir(cmdpath)
-	s.CmdFile.Path = cmdpath
-	for _, f := range s.Files {
-		if filepath.IsAbs(f.Path) {
-			continue
-		}
-		f.Path = filepath.Join(origin, f.Path)
-	}
-	return s, nil
-}
-
-// relocateCommands returns relocated command files.
-// cmdPath is a command path (relative client path).
-// cs is (main) command setup.
-//
-// cmdseen is used to filter out the existing files from the result.
-func relocateCommands(cmdseen map[string]bool, cmdPath string, cs *pb.CmdDescriptor_Setup) ([]*pb.FileSpec, error) {
-	// Note: paths in cmdseen is a client form, and can be a relative path.
-	// So, this code cannot dedup the subprograms exhaustively.
-
-	var result []*pb.FileSpec
-
-	cs, err := Relocate(cs, cmdPath)
-	if err != nil {
-		return nil, err
-	}
-	if !cmdseen[cs.CmdFile.Path] {
-		result = append(result, cs.CmdFile)
-		cmdseen[cs.CmdFile.Path] = true
-	}
-
-	for _, f := range cs.Files {
-		if cmdseen[f.Path] {
-			continue
-		}
-		result = append(result, f)
-		cmdseen[f.Path] = true
-	}
-
-	return result, nil
-}
-
-// RelocateCmd relocates main program and subprograms if necessary.
-// subprogSetups is a map from subprogram client path to subprogram setup.
-// It returns commands' FileSpec to be used.
-// Actual command path will be first FileSpec's Path.
-func RelocateCmd(cmdPath string, setup *pb.CmdDescriptor_Setup, subprogSetups map[string]*pb.CmdDescriptor_Setup) ([]*pb.FileSpec, error) {
-	// If relocatable, set up filespecs so that the command is installed in the command path.
-	// If not relocatable, rewrite command itself.
-
-	cmdseen := make(map[string]bool)
-	var allRelocatedFileSpecs []*pb.FileSpec
-
-	// Relocate the main binary
-	relocatable, err := IsRelocatable(setup)
-	if err != nil {
-		return nil, err
-	}
-	if relocatable {
-		relocatedFileSpecs, err := relocateCommands(cmdseen, cmdPath, setup)
-		if err != nil {
-			return nil, err
-		}
-		allRelocatedFileSpecs = append(allRelocatedFileSpecs, relocatedFileSpecs...)
-	} else {
-		cmdfile := proto.Clone(setup.CmdFile).(*pb.FileSpec)
-		cmdfile.Path = cmdPath
-		allRelocatedFileSpecs = append(allRelocatedFileSpecs,
-			cmdfile)
-		allRelocatedFileSpecs = append(allRelocatedFileSpecs,
-			setup.Files...)
-	}
-
-	// Relocate subprograms.
-	for sPath, subprogSetup := range subprogSetups {
-		relocatable, err := IsRelocatable(subprogSetup)
-		if err != nil {
-			return nil, err
-		}
-		if relocatable {
-			relocatedFileSpecs, err := relocateCommands(cmdseen, sPath, subprogSetup)
-			if err != nil {
-				return nil, err
-			}
-			allRelocatedFileSpecs = append(allRelocatedFileSpecs, relocatedFileSpecs...)
-		} else {
-			// Since the subprogram is invoked from the main program, it's hard to support it.
-			// We cannot change the command line in the main program.
-			//
-			// Not sure there is a case that the main program calls subprogram with the fixed path.
-			// If not, we can return an error here.
-			//
-			// However, usually the subprogram is called with relative path, or called based on
-			// -B (e.g. objcopy from clang).
-			cmdfile := proto.Clone(subprogSetup.CmdFile).(*pb.FileSpec)
-			cmdfile.Path = sPath
-			allRelocatedFileSpecs = append(allRelocatedFileSpecs,
-				cmdfile)
-			allRelocatedFileSpecs = append(allRelocatedFileSpecs,
-				subprogSetup.Files...)
-		}
-	}
-	return allRelocatedFileSpecs, nil
-}
diff --git a/command/descriptor/relocate_test.go b/command/descriptor/relocate_test.go
deleted file mode 100644
index dca31b7..0000000
--- a/command/descriptor/relocate_test.go
+++ /dev/null
@@ -1,193 +0,0 @@
-// Copyright 2017 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.
-
-package descriptor
-
-import (
-	"testing"
-
-	"github.com/google/go-cmp/cmp"
-	"google.golang.org/protobuf/testing/protocmp"
-
-	pb "go.chromium.org/goma/server/proto/command"
-)
-
-func TestRelocateCmd(t *testing.T) {
-	// Real example from chromium linux.
-	cs := &pb.CmdDescriptor_Setup{
-		CmdFile: &pb.FileSpec{
-			Path:         "bin/clang",
-			Size:         86885440,
-			Hash:         "daacc7bcef72015968bfdda381b8ac27fe735d125314ed17d8320dbda89d7be3",
-			IsExecutable: true,
-		},
-		CmdDir: "/var/opt/chromium_clang_linux/third_party/llvm-build/Release+Asserts",
-		Files: []*pb.FileSpec{
-			{
-				Path:         "/usr/bin/as",
-				Size:         356952,
-				Hash:         "ffc7f9fee274e30e308577761adc5fe97b2b22403874500a4c9f26517755ec33",
-				IsExecutable: true,
-			},
-			{
-				Path:         "/usr/bin/objcopy",
-				Size:         220128,
-				Hash:         "e6ddd34643b436bfe1754f2719ac8ec17ff15497091db592e1b4e335f3653291",
-				IsExecutable: true,
-			},
-			{
-				Path:    "/usr/bin/ld",
-				Symlink: "ld.bfd",
-			},
-			{
-				Path:         "/usr/bin/ld.bfd",
-				Size:         1050912,
-				Hash:         "850873e6c4def272aea40f906df43bcb5ad6a7343593dde2dcb89e1a0e4cd6f4",
-				IsExecutable: true,
-			},
-			{
-				Path:         "/usr/bin/nm",
-				Size:         40704,
-				Hash:         "9119a26b9dffd94cf2f87f10ff0baef81f0912a84a920f5f3441b3cfaf59ff2b",
-				IsExecutable: true,
-			},
-			{
-				Path:         "/usr/bin/strip",
-				Size:         220128,
-				Hash:         "86aa93fd3010abe601c179a4600acbd348b13d76c66f861591c4d9c615687264",
-				IsExecutable: true,
-			},
-		},
-		PathType: pb.CmdDescriptor_POSIX,
-	}
-
-	subprogSetups := map[string]*pb.CmdDescriptor_Setup{
-		"/home/goma/work/chrome/src/out/Release/../../third_party/llvm-build/Release+Asserts/lib/libFindBadConstructs.so": {
-			CmdFile: &pb.FileSpec{
-				Path:         "lib/libFindBadConstructs.so",
-				Size:         521748,
-				Hash:         "1c5e11d7975e49ab4396570f54f13d71685005a18593be29bf462b51f45954f6",
-				IsExecutable: true,
-			},
-			CmdDir:   "/var/opt/chromium_clang_linux/third_party/llvm-build/Release+Asserts",
-			PathType: pb.CmdDescriptor_POSIX,
-		},
-	}
-
-	const reqCmdPath = "/home/goma/work/chrome/src/third_party/llvm-build/Release+Asserts/bin/clang++"
-
-	cmdFiles, err := RelocateCmd(reqCmdPath, cs, subprogSetups)
-	if err != nil {
-		t.Errorf("RelocateCmd(%q, %q, %q)=_, _, %v; want nil-error", reqCmdPath, cs, subprogSetups, err)
-	}
-
-	want := []*pb.FileSpec{
-		{
-			Path:         "/home/goma/work/chrome/src/third_party/llvm-build/Release+Asserts/bin/clang++",
-			Size:         86885440,
-			Hash:         "daacc7bcef72015968bfdda381b8ac27fe735d125314ed17d8320dbda89d7be3",
-			IsExecutable: true,
-		},
-		{
-			Path:         "/usr/bin/as",
-			Size:         356952,
-			Hash:         "ffc7f9fee274e30e308577761adc5fe97b2b22403874500a4c9f26517755ec33",
-			IsExecutable: true,
-		},
-		{
-			Path:         "/usr/bin/objcopy",
-			Size:         220128,
-			Hash:         "e6ddd34643b436bfe1754f2719ac8ec17ff15497091db592e1b4e335f3653291",
-			IsExecutable: true,
-		},
-		{
-			Path:    "/usr/bin/ld",
-			Symlink: "ld.bfd",
-		},
-		{
-			Path:         "/usr/bin/ld.bfd",
-			Size:         1050912,
-			Hash:         "850873e6c4def272aea40f906df43bcb5ad6a7343593dde2dcb89e1a0e4cd6f4",
-			IsExecutable: true,
-		},
-		{
-			Path:         "/usr/bin/nm",
-			Size:         40704,
-			Hash:         "9119a26b9dffd94cf2f87f10ff0baef81f0912a84a920f5f3441b3cfaf59ff2b",
-			IsExecutable: true,
-		},
-		{
-			Path:         "/usr/bin/strip",
-			Size:         220128,
-			Hash:         "86aa93fd3010abe601c179a4600acbd348b13d76c66f861591c4d9c615687264",
-			IsExecutable: true,
-		},
-		{
-			Path:         "/home/goma/work/chrome/src/out/Release/../../third_party/llvm-build/Release+Asserts/lib/libFindBadConstructs.so",
-			Size:         521748,
-			Hash:         "1c5e11d7975e49ab4396570f54f13d71685005a18593be29bf462b51f45954f6",
-			IsExecutable: true,
-		},
-	}
-
-	if !cmp.Equal(cmdFiles, want, protocmp.Transform()) {
-		t.Errorf("RelocateCmd(%q, %q, %q)=%v; want=%v", reqCmdPath, cs, subprogSetups, cmdFiles, want)
-	}
-}
-
-func TestRelocateCmdSubprog(t *testing.T) {
-	// The main binary is not relocatable, but subprogram is relocatable.
-	cs := &pb.CmdDescriptor_Setup{
-		CmdFile: &pb.FileSpec{
-			Path:         "/usr/bin/clang",
-			Size:         1,
-			Hash:         "a",
-			IsExecutable: true,
-		},
-		CmdDir:   "/tmp",
-		PathType: pb.CmdDescriptor_POSIX,
-	}
-
-	// objdump is relocatable, and it's requested to place at /opt/bin/objdump.
-	subprogSetups := map[string]*pb.CmdDescriptor_Setup{
-		"/opt/bin/objdump": {
-			CmdFile: &pb.FileSpec{
-				Path:         "bin/objdump",
-				Size:         2,
-				Hash:         "b",
-				IsExecutable: true,
-			},
-			CmdDir:   "/cmddir",
-			PathType: pb.CmdDescriptor_POSIX,
-		},
-	}
-
-	const reqCmdPath = "/usr/bin/clang"
-
-	cmdFiles, err := RelocateCmd(reqCmdPath, cs, subprogSetups)
-	if err != nil {
-		t.Errorf("RelocateCmd(%q, %q, %q)=_, _, %v; want nil-error", reqCmdPath, cs, subprogSetups, err)
-	}
-
-	// objdump must be included in FileSpec. clang is not relocatable, so it will be included as is.
-	got := cmdFiles
-	want := []*pb.FileSpec{
-		{
-			Path:         "/usr/bin/clang",
-			Size:         1,
-			Hash:         "a",
-			IsExecutable: true,
-		},
-		{
-			Path:         "/opt/bin/objdump",
-			Size:         2,
-			Hash:         "b",
-			IsExecutable: true,
-		},
-	}
-
-	if !cmp.Equal(got, want, protocmp.Transform()) {
-		t.Errorf("RelocateCmd(%q, %q, %q)=_, %v, nil; want=_, %v, nil", reqCmdPath, cs, subprogSetups, got, want)
-	}
-}
diff --git a/command/descriptor/save.go b/command/descriptor/save.go
deleted file mode 100644
index ff43fcb..0000000
--- a/command/descriptor/save.go
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2017 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.
-
-package descriptor
-
-import (
-	"io/ioutil"
-	"path/filepath"
-
-	"google.golang.org/protobuf/proto"
-
-	"go.chromium.org/goma/server/hash"
-
-	pb "go.chromium.org/goma/server/proto/command"
-)
-
-// Save saves given cmd descriptor to a file in dir.
-func Save(dir string, desc *pb.CmdDescriptor) error {
-	b, err := proto.Marshal(desc)
-	if err != nil {
-		return err
-	}
-	err = ioutil.WriteFile(filepath.Join(dir, "descriptors", hash.SHA256Content(b)), b, 0644)
-	if err != nil {
-		return err
-	}
-	return nil
-}
diff --git a/command/descriptor/winpath/winpath.go b/command/descriptor/winpath/winpath.go
deleted file mode 100644
index fab0036..0000000
--- a/command/descriptor/winpath/winpath.go
+++ /dev/null
@@ -1,193 +0,0 @@
-// Copyright 2017 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.
-
-// Package winpath handles windows-path (backslash separated path).
-// It also accepts slash as path separator.
-package winpath
-
-import (
-	"fmt"
-	"regexp"
-	"strings"
-
-	"go.chromium.org/goma/server/command/descriptor/posixpath"
-)
-
-var (
-	absPathPattern = regexp.MustCompile(`^[A-Za-z]:[/\\].*`)
-)
-
-// FilePath provides win filepath.
-type FilePath struct{}
-
-func (FilePath) String() string { return "win.filepath" }
-
-func (FilePath) IsAbs(path string) bool     { return IsAbs(path) }
-func (FilePath) Base(path string) string    { return Base(path) }
-func (FilePath) Dir(path string) string     { return Dir(path) }
-func (FilePath) Join(elem ...string) string { return Join(elem...) }
-
-func (FilePath) Rel(basepath, targpath string) (string, error) {
-	return Rel(basepath, targpath)
-}
-
-func (FilePath) Clean(path string) string       { return Clean(path) }
-func (FilePath) SplitElem(path string) []string { return SplitElem(path) }
-func (FilePath) PathSep() string                { return `\` }
-
-// IsAbs returns true if fname is absolute path.
-func IsAbs(fname string) bool {
-	return absPathPattern.MatchString(fname)
-}
-
-// Base returns the last element of fname.
-// If fname is empty, or ends with path separator, Base returns ".".
-// If fname is `\` only, Base returns `\`.
-func Base(fname string) string {
-	if fname == "" {
-		return "."
-	}
-	fname = fixPathSep(fname, '\\', '/')
-	_, path := splitDrive(fname)
-	if path == "" {
-		return "."
-	}
-	base := posixpath.Base(path)
-	return fixPathSep(base, '/', '\\')
-}
-
-// Dir returns all but the last element of path, typically the path's
-// directory.  Differnt from filepath.Dir, it won't clean ".." in the result.
-// If path is empty, Dir returns ".".
-func Dir(fname string) string {
-	if fname == "" {
-		return "."
-	}
-	fname = fixPathSep(fname, '\\', '/')
-	drive, path := splitDrive(fname)
-	if path == "" {
-		return drive
-	}
-	dirname := posixpath.Dir(path)
-	return drive + fixPathSep(dirname, '/', '\\')
-}
-
-// Join joins any number of path elements into a single path, adding a "/"
-// if necessary.  Different from filepath.Join, it won't clean ".." in
-// the result. All empty strings are ignored.
-func Join(elem ...string) string {
-	if len(elem) == 0 {
-		return ""
-	}
-	// copy
-	elem = append([]string{}, elem...)
-	for i := range elem {
-		elem[i] = fixPathSep(elem[i], '\\', '/')
-	}
-	var drive string
-	drive, elem[0] = splitDrive(elem[0])
-	path := posixpath.Join(elem...)
-	return drive + fixPathSep(path, '/', '\\')
-}
-
-// Rel returns a relative path that is lexically equivalent to targpath when
-// joined to basepath with an intervening separator.
-// TODO: case insensitive match.
-func Rel(basepath, targpath string) (string, error) {
-	bdrive, bpath := splitDrive(basepath)
-	tdrive, tpath := splitDrive(targpath)
-	if tdrive == "" {
-		tdrive = bdrive
-		targpath = tdrive + tpath
-	}
-	if IsAbs(basepath) != IsAbs(targpath) {
-		return "", fmt.Errorf("Rel: can't make %s relative to %s (abs %t vs %t)", targpath, basepath, IsAbs(targpath), IsAbs(basepath))
-	}
-	if bdrive != tdrive {
-		return "", fmt.Errorf("Rel: can't make %s relative to %s (drive mismatch %q vs %q)", targpath, basepath, tdrive, bdrive)
-	}
-	bpath = fixPathSep(bpath, '\\', '/')
-	tpath = fixPathSep(tpath, '\\', '/')
-	rpath, err := posixpath.Rel(bpath, tpath)
-	if err != nil {
-		return "", err
-	}
-	return fixPathSep(rpath, '/', '\\'), nil
-}
-
-// Clean returns the shortest path name equivalent to path by purely lexical processing.
-func Clean(path string) string {
-	drive, path := splitDrive(path)
-	path = fixPathSep(path, '\\', '/')
-	path = posixpath.Clean(path)
-
-	return drive + fixPathSep(path, '/', '\\')
-}
-
-// SplitElem splits path into element, separated by `\` or "/".
-// If fname is absolute path, first element is `\` or `<drive>:\`,
-// otherwise if fname has drive, first element is `<drive>:`.
-// If fname ends with `\` or `\.`, last element is ".".
-// Empty string, "/", `\` or "." won't be appeared in other elements.
-func SplitElem(fname string) []string {
-	drive, path := splitDrive(fname)
-	path = fixPathSep(path, '\\', '/')
-	elems := posixpath.SplitElem(path)
-	if len(elems) > 0 && elems[0] == "/" {
-		elems[0] = drive + `\`
-	} else if drive != "" {
-		elems = append([]string{drive}, elems...)
-	}
-	return elems
-}
-
-// ToPosix converts fname into posix filepath.
-// It drops drive letters.
-func ToPosix(fname string) string {
-	_, path := splitDrive(fname)
-	return strings.ReplaceAll(path, "\\", "/")
-}
-
-func fixPathSep(path string, oldSep, newSep byte) string {
-	if !strings.ContainsRune(path, rune(oldSep)) {
-		return path
-	}
-	buf := make([]byte, len(path))
-	for i := 0; i < len(path); i++ {
-		b := path[i]
-		if b == oldSep {
-			b = newSep
-		}
-		buf[i] = b
-	}
-	return string(buf)
-}
-
-func isDriveLetter(ch byte) bool {
-	switch {
-	case ch >= 'A' && ch <= 'Z':
-		return true
-	case ch >= 'a' && ch <= 'z':
-		return true
-	default:
-		return false
-	}
-}
-
-func splitDrive(fname string) (string, string) {
-	if len(fname) == 0 {
-		return "", ""
-	}
-	if !isDriveLetter(fname[0]) {
-		return "", fname
-	}
-	if len(fname) < 2 {
-		return "", fname
-	}
-	if fname[1] != ':' {
-		return "", fname
-	}
-	// matches ^([A-Za-z]:)
-	return fname[:2], fname[2:]
-}
diff --git a/command/descriptor/winpath/winpath_test.go b/command/descriptor/winpath/winpath_test.go
deleted file mode 100644
index 9e424b7..0000000
--- a/command/descriptor/winpath/winpath_test.go
+++ /dev/null
@@ -1,422 +0,0 @@
-// Copyright 2017 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.
-
-package winpath
-
-import (
-	"reflect"
-	"testing"
-)
-
-func TestIsAbs(t *testing.T) {
-	for _, tc := range []struct {
-		path  string
-		isAbs bool
-	}{
-		{`C:\`, true},
-		{`c\`, false},
-		{`c::`, false},
-		{`c:`, false},
-		{`/`, false},
-		{`\`, false},
-		{`\Windows`, false},
-		{`c:a\b`, false},
-		{`c:\a\b`, true},
-		{`c:/a/b`, true},
-		// TODO: support UNC?
-	} {
-		got := IsAbs(tc.path)
-		if got != tc.isAbs {
-			t.Errorf("IsAbs(%q)=%t; want=%t", tc.path, got, tc.isAbs)
-		}
-	}
-}
-
-func TestBase(t *testing.T) {
-	for _, tc := range []struct {
-		path, result string
-	}{
-		{"", "."},
-		{".", "."},
-		{"/.", "."},
-		{"/", `\`},
-		{"////", `\`},
-		{"x/", "."}, // different from filepath.Base
-		{"abc", "abc"},
-		{"abc/def", "def"},
-		{"a/b/.x", ".x"},
-		{"a/b/c.", "c."},
-		{"a/b/c.x", "c.x"},
-
-		{`c:\`, `\`},
-		{`c:.`, `.`},
-		{`c:\a\b`, `b`},
-		{`c:a\b`, `b`},
-		{`c:a\b\c`, `c`},
-		// TODO: support UNC?
-	} {
-		got := Base(tc.path)
-		if got != tc.result {
-			t.Errorf("Base(%q)=%q; want=%q", tc.path, got, tc.result)
-		}
-	}
-}
-
-func TestDir(t *testing.T) {
-	for _, tc := range []struct {
-		path, result string
-	}{
-		{"", "."},
-		{".", "."},
-		{"/.", `\`},
-		{"/", `\`},
-		{"////", `\`},
-		{"/foo", `\`},
-		{"x/", "x"},
-		{"abc", "."},
-		{"abc/def", "abc"},
-		{"a/b/.x", `a\b`},
-		{"a/b/c.", `a\b`},
-		{"a/b/c.x", `a\b`},
-
-		{`c:\`, `c:\`},
-		{`c:.`, `c:.`},
-		{`c:\a\b`, `c:\a`},
-		{`c:a\b`, `c:a`},
-		{`c:a\b\c`, `c:a\b`},
-		// TODO: support UNC
-	} {
-		got := Dir(tc.path)
-		if got != tc.result {
-			t.Errorf("Dir(%q)=%q; want=%q", tc.path, got, tc.result)
-		}
-	}
-}
-
-func TestJoin(t *testing.T) {
-	for _, tc := range []struct {
-		elem []string
-		path string
-	}{
-		// zero parameters.
-		{nil, ""},
-
-		// one parameter
-		{[]string{""}, ``},
-		{[]string{"/"}, `\`},
-		{[]string{"a"}, "a"},
-
-		// two parameters
-		{[]string{"a", "b"}, `a\b`},
-		{[]string{"a", ""}, "a"},
-		{[]string{"", "b"}, "b"},
-		{[]string{"/", "a"}, `\a`},
-		{[]string{"/", "a/b"}, `\a\b`},
-		{[]string{"/", ""}, `\`},
-		{[]string{"//", "a"}, `\a`},
-		{[]string{"/a", "b"}, `\a\b`},
-		{[]string{"a/", "b"}, `a\b`},
-		{[]string{"a/", ""}, "a"},
-		{[]string{"", ""}, ""},
-		// different from filepath.Join, it won't clean \..\.
-		{[]string{"a", "../b"}, `a\..\b`},
-
-		// tree parameters
-		{[]string{"/", "a", "b"}, `\a\b`},
-
-		{[]string{`directory`, `file`}, `directory\file`},
-		{[]string{`C:\Windows\`, `System32`}, `C:\Windows\System32`},
-		{[]string{`C:\Windows\`, ``}, `C:\Windows`},
-		{[]string{`C:\`, `Windows`}, `C:\Windows`},
-		{[]string{`C:`, `a`}, `C:a`},
-		{[]string{`C:`, `a\b`}, `C:a\b`},
-		{[]string{`C:`, `a`, `b`}, `C:a\b`},
-		{[]string{`C:.`, `a`}, `C:a`},
-		{[]string{`C:a`, `b`}, `C:a\b`},
-		{[]string{`C:a`, `b`, `d`}, `C:a\b\d`},
-
-		// TODO: UNC path.
-
-		{[]string{`\`}, `\`},
-		{[]string{`\`, ``}, `\`},
-		{[]string{`\`, `a`}, `\a`},
-		{[]string{`\\`, `a`}, `\a`},
-		{[]string{`\`, `a`, `b`}, `\a\b`},
-		{[]string{`\\`, `a`, `b`}, `\a\b`},
-		{[]string{`\`, `\\a\b`, `c`}, `\a\b\c`},
-		{[]string{`\\a`, `b`, `c`}, `\a\b\c`},
-		{[]string{`\\a\`, `b`, `c`}, `\a\b\c`},
-		// different from filepath.Join, it won't clean \..\.
-		{[]string{`a\..`, `b`}, `a\..\b`},
-	} {
-		got := Join(tc.elem...)
-		if got != tc.path {
-			t.Errorf("Join(%q)=%q; want=%q", tc.elem, got, tc.path)
-		}
-	}
-}
-
-func TestRel(t *testing.T) {
-	for _, tc := range []struct {
-		root, path, want string
-	}{
-		{"a/b", "a/b", "."},
-		{"a/b/.", "a/b", "."},
-		{"a/b", "a/b/.", "."},
-		{"./a/b", "a/b", "."},
-		{"a/b", "./a/b", "."},
-		{"ab/cd", "ab/cde", `..\cde`},
-		{"ab/cd", "ab/c", `..\c`},
-		{"a/b", "a/b/c/d", `c\d`},
-		{"a/b", "a/b/../c", `..\c`},
-		{"a/b/../c", "a/b", `..\b`},
-		{"a/b/c", "a/c/d", `..\..\c\d`},
-		{"a/b", "c/d", `..\..\c\d`},
-		{"a/b/c/d", "a/b", `..\..`},
-		{"a/b/c/d", "a/b/", `..\..`},
-		{"a/b/c/d/", "a/b", `..\..`},
-		{"a/b/c/d/", "a/b/", `..\..`},
-		{"../../a/b", "../../a/b/c/d", `c\d`},
-		{"/a/b", "/a/b", "."},
-		{"/a/b/.", "/a/b", "."},
-		{"/a/b", "/a/b/.", "."},
-		{"/ab/cd", "/ab/cde", `..\cde`},
-		{"/ab/cd", "/ab/c", `..\c`},
-		{"/a/b", "/a/b/c/d", `c\d`},
-		{"/a/b", "/a/b/../c", `..\c`},
-		{"/a/b/../c", "/a/b", `..\b`},
-		{"/a/b/c", "/a/c/d", `..\..\c\d`},
-		{"/a/b", "/c/d", `..\..\c\d`},
-		{"/a/b/c/d", "/a/b", `..\..`},
-		{"/a/b/c/d", "/a/b/", `..\..`},
-		{"/a/b/c/d/", "/a/b", `..\..`},
-		{"/a/b/c/d/", "/a/b/", `..\..`},
-		{"/../../a/b", "/../../a/b/c/d", `c\d`},
-		{".", "a/b", `a\b`},
-		{".", "..", ".."},
-
-		{`C:a\b\c`, `C:a/b/d`, `..\d`},
-		{`C:\a\b\c`, `C:\a\b\d`, `..\d`},
-		{`C:\a\b\c`, `\a\b\d`, `..\d`},
-		{`C:\a\b\c`, `/a/b/d`, `..\d`},
-		{`C:\Projects`, `C:\Projects\src`, `src`},
-		{`C:\Projects`, `C:\Projects`, `.`},
-	} {
-		got, err := Rel(tc.root, tc.path)
-		if err != nil || got != tc.want {
-			t.Errorf("Rel(%q, %q)=%q, %v; want %q, nil", tc.root, tc.path, got, err, tc.want)
-		}
-	}
-
-	// can't do purely lexically
-	for _, tc := range []struct {
-		root, path string
-	}{
-		{"..", "."},
-		{"..", "a"},
-		{"../..", ".."},
-		{"a", "/a"},
-		{"/a", "a"},
-
-		{`C:\`, `D:\`},
-		{`C:`, `D:`},
-		{`C:\Projects`, `c:\projects\src`},  // case mismatch
-		{`C:\Projects`, `c:\projects`},      // case mismatch
-		{`C:\Projects\a\..`, `c:\projects`}, // different from filepath.Rel
-	} {
-		got, err := Rel(tc.root, tc.path)
-		if err == nil {
-			t.Errorf("Rel(%q, %q)=%q, nil; want error", tc.root, tc.path, got)
-		}
-	}
-}
-
-func TestClean(t *testing.T) {
-	for _, tc := range []struct {
-		path, result string
-	}{
-		// https://golang.org/src/path/filepath/path_test.go
-
-		// Already clean
-		{"abc", "abc"},
-		{"abc/def", `abc\def`},
-		{"a/b/c", `a\b\c`},
-		{".", "."},
-		{"..", ".."},
-		{"../..", `..\..`},
-		{"../../abc", `..\..\abc`},
-		{"/abc", `\abc`},
-		{"/", `\`},
-
-		// Empty is current dir
-		{"", "."},
-
-		// Remove trailing slash
-		{"abc/", "abc"},
-		{"abc/def/", `abc\def`},
-		{"a/b/c/", `a\b\c`},
-		{"./", "."},
-		{"../", ".."},
-		{"../../", `..\..`},
-		{"/abc/", `\abc`},
-
-		// Remove doubled slash
-		{"abc//def//ghi", `abc\def\ghi`},
-		{"//abc", `\abc`},
-		{"///abc", `\abc`},
-		{"//abc//", `\abc`},
-		{"abc//", "abc"},
-
-		// Remove . elements
-		{"abc/./def", `abc\def`},
-		{"/./abc/def", `\abc\def`},
-		{"abc/.", "abc"},
-
-		// Remove .. elements
-		{"abc/def/ghi/../jkl", `abc\def\jkl`},
-		{"abc/def/../ghi/../jkl", `abc\jkl`},
-		{"abc/def/..", "abc"},
-		{"abc/def/../..", "."},
-		{"/abc/def/../..", `\`},
-		{"abc/def/../../..", ".."},
-		{"/abc/def/../../..", `\`},
-		{"abc/def/../../../ghi/jkl/../../../mno", `..\..\mno`},
-		{"/../abc", `\abc`},
-
-		// Combinations
-		{"abc/./../def", "def"},
-		{"abc//./../def", "def"},
-		{"abc/../../././../def", `..\..\def`},
-
-		// win path case
-		{`c:`, `c:.`},
-		{`c:\`, `c:\`},
-		{`c:\abc`, `c:\abc`},
-		{`c:abc\..\..\.\.\..\def`, `c:..\..\def`},
-		{`c:\abc\def\..\..`, `c:\`},
-		{`c:\..\abc`, `c:\abc`},
-		{`\`, `\`},
-
-		// TODO: UNC path.
-	} {
-		got := Clean(tc.path)
-		if got != tc.result {
-			t.Errorf("Clean(%q)=%q; want=%q", tc.path, got, tc.result)
-		}
-	}
-
-}
-
-func TestSplitElem(t *testing.T) {
-	for _, tc := range []struct {
-		path string
-		elem []string
-	}{
-		{"", nil},
-
-		{"/", []string{`\`}},
-		{`\`, []string{`\`}},
-		{"a", []string{"a"}},
-		{".", []string{"."}},
-
-		{"/.", []string{`\`, "."}},
-		{`\.`, []string{`\`, "."}},
-		{"////", []string{`\`}},
-		{`\\\\`, []string{`\`}},
-		{"////.", []string{`\`, "."}},
-		{`\\\\.`, []string{`\`, "."}},
-		{".////", []string{"."}},
-		{`.\\\\`, []string{"."}},
-
-		{"a/b", []string{"a", "b"}},
-		{`a\b`, []string{"a", "b"}},
-		{"/a/b", []string{`\`, "a", "b"}},
-		{`\a\b`, []string{`\`, "a", "b"}},
-		{"a/b/.", []string{"a", "b", "."}},
-		{`a\b\.`, []string{"a", "b", "."}},
-		{"a/b/", []string{"a", "b", "."}},
-		{`a\b\`, []string{"a", "b", "."}},
-
-		{"a//b", []string{"a", "b"}},
-		{`a\\/b`, []string{"a", "b"}},
-		{"//a", []string{`\`, "a"}},
-		{`\\a`, []string{`\`, "a"}},
-		{"a//", []string{"a", "."}},
-		{`a\\`, []string{"a", "."}},
-		{"a//.", []string{"a", "."}},
-		{`a\\/.`, []string{"a", "."}},
-		{"a/./b", []string{"a", "b"}},
-		{`a\.\b`, []string{"a", "b"}},
-		{"a/././b", []string{"a", "b"}},
-		{`a\.\.\b`, []string{"a", "b"}},
-
-		{"a/../b", []string{"a", "..", "b"}},
-		{`a\..\b`, []string{"a", "..", "b"}},
-
-		{"c:/a/b", []string{`c:\`, "a", "b"}},
-		{`c:\a/b`, []string{`c:\`, "a", "b"}},
-		{"c:a/b", []string{"c:", "a", "b"}},
-		{`c:\\\\`, []string{`c:\`}},
-	} {
-		got := SplitElem(tc.path)
-		if !reflect.DeepEqual(got, tc.elem) {
-			t.Errorf("SplitElem(%q)=%q; want=%q", tc.path, got, tc.elem)
-		}
-	}
-
-}
-
-func TestToPosix(t *testing.T) {
-	for _, tc := range []struct {
-		path string
-		want string
-	}{
-		{"a", "a"},
-		{`a\b`, "a/b"},
-		{"a/b", "a/b"},
-		{`a\b\c`, "a/b/c"},
-		{`a/b\c`, "a/b/c"},
-		{`\a\b\c`, "/a/b/c"},
-		{`c:\a`, "/a"},
-		{`c:\a\b\c`, "/a/b/c"},
-		{`c:a\b\c`, "a/b/c"},
-	} {
-		got := ToPosix(tc.path)
-		if got != tc.want {
-			t.Errorf("ToPosix(%q)=%q; want=%q", tc.path, got, tc.want)
-		}
-	}
-}
-
-func BenchmarkCleanAlreadyClean(b *testing.B) {
-	for i := 0; i < b.N; i++ {
-		Clean("../abc/def/ghi")
-		Clean(`..\abc\def\ghi`)
-	}
-}
-
-func BenchmarkCleanAlreadyCleanAbs(b *testing.B) {
-	for i := 0; i < b.N; i++ {
-		Clean("/abc/def/ghi")
-		Clean(`\abc\def\ghi`)
-		Clean("c:/abc/def/ghi")
-		Clean(`c:\abc\def\ghi`)
-	}
-}
-
-func BenchmarkCleanParentDir(b *testing.B) {
-	for i := 0; i < b.N; i++ {
-		Clean("abc/def/ghi/../jkl")
-		Clean(`abc\def\ghi\..\jkl`)
-	}
-}
-
-func BenchmarkCleanParentDirAbs(b *testing.B) {
-	for i := 0; i < b.N; i++ {
-		Clean("/abc/def/ghi/../jkl")
-		Clean(`\abc\def\ghi\..\jkl`)
-		Clean("c:/abc/def/ghi/../jkl")
-		Clean(`c:\abc\def\ghi\..\jkl`)
-	}
-}
diff --git a/command/doc.go b/command/doc.go
deleted file mode 100644
index 378b6ce..0000000
--- a/command/doc.go
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright 2017 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.
-
-/*
-Package command manages commands/toolchains.
-*/
-package command
diff --git a/command/fake_storage.go b/command/fake_storage.go
deleted file mode 100644
index 41b266a..0000000
--- a/command/fake_storage.go
+++ /dev/null
@@ -1,141 +0,0 @@
-// Copyright 2018 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.
-
-package command
-
-import (
-	"bytes"
-	"context"
-	"io"
-	"io/ioutil"
-	"sort"
-	"strings"
-	"time"
-
-	"cloud.google.com/go/storage"
-	"github.com/googleapis/google-cloud-go-testing/storage/stiface"
-	"google.golang.org/api/iterator"
-)
-
-type fakeObject struct {
-	stiface.ObjectHandle
-	data    []byte
-	updated time.Time
-}
-
-func (o *fakeObject) NewReader(context.Context) (stiface.Reader, error) {
-	return &fakeObjectReader{
-		size: len(o.data),
-		rc:   ioutil.NopCloser(bytes.NewReader(o.data)),
-	}, nil
-}
-
-type fakeObjectReader struct {
-	stiface.Reader
-	size int
-	rc   io.ReadCloser
-}
-
-func (r *fakeObjectReader) Size() int64 { return int64(r.size) }
-
-func (r *fakeObjectReader) Read(p []byte) (int, error) {
-	return r.rc.Read(p)
-}
-
-func (r *fakeObjectReader) Close() error {
-	return r.rc.Close()
-}
-
-type fakeObjIter struct {
-	stiface.ObjectIterator
-	attrs []*storage.ObjectAttrs
-}
-
-func (o *fakeObjIter) Next() (*storage.ObjectAttrs, error) {
-	if len(o.attrs) == 0 {
-		return nil, iterator.Done
-	}
-	oa, attrs := o.attrs[0], o.attrs[1:]
-	o.attrs = attrs
-	return oa, nil
-}
-
-type fakeStorageBucket struct {
-	stiface.BucketHandle
-	objs map[string]*fakeObject
-}
-
-func newFakeStorageBucket() *fakeStorageBucket {
-	return &fakeStorageBucket{
-		objs: make(map[string]*fakeObject),
-	}
-}
-
-func (sb *fakeStorageBucket) store(obj string, data []byte, ts time.Time) {
-	sb.objs[obj] = &fakeObject{
-		data:    data,
-		updated: ts,
-	}
-}
-
-func (sb *fakeStorageBucket) storeString(obj string, value string, ts time.Time) {
-	sb.store(obj, []byte(value), ts)
-}
-
-func (sb *fakeStorageBucket) Object(name string) stiface.ObjectHandle {
-	if sb.objs[name] == nil {
-		// Return explicit nil as interface value.
-		return nil
-	}
-	return sb.objs[name]
-}
-
-func (sb *fakeStorageBucket) Objects(ctx context.Context, query *storage.Query) stiface.ObjectIterator {
-	// Get stored objects in sorted order.
-	names := []string{}
-	for name := range sb.objs {
-		names = append(names, name)
-	}
-	sort.Strings(names)
-
-	iter := &fakeObjIter{}
-	for _, name := range names {
-		if !strings.HasPrefix(name, query.Prefix) {
-			continue
-		}
-		obj := sb.objs[name]
-		iter.attrs = append(iter.attrs, &storage.ObjectAttrs{
-			Name:    name,
-			Updated: obj.updated,
-		})
-	}
-	return iter
-}
-
-type fakeStorage struct {
-	stiface.Client
-	buckets map[string]*fakeStorageBucket
-}
-
-func newFakeStorage() *fakeStorage {
-	return &fakeStorage{
-		buckets: make(map[string]*fakeStorageBucket),
-	}
-}
-
-func (s *fakeStorage) createBucket(bucket string) *fakeStorageBucket {
-	// Only create bucket if none exists. If it exists, just return existing bucket.
-	if s.buckets[bucket] == nil {
-		s.buckets[bucket] = newFakeStorageBucket()
-	}
-	return s.buckets[bucket]
-}
-
-func (s *fakeStorage) Bucket(name string) stiface.BucketHandle {
-	if s.buckets[name] == nil {
-		// Return explicit nil as interface value.
-		return nil
-	}
-	return s.buckets[name]
-}
diff --git a/command/fake_storage_test.go b/command/fake_storage_test.go
deleted file mode 100644
index f245167..0000000
--- a/command/fake_storage_test.go
+++ /dev/null
@@ -1,108 +0,0 @@
-// Copyright 2018 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.
-
-package command
-
-import (
-	"context"
-	"io/ioutil"
-	"testing"
-	"time"
-
-	"cloud.google.com/go/storage"
-	"google.golang.org/api/iterator"
-)
-
-func TestFakeStorageBucket(t *testing.T) {
-	fs := newFakeStorage()
-	bkt := fs.createBucket("my_bucket")
-
-	if bkt == nil {
-		t.Errorf("Unable to create Bucket my_bucket")
-	}
-	testValues := []struct {
-		name    string
-		value   string
-		updated time.Time
-	}{
-		// The iterators will be sorted in order of `name`.
-		{
-			name:    "path/bar",
-			value:   "BAR",
-			updated: time.Date(2018, time.December, 20, 18, 07, 21, 0, time.UTC),
-		},
-		{
-			name:    "path/foo",
-			value:   "FOO",
-			updated: time.Date(2018, time.December, 20, 17, 06, 20, 0, time.UTC),
-		},
-	}
-
-	for _, tv := range testValues {
-		bkt.storeString(tv.name, tv.value, tv.updated)
-	}
-
-	ctx := context.Background()
-	t.Logf("Testing Object()")
-	for _, tv := range testValues {
-		obj := bkt.Object(tv.name)
-		if obj.(*fakeObject) == nil {
-			t.Errorf("Could not get Object %s", tv.name)
-		}
-		rd, err := obj.NewReader(ctx)
-		data, err := ioutil.ReadAll(rd)
-		rd.Close()
-		if err != nil {
-			t.Errorf("Error reading from Object %s: %v", tv.name, err)
-		}
-		if string(data) != tv.value {
-			t.Errorf("Contents of Object %s, got=%s, want=%s", tv.name, string(data), tv.value)
-		}
-	}
-	obj := bkt.Object("unknown")
-	if obj != nil {
-		// If this fails but prints obj=<nil>, it is because the return type was not
-		// a true nil (interface var had type info, but not value)
-		t.Errorf("Object(unknown)=%v, want=nil", obj)
-	}
-
-	t.Logf("Testing Objects()")
-	iter := bkt.Objects(ctx, &storage.Query{
-		Prefix: "path/",
-	})
-	for _, tv := range testValues {
-		attr, err := iter.Next()
-		if err != nil {
-			t.Errorf("Iterator.Next() err: got=%v, want=nil", err)
-		}
-		if attr.Name != tv.name {
-			t.Errorf("Iterator.Next() name: got=%s, want=%s", attr.Name, tv.name)
-		}
-		if attr.Updated != tv.updated {
-			t.Errorf("Iterator.Next() name: got=%v, want=%v", attr.Updated, tv.updated)
-		}
-	}
-	_, err := iter.Next()
-	if err != iterator.Done {
-		t.Errorf("Iterator.Next(): got %v, want %v", err, iterator.Done)
-	}
-}
-
-func TestFakeStorage(t *testing.T) {
-	fs := newFakeStorage()
-	foo := fs.Bucket("foo")
-	if foo != nil {
-		// If this fails but prints foo=<nil>, it is because the return type was not
-		// a true nil (interface var had type info, but not value)
-		t.Errorf("Bucket(foo)=%v, want=nil", foo)
-	}
-	foo = fs.createBucket("foo")
-	foo2 := fs.Bucket("foo")
-	if foo2 == nil {
-		t.Errorf("Bucket(foo)=nil, want=non-nil")
-	}
-	if foo != foo2 {
-		t.Errorf("Duplicate bucket foo: %v vs %v", &foo, &foo2)
-	}
-}
diff --git a/command/normalizer/doc.go b/command/normalizer/doc.go
deleted file mode 100644
index d25cd9f..0000000
--- a/command/normalizer/doc.go
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright 2017 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.
-
-/*
-Package normalizer provides functions to normalize target.
-*/
-package normalizer
diff --git a/command/normalizer/normalizer.go b/command/normalizer/normalizer.go
deleted file mode 100644
index 37ed333..0000000
--- a/command/normalizer/normalizer.go
+++ /dev/null
@@ -1,112 +0,0 @@
-// Copyright 2017 Google LLC. All Rights Reserved.
-
-package normalizer
-
-import (
-	"fmt"
-	"strings"
-
-	"google.golang.org/protobuf/proto"
-
-	cmdpb "go.chromium.org/goma/server/proto/command"
-)
-
-type target struct {
-	arch     string
-	archType string
-	vendor   string
-	os       string
-	env      string
-}
-
-func parseTarget(t string) (target, error) {
-	tokens := strings.Split(t, "-")
-	if len(tokens) < 2 || len(tokens) > 4 {
-		return target{}, fmt.Errorf("target has invalid token number: len=%d: t=%s", len(tokens), t)
-	}
-
-	var arch, archType, vendor, os, env string
-	arch = tokens[0]
-	switch {
-	case len(arch) == 4 && arch[0] == 'i' && arch[1] >= '0' && arch[1] <= '9' && arch[2:] == "86":
-		archType = "i686"
-	case arch == "amd64":
-		archType = "x86_64"
-	default:
-		archType = arch
-	}
-	i := len(tokens) - 1
-	switch tokens[i] {
-	case "eabi", "gnu", "gnueabi", "gnueabihf", "macho", "android", "androideabi", "uclibc", "msvc":
-		env = tokens[i]
-		i--
-	}
-	switch i {
-	case 3:
-		return target{}, fmt.Errorf("still have 4 tokens: %s", t)
-	case 2:
-		vendor = tokens[1]
-		os = tokens[2]
-	case 1:
-		os = tokens[1]
-	case 0:
-	default:
-		return target{}, fmt.Errorf("index is broken: %q index=%d", t, i)
-	}
-	return target{
-		arch:     arch,
-		archType: archType,
-		vendor:   vendor,
-		os:       os,
-		env:      env}, nil
-}
-
-func normalizedTargetString(t target) string {
-	ret := []string{t.archType}
-	// We'll only take care of vendor name if it is "cros" to avoid
-	// ambiguity on ChromeOS compilers (b/17324429).
-	// Otherwise, we ignore vendor names as it doesn't affect so much for
-	// the features of compilers.
-	if t.vendor == "cros" {
-		ret = append(ret, t.vendor)
-	}
-
-	if t.vendor == "apple" && strings.HasPrefix(t.os, "darwin") {
-		ret = append(ret, "darwin")
-	} else if t.os != "" {
-		ret = append(ret, t.os)
-	}
-
-	if t.env != "" && t.env != "gnu" {
-		ret = append(ret, t.env)
-	}
-	return strings.Join(ret, "-")
-}
-
-// Target converts a target to a normalized one.
-func Target(t string) (string, error) {
-	p, err := parseTarget(t)
-	if err != nil {
-		return "", err
-	}
-	return normalizedTargetString(p), nil
-}
-
-// Selector returns a selector whose target is normalized.
-func Selector(s *cmdpb.Selector) (*cmdpb.Selector, error) {
-	if s.Target == "" || s.Target == "java" {
-		return s, nil
-	}
-	// For MSVC, we don't have to normalize target.
-	if s.Name == "cl.exe" {
-		return s, nil
-	}
-
-	t, err := Target(s.GetTarget())
-	if err != nil {
-		return &cmdpb.Selector{}, err
-	}
-	s = proto.Clone(s).(*cmdpb.Selector)
-	s.Target = t
-	return s, nil
-}
diff --git a/command/normalizer/normalizer_test.go b/command/normalizer/normalizer_test.go
deleted file mode 100644
index 08199e7..0000000
--- a/command/normalizer/normalizer_test.go
+++ /dev/null
@@ -1,454 +0,0 @@
-// Copyright 2017 Google LLC. All Rights Reserved.
-
-package normalizer
-
-import (
-	"reflect"
-	"testing"
-
-	"google.golang.org/protobuf/proto"
-
-	cmdpb "go.chromium.org/goma/server/proto/command"
-)
-
-func TestParseTarget(t *testing.T) {
-	for _, tc := range []struct {
-		input     string
-		want      target
-		wantError bool
-	}{
-		{
-			input: "arm-eabi",
-			want: target{
-				arch:     "arm",
-				archType: "arm",
-				env:      "eabi",
-			},
-		},
-		{
-			input: "arm-linux-androideabi",
-			want: target{
-				arch:     "arm",
-				archType: "arm",
-				os:       "linux",
-				env:      "androideabi",
-			},
-		},
-		{
-			input: "arm-none-eabi",
-			want: target{
-				arch:     "arm",
-				archType: "arm",
-				os:       "none",
-				env:      "eabi",
-			},
-		},
-		{
-			input: "armv7a-cros-linux-gnueabi",
-			want: target{
-				arch:     "armv7a",
-				archType: "armv7a",
-				vendor:   "cros",
-				os:       "linux",
-				env:      "gnueabi",
-			},
-		},
-		{
-			input: "armv7a-cros-linux-gnueabihf",
-			want: target{
-				arch:     "armv7a",
-				archType: "armv7a",
-				vendor:   "cros",
-				os:       "linux",
-				env:      "gnueabihf",
-			},
-		},
-		{
-			input: "i486-linux-gnu",
-			want: target{
-				arch:     "i486",
-				archType: "i686",
-				os:       "linux",
-				env:      "gnu",
-			},
-		},
-		{
-			input: "i686-android-linux",
-			want: target{
-				arch:     "i686",
-				archType: "i686",
-				vendor:   "android",
-				os:       "linux",
-			},
-		},
-		{
-			input: "i686-apple-darwin11",
-			want: target{
-				arch:     "i686",
-				archType: "i686",
-				vendor:   "apple",
-				os:       "darwin11",
-			},
-		},
-		{
-			input: "i686-linux",
-			want: target{
-				arch:     "i686",
-				archType: "i686",
-				os:       "linux",
-			},
-		},
-		{
-			input: "i686-linux-android",
-			want: target{
-				arch:     "i686",
-				archType: "i686",
-				os:       "linux",
-				env:      "android",
-			},
-		},
-		{
-			input: "i686-pc-linux-gnu",
-			want: target{
-				arch:     "i686",
-				archType: "i686",
-				vendor:   "pc",
-				os:       "linux",
-				env:      "gnu",
-			},
-		},
-		{
-			input: "i686-unknown-linux-gnu",
-			want: target{
-				arch:     "i686",
-				archType: "i686",
-				vendor:   "unknown",
-				os:       "linux",
-				env:      "gnu",
-			},
-		},
-		{
-			input: "mipsel-linux-android",
-			want: target{
-				arch:     "mipsel",
-				archType: "mipsel",
-				os:       "linux",
-				env:      "android",
-			},
-		},
-		{
-			input: "mipsel-linux-uclibc",
-			want: target{
-				arch:     "mipsel",
-				archType: "mipsel",
-				os:       "linux",
-				env:      "uclibc",
-			},
-		},
-		{
-			input: "sh-linux-gnu",
-			want: target{
-				arch:     "sh",
-				archType: "sh",
-				os:       "linux",
-				env:      "gnu",
-			},
-		},
-		{
-			input: "x86_64-apple-darwin10.6.0",
-			want: target{
-				arch:     "x86_64",
-				archType: "x86_64",
-				vendor:   "apple",
-				os:       "darwin10.6.0",
-			},
-		},
-		{
-			input: "x86_64-apple-darwin16.5.0",
-			want: target{
-				arch:     "x86_64",
-				archType: "x86_64",
-				vendor:   "apple",
-				os:       "darwin16.5.0",
-			},
-		},
-		{
-			input: "x86_64-cros-linux-gnu",
-			want: target{
-				arch:     "x86_64",
-				archType: "x86_64",
-				vendor:   "cros",
-				os:       "linux",
-				env:      "gnu",
-			},
-		},
-		{
-			input: "x86_64-linux",
-			want: target{
-				arch:     "x86_64",
-				archType: "x86_64",
-				os:       "linux",
-			},
-		},
-		{
-			input: "x86_64-linux-gnu",
-			want: target{
-				arch:     "x86_64",
-				archType: "x86_64",
-				os:       "linux",
-				env:      "gnu",
-			},
-		},
-		{
-			input: "x86_64-nacl",
-			want: target{
-				arch:     "x86_64",
-				archType: "x86_64",
-				os:       "nacl",
-			},
-		},
-		{
-			input: "x86_64-pc-linux-gnu",
-			want: target{
-				arch:     "x86_64",
-				archType: "x86_64",
-				vendor:   "pc",
-				os:       "linux",
-				env:      "gnu",
-			},
-		},
-		{
-			input: "x86_64-unknown-linux-gnu",
-			want: target{
-				arch:     "x86_64",
-				archType: "x86_64",
-				vendor:   "unknown",
-				os:       "linux",
-				env:      "gnu",
-			},
-		},
-		{
-			input: "i386-pc-windows-msvc",
-			want: target{
-				arch:     "i386",
-				archType: "i686",
-				vendor:   "pc",
-				os:       "windows",
-				env:      "msvc",
-			},
-		},
-		{
-			input: "x86_64-pc-windows-msvc",
-			want: target{
-				arch:     "x86_64",
-				archType: "x86_64",
-				vendor:   "pc",
-				os:       "windows",
-				env:      "msvc",
-			},
-		},
-		// error cases
-		{
-			input:     "x86_64",
-			wantError: true,
-		},
-		{
-			input:     "x86_64-too-long-target-name-but-linux-msvc",
-			wantError: true,
-		},
-		{
-			input:     "x86_64-dummy-long-target",
-			wantError: true,
-		},
-	} {
-		result, err := parseTarget(tc.input)
-		if tc.wantError && err == nil {
-			t.Errorf("parseTarget(%q)=_,nil; want err", tc.input)
-		}
-		if !tc.wantError && err != nil {
-			t.Errorf("parseTarget(%q)=_,%v; want nil", tc.input, err)
-		}
-		if err == nil && !reflect.DeepEqual(result, tc.want) {
-			t.Errorf("parseTarget(%q)=%q; want %q", tc.input, result, tc.want)
-		}
-	}
-}
-
-func TestNormalizedTargetString(t *testing.T) {
-	for _, tc := range []struct {
-		input target
-		want  string
-	}{
-		{
-			input: target{
-				arch:     "x86_64",
-				archType: "x86_64",
-				vendor:   "pc",
-				os:       "linux",
-				env:      "gnu",
-			},
-			want: "x86_64-linux",
-		},
-		{
-			input: target{
-				arch:     "x86_64",
-				archType: "x86_64",
-				vendor:   "cros",
-				os:       "linux",
-				env:      "gnu",
-			},
-			want: "x86_64-cros-linux",
-		},
-		{
-			input: target{
-				arch:     "i486",
-				archType: "i686",
-				vendor:   "pc",
-				os:       "windows",
-				env:      "msvc",
-			},
-			want: "i686-windows-msvc",
-		},
-		{
-			input: target{
-				arch:     "x86_64",
-				archType: "x86_64",
-				vendor:   "apple",
-				os:       "darwin16.5.0",
-			},
-			want: "x86_64-darwin",
-		},
-	} {
-		actual := normalizedTargetString(tc.input)
-		if actual != tc.want {
-			t.Errorf("normalizedTargetString(%q)=%q; want %q", tc.input, actual, tc.want)
-		}
-	}
-}
-
-func TestSelector(t *testing.T) {
-	for _, tc := range []struct {
-		input     *cmdpb.Selector
-		want      *cmdpb.Selector
-		wantError bool
-	}{
-		{
-			input: &cmdpb.Selector{
-				Name:       "clang",
-				Version:    "4.2.1[clang version 5.0.0 (trunk 300839)]",
-				Target:     "x86_64-unknown-linux-gnu",
-				BinaryHash: "5f650cc98121b383aaa25e53a135d8b4c5e0748f25082b4f2d428a5934d22fda",
-			},
-			want: &cmdpb.Selector{
-				Name:       "clang",
-				Version:    "4.2.1[clang version 5.0.0 (trunk 300839)]",
-				Target:     "x86_64-linux",
-				BinaryHash: "5f650cc98121b383aaa25e53a135d8b4c5e0748f25082b4f2d428a5934d22fda",
-			},
-		},
-		{
-			input: &cmdpb.Selector{
-				Name:       "clang",
-				Version:    "4.2.1[clang version 5.0.0 (trunk 300839)]",
-				BinaryHash: "5f650cc98121b383aaa25e53a135d8b4c5e0748f25082b4f2d428a5934d22fda",
-			},
-			want: &cmdpb.Selector{
-				Name:       "clang",
-				Version:    "4.2.1[clang version 5.0.0 (trunk 300839)]",
-				BinaryHash: "5f650cc98121b383aaa25e53a135d8b4c5e0748f25082b4f2d428a5934d22fda",
-			},
-		},
-		{
-			input: &cmdpb.Selector{
-				Name:       "javac",
-				Target:     "java",
-				Version:    "1.8.0_45-internal",
-				BinaryHash: "609aeefbab4b988d1a3705a3da442590c6f22aa8f27036f8a08deaabd3714c27",
-			},
-			want: &cmdpb.Selector{
-				Name:       "javac",
-				Target:     "java",
-				Version:    "1.8.0_45-internal",
-				BinaryHash: "609aeefbab4b988d1a3705a3da442590c6f22aa8f27036f8a08deaabd3714c27",
-			},
-		},
-		{
-			input: &cmdpb.Selector{
-				Name:       "cl.exe",
-				Target:     "x64",
-				Version:    "19.11.25505",
-				BinaryHash: "5d734edd36be5be66be72f543522e95368a88da687467b5797137e47cbdeecd0",
-			},
-			want: &cmdpb.Selector{
-				Name:       "cl.exe",
-				Target:     "x64",
-				Version:    "19.11.25505",
-				BinaryHash: "5d734edd36be5be66be72f543522e95368a88da687467b5797137e47cbdeecd0",
-			},
-		},
-		{
-			input: &cmdpb.Selector{
-				Name:       "clang",
-				Version:    "4.2.1[clang version 5.0.0 (trunk 300839)]",
-				Target:     "x86_64-unknown-linux-gnu-should-parse-error",
-				BinaryHash: "5f650cc98121b383aaa25e53a135d8b4c5e0748f25082b4f2d428a5934d22fda",
-			},
-			wantError: true,
-		},
-	} {
-		actual, err := Selector(tc.input)
-		if err != nil && !tc.wantError {
-			t.Errorf("Selector(%v)=_,%v; want nil", tc.input, err)
-		}
-		if err == nil && tc.wantError {
-			t.Errorf("Selector(%v)=_,nil; want err", tc.input)
-		}
-		if err == nil && !proto.Equal(actual, tc.want) {
-			t.Errorf("Selector(%v)=%v; want %v", tc.input, &actual, tc.want)
-		}
-	}
-}
-
-func TestTarget(t *testing.T) {
-	for _, tc := range []struct {
-		input     string
-		want      string
-		wantError bool
-	}{
-		{
-			input: "x86_64-unknown-linux-gnu",
-			want:  "x86_64-linux",
-		},
-		{
-			input: "x86_64-apple-darwin16.5.0",
-			want:  "x86_64-darwin",
-		},
-		{
-			input: "x86_64--nacl",
-			want:  "x86_64-nacl",
-		},
-		{
-			input: "x86_64--darwin",
-			want:  "x86_64-darwin",
-		},
-		{
-			input: "le32-unknown-nacl",
-			want:  "le32-nacl",
-		},
-		{
-			input:     "x86_64-unknown-linux-gnu-should-parse-error",
-			wantError: true,
-		},
-	} {
-		actual, err := Target(tc.input)
-		if err != nil && !tc.wantError {
-			t.Errorf("Target(%s)=_,%v; want nil", tc.input, err)
-		}
-		if err == nil && tc.wantError {
-			t.Errorf("Target(%s)=_,nil; want err", tc.input)
-		}
-		if err == nil && actual != tc.want {
-			t.Errorf("Target(%s)=%s; want %s", tc.input, actual, tc.want)
-		}
-	}
-}
diff --git a/command/pathconv/converter.go b/command/pathconv/converter.go
deleted file mode 100644
index bea28bd..0000000
--- a/command/pathconv/converter.go
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2017 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.
-
-package pathconv
-
-// PathConverter provides path conversion between a client path and a server path.
-// It also provides client path utilities.
-type PathConverter interface {
-	// ToClientPath converts a server path to a client path.
-	ToClientPath(string) (string, error)
-
-	// ToServerPath converts an absolute client path to a server path.
-	ToServerPath(string) (string, error)
-
-	// IsAbsClient reports whether the path is absolute as client path.
-	IsAbsClient(string) bool
-
-	// JoinClient joins any number of path elements into a single path.
-	JoinClient(elem ...string) string
-
-	// IsSafeClient reports whether the path is safe absolute path
-	// (not go out from root directory).
-	// It returns false for relative path.
-	IsSafeClient(string) bool
-}
diff --git a/command/pathconv/doc.go b/command/pathconv/doc.go
deleted file mode 100644
index 31c6cff..0000000
--- a/command/pathconv/doc.go
+++ /dev/null
@@ -1,11 +0,0 @@
-// Copyright 2017 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.
-
-/*
-Package pathconv provides path converter between client and server.
-
-TODO: Do we want to introduce ClientPath and ServerPath type so that
-we won't be confused the path is client path or server path?
-*/
-package pathconv
diff --git a/command/pathconv/nop.go b/command/pathconv/nop.go
deleted file mode 100644
index aab0693..0000000
--- a/command/pathconv/nop.go
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright 2017 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.
-
-package pathconv
-
-import (
-	"strings"
-
-	"go.chromium.org/goma/server/command/descriptor/posixpath"
-)
-
-type nopPathConverter struct{}
-
-// Default returns a path converter for the case server and client are both same posix path namesystem.
-func Default() PathConverter {
-	return nopPathConverter{}
-}
-
-func (nopPathConverter) ToClientPath(path string) (string, error) {
-	return path, nil
-}
-
-func (nopPathConverter) ToServerPath(path string) (string, error) {
-	return path, nil
-}
-
-func (nopPathConverter) IsAbsClient(path string) bool {
-	return posixpath.IsAbs(path)
-}
-
-func (nopPathConverter) JoinClient(elem ...string) string {
-	return posixpath.Join(elem...)
-}
-
-func (nopPathConverter) IsSafeClient(path string) bool {
-	if !posixpath.IsAbs(path) {
-		return false
-	}
-	elems := strings.Split(path, "/")
-	depth := 0
-	for _, elem := range elems[1:] {
-		switch elem {
-		case "", ".":
-			continue
-		case "..":
-			depth--
-			if depth < 0 {
-				return false
-			}
-		default:
-			depth++
-		}
-	}
-
-	return true
-}
diff --git a/command/pathconv/nop_test.go b/command/pathconv/nop_test.go
deleted file mode 100644
index 3f07078..0000000
--- a/command/pathconv/nop_test.go
+++ /dev/null
@@ -1,106 +0,0 @@
-// Copyright 2017 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.
-
-package pathconv
-
-import (
-	"testing"
-)
-
-func TestPathConverter(t *testing.T) {
-	testcases := []struct {
-		clientPath string
-		serverPath string
-	}{
-		{
-			clientPath: "",
-			serverPath: "",
-		},
-		{
-			clientPath: "/tmp/foo.c",
-			serverPath: "/tmp/foo.c",
-		},
-	}
-
-	c := Default()
-	for _, tc := range testcases {
-		got, err := c.ToServerPath(tc.clientPath)
-		if err != nil {
-			t.Errorf("ToServerPath(%q)=_,%v; want nil", tc.clientPath, err)
-		}
-		if got != tc.serverPath {
-			t.Errorf("ToServerPath(%q)=%q,_; want %q", tc.clientPath, got, tc.serverPath)
-		}
-	}
-
-	for _, tc := range testcases {
-		got, err := c.ToClientPath(tc.serverPath)
-		if err != nil {
-			t.Errorf("ToClientPath(%q)=_,%v; want nil", tc.serverPath, err)
-		}
-		if got != tc.serverPath {
-			t.Errorf("ToClientPath(%q)=%q,_; want %q", tc.serverPath, got, tc.clientPath)
-		}
-	}
-}
-
-func TestIsSafeClient(t *testing.T) {
-	testcases := []struct {
-		path string
-		safe bool
-	}{
-		{
-			path: "",
-			safe: false,
-		},
-		{
-			path: "/",
-			safe: true,
-		},
-		{
-			path: "a",
-			safe: false,
-		},
-		{
-			path: "a/b",
-			safe: false,
-		},
-		{
-			path: "/b",
-			safe: true,
-		},
-		{
-			path: "/a/b",
-			safe: true,
-		},
-		{
-			path: "a/b/c",
-			safe: false,
-		},
-		{
-			path: "a/../../b/./..",
-			safe: false,
-		},
-		{
-			path: "/../../",
-			safe: false,
-		},
-		{
-			path: "/../../a/b/",
-			safe: false,
-		},
-		{
-			path: "/a/../..",
-			safe: false,
-		},
-	}
-
-	c := Default()
-	for _, tc := range testcases {
-		got := c.IsSafeClient(tc.path)
-		if got != tc.safe {
-			t.Errorf("IsSafeClient(%q)=%t; want %t", tc.path, got, tc.safe)
-		}
-	}
-}
diff --git a/exec/client.go b/exec/client.go
deleted file mode 100644
index 525ea3d..0000000
--- a/exec/client.go
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright 2017 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.
-
-package exec
-
-import (
-	"context"
-
-	"go.opencensus.io/trace"
-	"google.golang.org/grpc"
-
-	gomapb "go.chromium.org/goma/server/proto/api"
-	pb "go.chromium.org/goma/server/proto/exec"
-)
-
-// TODO: expvar counter
-
-// DefaultMaxReqMsgSize is max request message size for exec serivce.
-// exec server may recieve (exec client may send) > 45MB
-// if there are many embedded content.
-// request from gce staging bot, 45MB is used.
-// 64MB might not be sufficient enough.
-// grpc's default is 4MB.
-const DefaultMaxReqMsgSize = 64 * 1024 * 1024
-
-// DefaultMaxRespMsgSize is max response size of exec service.
-// exec server may send (exec client may receive) > 4MB.
-// e.g. 2 outputs may close to 2MB (*.o, *.dwo) +
-// other outputs (*.o.d, stdout, stderr).  http://b/79554706
-// grpc's default is 4MB.
-const DefaultMaxRespMsgSize = 6 * 1024 * 1024
-
-// Client is a client to access exec service via gRPC.
-type Client struct {
-	addr     string
-	dialOpts []grpc.DialOption
-}
-
-// NewClient creates new client to access exec service serving on address.
-func NewClient(address string, opts ...grpc.DialOption) Client {
-	return Client{
-		addr:     address,
-		dialOpts: opts,
-	}
-}
-
-// Exec handles goma Exec requests.
-func (c Client) Exec(ctx context.Context, in *gomapb.ExecReq, opts ...grpc.CallOption) (*gomapb.ExecResp, error) {
-	ctx, span := trace.StartSpan(ctx, "go.chromium.org/goma/server/exec.Client.Exec")
-	defer span.End()
-	conn, err := grpc.DialContext(ctx, c.addr,
-		append([]grpc.DialOption{
-			grpc.WithBlock(),
-		}, c.dialOpts...)...)
-	if err != nil {
-		return nil, err
-	}
-	defer conn.Close()
-
-	resp, err := pb.NewExecServiceClient(conn).Exec(ctx, in,
-		append([]grpc.CallOption{
-			grpc.MaxCallSendMsgSize(DefaultMaxReqMsgSize),
-			grpc.MaxCallRecvMsgSize(DefaultMaxRespMsgSize),
-		}, opts...)...)
-
-	return resp, err
-}
diff --git a/exec/doc.go b/exec/doc.go
deleted file mode 100644
index d38a0f0..0000000
--- a/exec/doc.go
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright 2017 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.
-
-/*
-Package exec provides goma exec service implementation.
-*/
-package exec
diff --git a/exec/inventory.go b/exec/inventory.go
deleted file mode 100644
index 17ddd47..0000000
--- a/exec/inventory.go
+++ /dev/null
@@ -1,669 +0,0 @@
-// Copyright 2017 Google LLC. All Rights Reserved.
-
-package exec
-
-import (
-	"context"
-	"errors"
-	"fmt"
-	"net/http"
-	"path/filepath"
-	"sort"
-	"strings"
-	"sync"
-	"time"
-
-	"go.opencensus.io/stats"
-	"go.opencensus.io/stats/view"
-	"go.opencensus.io/tag"
-	"go.opencensus.io/trace"
-	"google.golang.org/protobuf/proto"
-
-	"go.chromium.org/goma/server/auth/enduser"
-	"go.chromium.org/goma/server/command/descriptor"
-	"go.chromium.org/goma/server/command/descriptor/winpath"
-	"go.chromium.org/goma/server/command/normalizer"
-	"go.chromium.org/goma/server/log"
-	gomapb "go.chromium.org/goma/server/proto/api"
-	cmdpb "go.chromium.org/goma/server/proto/command"
-)
-
-var (
-	toolchainSelects = stats.Int64(
-		"go.chromium.org/goma/server/exec.toolchain-selects",
-		"Toolchain selection",
-		stats.UnitDimensionless)
-
-	selectorKey = tag.MustNewKey("selector")
-	resultKey   = tag.MustNewKey("result")
-
-	// DefaultToolchainViews are the default views provided by this package.
-	// You need to register the view for data to actually be collected.
-	DefaultToolchainViews = []*view.View{
-		{
-			Description: `counts toolchain selection. result is "used", "found", "requested" or "missed"`,
-			TagKeys: []tag.Key{
-				selectorKey,
-				resultKey,
-			},
-			Measure:     toolchainSelects,
-			Aggregation: view.Count(),
-		},
-	}
-)
-
-const (
-	maxKeyLength = 255
-	// Printable ASCII characters range from 0x20 to 0x7e
-	validKeyValueMin = 32
-	validKeyValueMax = 126
-)
-
-type resultValue string
-
-const (
-	// toolchain is requested, but not registered.
-	resultMissed resultValue = "missed"
-
-	// toolchain (subprogram) is requested, but not checked
-	// because command missed.
-	resultRequested resultValue = "requested"
-
-	// toolchain is requested and registered, but not used
-	// because some other toolchain in the same request missed.
-	resultFound resultValue = "found"
-
-	// toolchain is requested and used.
-	resultUsed resultValue = "used"
-)
-
-func recordToolchainSelect(ctx context.Context, s selector, result resultValue) error {
-	// tag string cannot be over 255 or containing non-printable ascii characters.
-	// https://github.com/census-instrumentation/opencensus-go/blob/264a2a48d94c062252389fffbc308ba555e35166/tag/validate.go
-	tagNormalizer := func(tag string) string {
-		if len(tag) > maxKeyLength {
-			tag = tag[:maxKeyLength]
-		}
-		buf := []rune(tag)
-		for i, v := range buf {
-			if validKeyValueMin > v || v > validKeyValueMax {
-				buf[i] = '_'
-			}
-		}
-		return string(buf)
-	}
-
-	// selector string can be too long, more than tag value limit
-	// (255 ASCII characters).
-	// http://b/115441117
-	var buf strings.Builder
-	fmt.Fprintf(&buf, "n:%s", s.Name)
-	fmt.Fprintf(&buf, " t:%s", s.Target)
-	fmt.Fprintf(&buf, " b:%s", s.BinaryHash)
-	fmt.Fprintf(&buf, " v:%s", s.Version)
-
-	ctx, err := tag.New(ctx,
-		tag.Upsert(selectorKey, tagNormalizer(buf.String())),
-		tag.Upsert(resultKey, tagNormalizer(string(result))))
-	if err != nil {
-		return err
-	}
-	stats.Record(ctx, toolchainSelects.M(1))
-	return nil
-}
-
-// Inventory holds available command configs.
-type Inventory struct {
-	mu        sync.RWMutex
-	versionID string
-	// map from selector -> slice of addresses.
-	addrs map[selector][]string
-	// map from address -> selector -> config.
-	configs map[string]map[selector]*cmdpb.Config
-	// config for arbitrary toolchain support.
-	platformConfigs []*platformConfig
-}
-
-type selector struct {
-	Name       string
-	Version    string
-	Target     string
-	BinaryHash string
-}
-
-func fromSelectorProto(s *cmdpb.Selector) selector {
-	return selector{
-		Name:       s.Name,
-		Version:    s.Version,
-		Target:     s.Target,
-		BinaryHash: s.BinaryHash,
-	}
-}
-
-func (s selector) Proto() *cmdpb.Selector {
-	return &cmdpb.Selector{
-		Name:       s.Name,
-		Version:    s.Version,
-		Target:     s.Target,
-		BinaryHash: s.BinaryHash,
-	}
-}
-
-func (s selector) String() string {
-	return s.Proto().String()
-}
-
-type byName []selector
-
-func (s byName) Len() int      { return len(s) }
-func (s byName) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
-func (s byName) Less(i, j int) bool {
-	return s[i].String() < s[j].String()
-}
-
-// platformConfig is for arbitrary toolchain support.
-type platformConfig struct {
-	dimensionSet       map[string]bool
-	remoteexecPlatform *cmdpb.RemoteexecPlatform
-	acl                *cmdpb.ACL
-}
-
-func numConfigs(configs map[string]map[selector]*cmdpb.Config) int {
-	n := 0
-	for _, m := range configs {
-		n += len(m)
-	}
-	return n
-}
-
-// Configure sets config in the inventory.
-func (in *Inventory) Configure(ctx context.Context, cfgs *cmdpb.ConfigResp) error {
-	ctx, span := trace.StartSpan(ctx, "go.chromium.org/goma/server/exec.Service.Configure")
-	defer span.End()
-	logger := log.FromContext(ctx)
-	newAddrs := make(map[selector][]string)
-	newConfigs := make(map[string]map[selector]*cmdpb.Config)
-	var newPlatformConfigs []*platformConfig
-	for _, cfg := range cfgs.Configs {
-		// If RemoteexecPlatform exists but CmdDescriptor does not exists,
-		// this config is for arbitrary toolchain support.
-		// TODO: Split this from ConfigResp.Configs?
-		if cfg.CmdDescriptor == nil && cfg.RemoteexecPlatform != nil {
-			dimensionSet := make(map[string]bool)
-			for _, d := range cfg.GetDimensions() {
-				dimensionSet[d] = true
-			}
-			newPlatformConfigs = append(newPlatformConfigs, &platformConfig{
-				dimensionSet:       dimensionSet,
-				remoteexecPlatform: cfg.GetRemoteexecPlatform(),
-				acl:                cfg.GetAcl(),
-			})
-			logger.Infof("configure platform config: %v", cfg)
-			continue
-		}
-
-		if cfg.Target == nil {
-			logger.Warnf("no target in %s", cfg)
-			continue
-		}
-		if cfg.Target.Addr == "" {
-			logger.Warnf("no target address in %s", cfg)
-			continue
-		}
-		if cfg.CmdDescriptor == nil || cfg.CmdDescriptor.Selector == nil {
-			logger.Warnf("no cmd descriptor in %s", cfg)
-			continue
-		}
-		selpb, err := normalizer.Selector(cfg.CmdDescriptor.Selector)
-		if err != nil {
-			logger.Errorf("failed to normalize selector in %s", cfg)
-			continue
-		}
-		sel := fromSelectorProto(selpb)
-		if cfg.CmdDescriptor.GetSetup().GetPathType() == cmdpb.CmdDescriptor_UNKNOWN_PATH_TYPE {
-			logger.Errorf("unknown path type in %s %s", sel, cfg)
-			continue
-		}
-		addr := cfg.Target.Addr
-		newAddrs[sel] = append(newAddrs[sel], addr)
-		m, ok := newConfigs[addr]
-		if !ok {
-			newConfigs[addr] = make(map[selector]*cmdpb.Config)
-			m = newConfigs[addr]
-		}
-		m[sel] = cfg
-		logger.Infof("configure %s: %s => %v", sel, addr, cfg)
-	}
-	in.mu.Lock()
-	defer in.mu.Unlock()
-	n0 := numConfigs(in.configs)
-	n1 := numConfigs(newConfigs)
-	logger.Infof("configure %s:%d -> %s:%d", in.versionID, n0, cfgs.VersionId, n1)
-	if diff := n0 - n1; n0 != 0 && 100*diff/n0 > 1 {
-		ratio := 100 * diff / n0
-		// mitigate for https://bugs.chromium.org/p/chromium/issues/detail?id=1243381
-		// if configs reduced more than 1%, reject it, and
-		// retry load.
-		// if it is a real removal, restaring server can forget
-		// the old one.
-		return fmt.Errorf("too many configs will be removed: %d -> %d: -%d%%. keep old ones.  Please restart the server if the config removal is intended", n0, n1, ratio)
-	}
-	in.versionID = cfgs.VersionId
-	in.addrs = newAddrs
-	in.configs = newConfigs
-	in.platformConfigs = newPlatformConfigs
-	if len(in.configs) == 0 && len(in.platformConfigs) == 0 {
-		return fmt.Errorf("no available config in %s", cfgs.VersionId)
-	}
-	return nil
-}
-
-func (in *Inventory) VersionID() string {
-	in.mu.RLock()
-	defer in.mu.RUnlock()
-	return in.versionID
-}
-
-func (in *Inventory) status() (string, []*cmdpb.Config) {
-	in.mu.RLock()
-	defer in.mu.RUnlock()
-	// sort by addr
-	var addrs []string
-	for a := range in.configs {
-		addrs = append(addrs, a)
-	}
-	sort.Strings(addrs)
-
-	var resp []*cmdpb.Config
-	// sort by selector
-	for _, a := range addrs {
-		var sels []selector
-		m := in.configs[a]
-		for sel := range m {
-			sels = append(sels, sel)
-		}
-		sort.Sort(byName(sels))
-		for _, sel := range sels {
-			resp = append(resp, proto.Clone(m[sel]).(*cmdpb.Config))
-		}
-	}
-	return in.versionID, resp
-}
-
-func checkACL(ctx context.Context, acl *cmdpb.ACL) error {
-	if acl == nil {
-		return nil
-	}
-	eu, ok := enduser.FromContext(ctx)
-	if len(acl.DisallowedGroups) > 0 {
-		if !ok {
-			return errors.New("no enduser group in context")
-		}
-		for _, g := range acl.DisallowedGroups {
-			if g == eu.Group {
-				return fmt.Errorf("enduser group %q not allowed (in disallowed groups)", eu.Group)
-			}
-		}
-	}
-	if len(acl.AllowedGroups) > 0 {
-		if !ok {
-			return errors.New("no enduser group in context")
-		}
-		for _, g := range acl.AllowedGroups {
-			if g == eu.Group {
-				return nil
-			}
-		}
-		return fmt.Errorf("enduser group %q not allowed (not in allowed groups)", eu.Group)
-	}
-	return nil
-}
-
-// pickCmd takes selectors of compiler and subprograms, and returns configs of
-// the best cmd_server that has both compiler and subprograms.
-// First, it find out cmd_server that has both selectors of compiler and
-// subprograms. (Step 1. and Step 2.)
-// Then, it picks cmd_server whose compiler's build time is latest. (Step 3.)
-func (in *Inventory) pickCmd(ctx context.Context, cmdSel selector, subprogSels []selector) (*cmdpb.Config, map[selector]*cmdpb.Config, error) {
-	logger := log.FromContext(ctx)
-	in.mu.RLock()
-	defer in.mu.RUnlock()
-
-	record := func(ctx context.Context, s selector, result resultValue) {
-		err := recordToolchainSelect(ctx, s, result)
-		if err != nil {
-			logger.Errorf("failed to record stats: %s=%s, err: %v", s, result, err)
-		}
-	}
-
-	// 1. command spec selector -> addresses
-	addrs, ok := in.addrs[cmdSel]
-	if !ok {
-		record(ctx, cmdSel, resultMissed)
-		for _, s := range subprogSels {
-			record(ctx, s, resultRequested)
-		}
-		return nil, nil, fmt.Errorf("no compiler for %v", cmdSel)
-	}
-
-	// 2. choose configs that has all subprograms
-	var ccfgs []*cmdpb.Config
-	subprogResult := make(map[selector]resultValue)
-	for _, s := range subprogSels {
-		subprogResult[s] = resultMissed
-	}
-Loop:
-	for _, a := range addrs {
-		m, ok := in.configs[a]
-		if !ok {
-			logger.Errorf("unknown address (%s) is given.", a)
-			continue
-		}
-		for _, s := range subprogSels {
-			if _, ok := m[s]; !ok {
-				logger.Infof("cfg for %v is not registered in %s.", s, a)
-				continue Loop
-			}
-			subprogResult[s] = resultFound
-		}
-		cfg, ok := m[cmdSel]
-		if !ok {
-			logger.Errorf("cfg for %v is not registered. possibly configs broken.", cmdSel)
-			continue
-		}
-		if err := checkACL(ctx, cfg.Acl); err != nil {
-			logger.Errorf("cfg for %v; access denied: %v", cmdSel, err)
-			continue
-		}
-		ccfgs = append(ccfgs, cfg)
-	}
-	if len(ccfgs) == 0 {
-		record(ctx, cmdSel, resultFound)
-		for s, r := range subprogResult {
-			record(ctx, s, r)
-		}
-		return nil, nil, fmt.Errorf("no matching backend found for %v", cmdSel)
-	}
-
-	for _, cfg := range ccfgs {
-		err := cfg.GetBuildInfo().GetTimestamp().CheckValid()
-		if err != nil {
-			logger.Warnf("invalid timestamp in %v: %v", cfg, err)
-		}
-	}
-
-	// 3. choose the latest compiler config.
-	sort.Slice(ccfgs, func(i, j int) bool {
-		var ti, tj time.Time
-		tsi := ccfgs[i].GetBuildInfo().GetTimestamp()
-		if tsi.IsValid() {
-			ti = tsi.AsTime()
-		}
-		tsj := ccfgs[j].GetBuildInfo().GetTimestamp()
-		if tsj.IsValid() {
-			tj = tsj.AsTime()
-		}
-		return ti.Before(tj)
-	})
-	ccfg := ccfgs[len(ccfgs)-1]
-
-	record(ctx, cmdSel, resultUsed)
-	for _, s := range subprogSels {
-		record(ctx, s, resultUsed)
-	}
-	return ccfg, in.configs[ccfg.Target.Addr], nil
-}
-
-// Pick picks command and subprograms requested in req, and
-// returns config, selector and commands' FileSpec.
-// It also update resp.Result about compiler selection.
-func (in *Inventory) Pick(ctx context.Context, req *gomapb.ExecReq, resp *gomapb.ExecResp) (*cmdpb.Config, []*cmdpb.FileSpec, error) {
-	ctx, span := trace.StartSpan(ctx, "go.chromium.org/goma/server/exec.Service.pick")
-	defer span.End()
-	logger := log.FromContext(ctx)
-
-	if req.GetToolchainIncluded() {
-		// If toolchain is included in ExecReq, pick from ExecReq.
-		return in.pickFromExecReq(ctx, req, resp)
-	}
-
-	cmdSel, cmdPath, err := fromCommandSpec(req.GetCommandSpec())
-	if err != nil {
-		resp.Error = gomapb.ExecResp_BAD_REQUEST.Enum()
-		return nil, nil, fmt.Errorf("normalize %v: %v", req.GetCommandSpec(), err)
-	}
-	span.AddAttributes(
-		trace.StringAttribute("command_spec", req.GetCommandSpec().String()),
-		trace.StringAttribute("selector", cmdSel.String()),
-	)
-
-	var sSels []selector
-	path2sel := make(map[string]selector)
-	for _, sp := range req.GetSubprogram() {
-		ss := fromSubprogramSpec(sp)
-		if _, found := path2sel[sp.GetPath()]; found {
-			logger.Warnf("subprogram duplicated? skipped: path=%s", sp.GetPath())
-			continue
-		} else {
-			path2sel[sp.GetPath()] = ss
-		}
-		sSels = append(sSels, ss)
-		span.AddAttributes(trace.StringAttribute("subprog:"+sp.GetPath(), ss.String()))
-	}
-
-	resp.Result = initResult(req)
-	cfg, sels, err := in.pickCmd(ctx, cmdSel, sSels)
-	if err != nil {
-		resp.Error = gomapb.ExecResp_BAD_REQUEST.Enum()
-		return nil, nil, fmt.Errorf("pick %v: %v", cmdSel, err)
-	}
-	logger.Infof("pick command %s => %s", cmdPath, cfg.GetCmdDescriptor().GetSelector())
-	subprogSetups := make(map[string]*cmdpb.CmdDescriptor_Setup)
-	for path, ss := range path2sel {
-		logger.Infof("pick subprog %s => %s", path, ss)
-		scfg := proto.Clone(sels[ss]).(*cmdpb.Config)
-		subprogSetups[path] = scfg.CmdDescriptor.Setup
-	}
-	setPicked(resp.Result, cfg, path2sel)
-
-	if cfg.CmdDescriptor.GetCross().GetWindowsCross() {
-		cmdPath = winpath.ToPosix(cmdPath)
-	}
-	cmdFiles, err := descriptor.RelocateCmd(cmdPath, cfg.CmdDescriptor.Setup, subprogSetups)
-	if err != nil {
-		resp.Error = gomapb.ExecResp_BAD_REQUEST.Enum()
-		return cfg, nil, fmt.Errorf("relocate %v: %v", cmdSel, err)
-	}
-
-	return cfg, cmdFiles, nil
-}
-
-func pathTypeFromPathStyle(pathStyle gomapb.RequesterInfo_PathStyle) cmdpb.CmdDescriptor_PathType {
-	switch pathStyle {
-	case gomapb.RequesterInfo_UNKNOWN_STYLE:
-		return cmdpb.CmdDescriptor_UNKNOWN_PATH_TYPE
-	case gomapb.RequesterInfo_POSIX_STYLE:
-		return cmdpb.CmdDescriptor_POSIX
-	case gomapb.RequesterInfo_WINDOWS_STYLE:
-		return cmdpb.CmdDescriptor_WINDOWS
-	}
-
-	return cmdpb.CmdDescriptor_UNKNOWN_PATH_TYPE
-}
-
-// Returns true if all `dimensions` exist in `platformDimensions`.
-func matchDimensions(dimensions []string, platformDimensions map[string]bool) bool {
-	for _, d := range dimensions {
-		if !platformDimensions[d] {
-			return false
-		}
-	}
-
-	// everything matched.
-	return true
-}
-
-// toolchainSpecToFileSpec converts ToolchainSpec to FileSpec.
-func toolchainSpecToFileSpec(ts *gomapb.ToolchainSpec) *cmdpb.FileSpec {
-	if ts.GetSymlinkPath() != "" {
-		// Symlink case
-		// TODO: Do we need to check Size, Hash, and IsExecutable are empty?
-		// Currently they're just ignored.
-		return &cmdpb.FileSpec{
-			Path:    ts.GetPath(),
-			Symlink: ts.GetSymlinkPath(),
-		}
-	}
-
-	// Non symlink case
-	return &cmdpb.FileSpec{
-		Path:         ts.GetPath(),
-		IsExecutable: ts.GetIsExecutable(),
-		Size:         ts.GetSize(),
-		Hash:         ts.GetHash(),
-	}
-}
-
-// getCmdFiles dynamically generates cmdFiles from ExecReq for arbitrary toolchain support.
-func getCmdFiles(ctx context.Context, req *gomapb.ExecReq) []*cmdpb.FileSpec {
-	logger := log.FromContext(ctx)
-
-	if len(req.ToolchainSpecs) == 0 {
-		// For backward compatibility, if len(req.ToolchainSpecs) == 0, add a compiler from
-		// CommandSpec. After a client that can utilize ToolchainSpecs is rolled, this code
-		// can be removed.
-		logger.Debugf("toolchain input: command spec: %v", req.CommandSpec)
-		return []*cmdpb.FileSpec{
-			{
-				Path:         req.CommandSpec.GetLocalCompilerPath(),
-				IsExecutable: true,
-				Size:         req.CommandSpec.GetSize(),
-				Hash:         string(req.CommandSpec.GetBinaryHash()),
-			},
-		}
-	}
-
-	var cmdFiles []*cmdpb.FileSpec
-	for _, ts := range req.ToolchainSpecs {
-		logger.Debugf("toolchain input: toolchain spec: %v", ts)
-		cmdFiles = append(cmdFiles, toolchainSpecToFileSpec(ts))
-	}
-
-	return cmdFiles
-}
-
-func (in *Inventory) pickFromExecReq(ctx context.Context, req *gomapb.ExecReq, resp *gomapb.ExecResp) (*cmdpb.Config, []*cmdpb.FileSpec, error) {
-	logger := log.FromContext(ctx)
-
-	dimensions := req.GetRequesterInfo().GetDimensions()
-	if len(dimensions) == 0 {
-		resp.Error = gomapb.ExecResp_BAD_REQUEST.Enum()
-		resp.ErrorMessage = append(resp.ErrorMessage, "No dimensions are specified")
-		return nil, nil, errors.New("no dimensions are specified")
-	}
-
-	// Select the best possible platform. If there is multiple possible platforms,
-	// select the first one.
-	var matchedConfig *platformConfig
-	for _, pCfg := range in.platformConfigs {
-		if err := checkACL(ctx, pCfg.acl); err != nil {
-			logger.Errorf("pcfg %v; access denied: %v", pCfg, err)
-			continue
-		}
-		if matchDimensions(dimensions, pCfg.dimensionSet) {
-			matchedConfig = pCfg
-			break
-		}
-	}
-
-	if matchedConfig == nil {
-		resp.Error = gomapb.ExecResp_BAD_REQUEST.Enum()
-		resp.ErrorMessage = append(resp.ErrorMessage, fmt.Sprintf("Could not matching runtime config with dimensions=%v", dimensions))
-		return nil, nil, fmt.Errorf("possible platform not found in inventory: dimensions=%v", dimensions)
-	}
-
-	cmdSel, _, err := fromCommandSpec(req.GetCommandSpec())
-	if err != nil {
-		resp.Error = gomapb.ExecResp_BAD_REQUEST.Enum()
-		resp.ErrorMessage = append(resp.ErrorMessage, fmt.Sprintf("unexpected CommandSpec %s", req.GetCommandSpec()))
-
-		return nil, nil, fmt.Errorf("normalize %v: %v", req.GetCommandSpec(), err)
-	}
-
-	// Dynamically generate cmdpb.Config here.
-	cfg := &cmdpb.Config{
-		RemoteexecPlatform: matchedConfig.remoteexecPlatform,
-		CmdDescriptor: &cmdpb.CmdDescriptor{
-			Selector: cmdSel.Proto(),
-			Setup: &cmdpb.CmdDescriptor_Setup{
-				PathType: pathTypeFromPathStyle(req.GetRequesterInfo().GetPathStyle()),
-			},
-		},
-	}
-
-	cmdFiles := getCmdFiles(ctx, req)
-
-	logger.Infof("pick platform %v", cfg)
-	return cfg, cmdFiles, nil
-}
-
-// initResult initializes ExecResult from request before command selection.
-func initResult(req *gomapb.ExecReq) *gomapb.ExecResult {
-	var subprograms []*gomapb.SubprogramSpec
-	for _, s := range req.GetSubprogram() {
-		subprograms = append(subprograms, &gomapb.SubprogramSpec{
-			Path: proto.String(s.GetPath()),
-		})
-	}
-
-	return &gomapb.ExecResult{
-		ExitStatus: proto.Int32(-1),
-		CommandSpec: &gomapb.CommandSpec{
-			Name:    proto.String(req.GetCommandSpec().GetName()),
-			Version: proto.String(req.GetCommandSpec().GetVersion()),
-			Target:  proto.String(req.GetCommandSpec().GetTarget()),
-		},
-		Subprogram: subprograms,
-	}
-}
-
-// setPicked sets picked command in the ExecResult.
-func setPicked(result *gomapb.ExecResult, cfg *cmdpb.Config, path2sel map[string]selector) {
-	result.CommandSpec.BinaryHash = []byte(cfg.CmdDescriptor.Selector.BinaryHash)
-	// TODO: detailed_info, or so?
-	for i, s := range result.Subprogram {
-		if ss, found := path2sel[s.GetPath()]; found {
-			result.Subprogram[i].BinaryHash = proto.String(ss.BinaryHash)
-		}
-	}
-}
-
-func fromCommandSpec(spec *gomapb.CommandSpec) (selector, string, error) {
-	s, err := normalizer.Selector(&cmdpb.Selector{
-		Name:       spec.GetName(),
-		Version:    spec.GetVersion(),
-		Target:     spec.GetTarget(),
-		BinaryHash: string(spec.GetBinaryHash()),
-	})
-	if err != nil {
-		return selector{}, "", err
-	}
-	return fromSelectorProto(s), spec.GetLocalCompilerPath(), nil
-}
-
-func fromSubprogramSpec(spec *gomapb.SubprogramSpec) selector {
-	return selector{
-		Name:       filepath.Base(spec.GetPath()), // TODO: fix
-		BinaryHash: string(spec.GetBinaryHash()),
-	}
-}
-
-func (in *Inventory) ServeHTTP(w http.ResponseWriter, req *http.Request) {
-	versionID, resp := in.status()
-	w.Header().Set("Content-Type", "text/plain")
-	fmt.Fprintf(w, "version-id: %s\n", versionID)
-	fmt.Fprintln(w)
-	for _, cfg := range resp {
-		fmt.Fprintf(w, "%v\n", cfg)
-	}
-}
diff --git a/exec/stats.go b/exec/stats.go
deleted file mode 100644
index 040005a..0000000
--- a/exec/stats.go
+++ /dev/null
@@ -1,99 +0,0 @@
-// Copyright 2018 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.
-
-package exec
-
-import (
-	"context"
-	"fmt"
-
-	"go.opencensus.io/stats"
-	"go.opencensus.io/stats/view"
-	"go.opencensus.io/tag"
-
-	"go.chromium.org/goma/server/log"
-	gomapb "go.chromium.org/goma/server/proto/api"
-)
-
-var (
-	apiErrors = stats.Int64(
-		"go.chromium.org/goma/server/exec.api-error",
-		"exec request api-error",
-		stats.UnitDimensionless)
-	clientRetries = stats.Int64(
-		"go.chromium.org/goma/server/exec.client-retry",
-		"exec request per client retry",
-		stats.UnitDimensionless)
-
-	apiErrorKey    = tag.MustNewKey("api-error")
-	clientRetryKey = tag.MustNewKey("client-retry")
-
-	// DefaultViews are the default views provided by this package.
-	// You need to register the view for data to actually be collected.
-	DefaultViews = []*view.View{
-		{
-			Description: "exec request api-error",
-			TagKeys: []tag.Key{
-				apiErrorKey,
-			},
-			Measure:     apiErrors,
-			Aggregation: view.Count(),
-		},
-		{
-			Description: "exec request client retry",
-			TagKeys: []tag.Key{
-				clientRetryKey,
-			},
-			Measure:     clientRetries,
-			Aggregation: view.Count(),
-		},
-		{
-			Description: `counts toolchain selection. result is "used", "found", "requested" or "missed"`,
-			TagKeys: []tag.Key{
-				selectorKey,
-				resultKey,
-			},
-			Measure:     toolchainSelects,
-			Aggregation: view.Count(),
-		},
-	}
-)
-
-func apiErrorValue(ctx context.Context, resp *gomapb.ExecResp) string {
-	logger := log.FromContext(ctx)
-	if errVal := resp.GetError(); errVal != gomapb.ExecResp_OK {
-		// marked as BAD_REQUEST for
-		// - compiler/subprogram not found
-		// - bad path_type in command config
-		// - input root detection failed
-		logger.Errorf("api-error=%s error_message=%s", errVal, resp.ErrorMessage)
-		return errVal.String()
-	}
-	if len(resp.ErrorMessage) > 0 {
-		logger.Errorf("api-error=internal: error_messge=%s", resp.ErrorMessage)
-		return "internal"
-	}
-	if len(resp.MissingInput) > 0 {
-		logger.Errorf("api-error=missing-inputs: missing=%d", len(resp.MissingInput))
-		return "missing-inputs"
-	}
-	return "OK"
-}
-
-// RecordAPIError records api-error in resp.
-func RecordAPIError(ctx context.Context, resp *gomapb.ExecResp) error {
-	ctx, err := tag.New(ctx, tag.Upsert(apiErrorKey, apiErrorValue(ctx, resp)))
-	if err != nil {
-		return err
-	}
-	stats.Record(ctx, apiErrors.M(1))
-	return nil
-}
-
-// RecordRequesterInfo records requester info.
-// e.g. client retry count.
-func RecordRequesterInfo(ctx context.Context, reqInfo *gomapb.RequesterInfo) error {
-	return stats.RecordWithTags(ctx, []tag.Mutator{tag.Upsert(clientRetryKey, fmt.Sprintf("%d", reqInfo.GetRetry()))}, clientRetries.M(1))
-	// TODO: record api version / goma revision etc?
-}
diff --git a/execlog/client.go b/execlog/client.go
deleted file mode 100644
index 1cdde09..0000000
--- a/execlog/client.go
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright 2017 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.
-
-package execlog
-
-import (
-	"context"
-
-	"google.golang.org/grpc"
-
-	gomapb "go.chromium.org/goma/server/proto/api"
-	pb "go.chromium.org/goma/server/proto/execlog"
-)
-
-// TODO: expvar counter
-
-// Client is a client to access execlog service via gRPC.
-type Client struct {
-	addr     string
-	dialOpts []grpc.DialOption
-}
-
-// NewClient creates new client to access execlog service serving on address.
-func NewClient(address string, opts ...grpc.DialOption) Client {
-	return Client{
-		addr:     address,
-		dialOpts: opts,
-	}
-}
-
-// SaveLog saves requested execlog.
-func (c Client) SaveLog(ctx context.Context, in *gomapb.SaveLogReq, opts ...grpc.CallOption) (*gomapb.SaveLogResp, error) {
-	conn, err := grpc.DialContext(ctx, c.addr,
-		append([]grpc.DialOption{
-			grpc.WithBlock(),
-		}, c.dialOpts...)...)
-	if err != nil {
-		return nil, err
-	}
-	defer conn.Close()
-
-	return pb.NewLogServiceClient(conn).SaveLog(ctx, in,
-		append([]grpc.CallOption{
-			grpc.MaxCallSendMsgSize(DefaultMaxReqMsgSize),
-		}, opts...)...)
-}
diff --git a/execlog/doc.go b/execlog/doc.go
deleted file mode 100644
index 168480a..0000000
--- a/execlog/doc.go
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright 2017 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.
-
-/*
-Package execlog provides goma execlog service implementation.
-*/
-package execlog
diff --git a/execlog/service.go b/execlog/service.go
deleted file mode 100644
index 6c3443e..0000000
--- a/execlog/service.go
+++ /dev/null
@@ -1,284 +0,0 @@
-// Copyright 2017 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.
-
-package execlog
-
-import (
-	"context"
-	"fmt"
-
-	"go.opencensus.io/stats"
-	"go.opencensus.io/stats/view"
-	"go.opencensus.io/tag"
-
-	"go.chromium.org/goma/server/log"
-	gomapb "go.chromium.org/goma/server/proto/api"
-	execlogpb "go.chromium.org/goma/server/proto/execlog"
-)
-
-// DefaultMaxReqMsgSize is max request message size for execlog service.
-// execlog server may receives > 8MB.
-// grpc's default is 4MB.
-const DefaultMaxReqMsgSize = 10 * 1024 * 1024
-
-var (
-	requests = stats.Int64(
-		"go.chromium.org/goma/execlog/requests",
-		"number of execlog entries",
-		stats.UnitDimensionless)
-
-	osFamilyKey           = tag.MustNewKey("os_family")
-	serviceAccountKey     = tag.MustNewKey("service_account")
-	gomaErrorKey          = tag.MustNewKey("goma_error")
-	compilerProxyErrorKey = tag.MustNewKey("compiler_proxy_error")
-	cacheHitKey           = tag.MustNewKey("cache_hit")
-	depscacheUsedKey      = tag.MustNewKey("depscache_used")
-	localRunKey           = tag.MustNewKey("local_run")
-	execExitStatusKey     = tag.MustNewKey("exec_exit_status")
-	execRequestRetryKey   = tag.MustNewKey("exec_request_retry")
-
-	handlerTime = stats.Float64(
-		"go.chromium.org/goma/execlog/handler_time",
-		"Time in compiler_proxy handler",
-		stats.UnitMilliseconds)
-
-	pendingTime = stats.Float64(
-		"go.chromium.org/goma/execlog/pending_time",
-		"Time in pending queue in compiler_proxy",
-		stats.UnitMilliseconds)
-	// need compiler_info_process_time?
-
-	includeProcessorWaitTime = stats.Float64(
-		"go.chromium.org/goma/execlog/include_processor_wait_time",
-		"Time to wait include processor",
-		stats.UnitMilliseconds)
-	includeProcessorRunTime = stats.Float64(
-		"go.chromium.org/goma/execlog/include_processor_run_time",
-		"Time to run include processor",
-		stats.UnitMilliseconds)
-	includePreprocessTotalFiles = stats.Int64(
-		"go.chromium.org/goma/execlog/include_preprocess_total_files",
-		"Number of files processed in include preprocess",
-		stats.UnitDimensionless)
-
-	includeFileloadPendingTime = stats.Float64(
-		"go.chromium.org/goma/execlog/include_fileload_pending_time",
-		"Time to wait upload input files",
-		stats.UnitMilliseconds)
-	includeFileloadRunTime = stats.Float64(
-		"go.chromium.org/goma/execlog/include_fileload_run_time",
-		"Time to upload input files",
-		stats.UnitMilliseconds)
-
-	rpcThrottleTime = stats.Float64(
-		"go.chromium.org/goma/execlog/rpc_throttle_time",
-		"Time to wait to call Exec by throttling (backoff by error)",
-		stats.UnitMilliseconds)
-	rpcPendingTime = stats.Float64(
-		"go.chromium.org/goma/execlog/rpc_pending_time",
-		"Time to wait to call Exec (too many requests)",
-		stats.UnitMilliseconds)
-	rpcWaitTime = stats.Float64(
-		"go.chromium.org/goma/execlog/rpc_wait_time",
-		"Time to wait Exec call response (i.e. server latency)",
-		stats.UnitMilliseconds)
-
-	fileResponseTime = stats.Float64(
-		"go.chromium.org/goma/execlog/file_response_time",
-		"Time to process output files",
-		stats.UnitMilliseconds)
-
-	localDelayTime = stats.Float64(
-		"go.chromium.org/goma/execlog/local_delay_time",
-		"Time delayed to start run locally",
-		stats.UnitMilliseconds)
-	localPendingTime = stats.Float64(
-		"go.chromium.org/goma/execlog/local_penging_time",
-		"Time to wait to run locally",
-		stats.UnitMilliseconds)
-	localRunTime = stats.Float64(
-		"go.chromium.org/goma/execlog/local_run_time",
-		"Time to run locally",
-		stats.UnitMilliseconds)
-
-	defaultLatencyDistribution = view.Distribution(1, 2, 3, 4, 5, 6, 8, 10, 13, 16, 20, 25, 30, 40, 50, 65, 80, 100, 130, 160, 200, 250, 300, 400, 500, 650, 800, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000)
-
-	tagKeys = []tag.Key{
-		osFamilyKey,
-		serviceAccountKey,
-		cacheHitKey,
-		depscacheUsedKey,
-		localRunKey,
-		execExitStatusKey,
-	}
-
-	DefaultViews = []*view.View{
-		{
-			TagKeys: []tag.Key{
-				osFamilyKey,
-				serviceAccountKey,
-				gomaErrorKey,
-				compilerProxyErrorKey,
-				cacheHitKey,
-				depscacheUsedKey,
-				localRunKey,
-				execExitStatusKey,
-				execRequestRetryKey,
-			},
-			Measure:     requests,
-			Aggregation: view.Sum(),
-		},
-		{
-			TagKeys:     tagKeys,
-			Measure:     handlerTime,
-			Aggregation: defaultLatencyDistribution,
-		},
-		{
-			TagKeys:     tagKeys,
-			Measure:     pendingTime,
-			Aggregation: defaultLatencyDistribution,
-		},
-		{
-			TagKeys:     tagKeys,
-			Measure:     includeProcessorWaitTime,
-			Aggregation: defaultLatencyDistribution,
-		},
-		{
-			TagKeys:     tagKeys,
-			Measure:     includeProcessorRunTime,
-			Aggregation: defaultLatencyDistribution,
-		},
-		{
-			TagKeys:     tagKeys,
-			Measure:     includePreprocessTotalFiles,
-			Aggregation: view.Sum(),
-		},
-		{
-			TagKeys:     tagKeys,
-			Measure:     includeFileloadPendingTime,
-			Aggregation: defaultLatencyDistribution,
-		},
-		{
-			TagKeys:     tagKeys,
-			Measure:     includeFileloadRunTime,
-			Aggregation: defaultLatencyDistribution,
-		},
-		{
-			TagKeys:     tagKeys,
-			Measure:     rpcThrottleTime,
-			Aggregation: defaultLatencyDistribution,
-		},
-		{
-			TagKeys:     tagKeys,
-			Measure:     rpcPendingTime,
-			Aggregation: defaultLatencyDistribution,
-		},
-		{
-			TagKeys:     tagKeys,
-			Measure:     rpcWaitTime,
-			Aggregation: defaultLatencyDistribution,
-		},
-		{
-			TagKeys:     tagKeys,
-			Measure:     fileResponseTime,
-			Aggregation: defaultLatencyDistribution,
-		},
-		{
-			TagKeys:     tagKeys,
-			Measure:     localDelayTime,
-			Aggregation: defaultLatencyDistribution,
-		},
-		{
-			TagKeys:     tagKeys,
-			Measure:     localPendingTime,
-			Aggregation: defaultLatencyDistribution,
-		},
-		{
-			TagKeys:     tagKeys,
-			Measure:     localRunTime,
-			Aggregation: defaultLatencyDistribution,
-		},
-	}
-)
-
-// Service represents goma execlog service.
-type Service struct {
-	execlogpb.UnimplementedLogServiceServer
-}
-
-func osFamily(e *gomapb.ExecLog) string {
-	oi := e.GetOsInfo().GetOsInfoOneof()
-	switch oi.(type) {
-	case *gomapb.OSInfo_LinuxInfo_:
-		return "Linux"
-	case *gomapb.OSInfo_WinInfo_:
-		return "Windows"
-	case *gomapb.OSInfo_MacInfo_:
-		return "Mac"
-	default:
-		return "Unknown"
-	}
-}
-
-// SaveLog emits some metrics.
-//   - go.chromium.org/goma/execlog/requests
-//     {os_family, ,goma_error, compiler_proxy_error,
-//     cache_hit, depscache_used, local_run,
-//     exec_exit_status, exec_request_retry}
-//   - go.chromium.org/goma/execlog/handler_time
-//
-// TODO: implement saving logic to GCS?
-func (Service) SaveLog(ctx context.Context, req *gomapb.SaveLogReq) (*gomapb.SaveLogResp, error) {
-	logger := log.FromContext(ctx)
-	for _, e := range req.GetExecLog() {
-		os := osFamily(e)
-		serviceAccount := e.GetServiceAccountId()
-		localRun := e.GetLocalRunTime() > 0
-		tags := []tag.Mutator{
-			tag.Upsert(osFamilyKey, os),
-			tag.Upsert(serviceAccountKey, serviceAccount),
-			tag.Upsert(cacheHitKey, fmt.Sprint(e.GetCacheHit())),
-			tag.Upsert(depscacheUsedKey, fmt.Sprint(e.GetDepscacheUsed())),
-			tag.Upsert(localRunKey, fmt.Sprint(localRun)),
-			tag.Upsert(execExitStatusKey, fmt.Sprint(e.GetExecExitStatus())),
-		}
-		ctx, err := tag.New(ctx, tags...)
-		if err != nil {
-			logger.Errorf("Failed to set tags for savelog: %v", err)
-			continue
-		}
-		stats.RecordWithTags(ctx, []tag.Mutator{
-			tag.Upsert(gomaErrorKey, fmt.Sprint(e.GetGomaError())),
-			tag.Upsert(compilerProxyErrorKey, fmt.Sprint(e.GetCompilerProxyError())),
-			tag.Upsert(execRequestRetryKey, fmt.Sprint(e.GetExecRequestRetry())),
-		}, requests.M(1))
-		stats.Record(ctx, handlerTime.M(float64(e.GetHandlerTime())))
-		stats.Record(ctx, pendingTime.M(float64(e.GetPendingTime())))
-		stats.Record(ctx, includeProcessorWaitTime.M(float64(e.GetIncludeProcessorWaitTime())))
-		stats.Record(ctx, includeProcessorRunTime.M(float64(e.GetIncludeProcessorRunTime())))
-		stats.Record(ctx, includePreprocessTotalFiles.M(int64(e.GetIncludePreprocessTotalFiles())))
-		for _, t := range e.GetIncludeFileloadPendingTime() {
-			stats.Record(ctx, includeFileloadPendingTime.M(float64(t)))
-		}
-		for _, t := range e.GetIncludeFileloadRunTime() {
-			stats.Record(ctx, includeFileloadRunTime.M(float64(t)))
-		}
-		for _, t := range e.GetRpcThrottleTime() {
-			stats.Record(ctx, rpcThrottleTime.M(float64(t)))
-		}
-		for _, t := range e.GetRpcPendingTime() {
-			stats.Record(ctx, rpcPendingTime.M(float64(t)))
-		}
-		for _, t := range e.GetRpcWaitTime() {
-			stats.Record(ctx, rpcWaitTime.M(float64(t)))
-		}
-		stats.Record(ctx, fileResponseTime.M(float64(e.GetFileResponseTime())))
-
-		stats.Record(ctx, localDelayTime.M(float64(e.GetLocalDelayTime())))
-		stats.Record(ctx, localPendingTime.M(float64(e.GetLocalPendingTime())))
-		stats.Record(ctx, localRunTime.M(float64(e.GetLocalRunTime())))
-	}
-
-	return &gomapb.SaveLogResp{}, nil
-}
diff --git a/file/cache.go b/file/cache.go
deleted file mode 100644
index 05a9b2d..0000000
--- a/file/cache.go
+++ /dev/null
@@ -1,143 +0,0 @@
-// Copyright 2017 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.
-
-package file
-
-import (
-	"context"
-	"errors"
-	"io/ioutil"
-	"path/filepath"
-
-	"google.golang.org/grpc"
-	"google.golang.org/protobuf/proto"
-
-	"go.chromium.org/goma/server/hash"
-	"go.chromium.org/goma/server/log"
-	gomapb "go.chromium.org/goma/server/proto/api"
-	filepb "go.chromium.org/goma/server/proto/file"
-)
-
-var (
-	errInvalidFileBlob = errors.New("invalid FileBlob")
-)
-
-// LocalCache is a goma file service with local disk cache.
-type LocalCache struct {
-	// Client is goma file service client.
-	// If it is nil, it only uses local disk cache.
-	Client filepb.FileServiceClient
-
-	// Dir is a directory for local disk cache.
-	Dir string
-}
-
-func (c LocalCache) load(ctx context.Context, hk string) (*gomapb.FileBlob, error) {
-	hpath := filepath.Join(c.Dir, hk)
-	b, err := ioutil.ReadFile(hpath)
-	if err != nil {
-		return nil, err
-	}
-	blob := &gomapb.FileBlob{
-		BlobType: gomapb.FileBlob_FILE_UNSPECIFIED.Enum(),
-	}
-	err = proto.Unmarshal(b, blob)
-	if err != nil {
-		return nil, err
-	}
-	return blob, nil
-}
-
-func (c LocalCache) save(ctx context.Context, blob *gomapb.FileBlob) (string, error) {
-	if !IsValid(blob) {
-		return "", errInvalidFileBlob
-	}
-	b, err := proto.Marshal(blob)
-	if err != nil {
-		return "", err
-	}
-	hashKey := hash.SHA256Content(b)
-	hpath := filepath.Join(c.Dir, hashKey)
-	err = ioutil.WriteFile(hpath, b, 0644)
-	if err != nil {
-		return "", err
-	}
-	return hashKey, nil
-}
-
-// LookupFile looks up FileBlob for requested hash keys.
-// It it is found in local disk cache, it will be used.
-// Otherwise and c.Client is not nil, ask c.Client.
-func (c LocalCache) LookupFile(ctx context.Context, req *gomapb.LookupFileReq, opts ...grpc.CallOption) (*gomapb.LookupFileResp, error) {
-	logger := log.FromContext(ctx)
-
-	logger.Infof("lookup %d keys", len(req.HashKey))
-	resp := &gomapb.LookupFileResp{
-		Blob: make([]*gomapb.FileBlob, len(req.HashKey)),
-	}
-	// TODO: single LookupFile rpc.
-	for i, hk := range req.HashKey {
-		blob, err := c.load(ctx, hk)
-		if err != nil {
-			logger.Infof("lookup %d %s miss: %v", i, hk, err)
-			if c.Client != nil {
-				cresp, err := c.Client.LookupFile(ctx, &gomapb.LookupFileReq{
-					HashKey: []string{hk},
-				}, opts...)
-				if err != nil {
-					logger.Warnf("lookup %d %s rpc miss: %v", i, hk, err)
-					continue
-				}
-				if len(cresp.Blob) == 0 {
-					logger.Errorf("lookup %d %s no blob", i, hk)
-					continue
-				}
-
-				if !IsValid(cresp.Blob[0]) {
-					logger.Warnf("lookup %d %s invalid blob", i, hk)
-					continue
-				}
-
-				blob = cresp.Blob[0]
-				_, err = c.save(ctx, blob)
-				if err != nil {
-					logger.Errorf("lookup %d %s save: %v", i, hk, err)
-				} else {
-					logger.Infof("fileblob save %s", hk)
-				}
-			}
-		}
-		resp.Blob[i] = blob
-	}
-	logger.Infof("lookup done")
-	return resp, nil
-}
-
-// StoreFile stores FileBlob in local disk, and c.Client if c.Client is not nil.
-func (c LocalCache) StoreFile(ctx context.Context, req *gomapb.StoreFileReq, opts ...grpc.CallOption) (*gomapb.StoreFileResp, error) {
-	logger := log.FromContext(ctx)
-
-	logger.Infof("store %d blobs", len(req.Blob))
-	resp := &gomapb.StoreFileResp{
-		HashKey: make([]string, len(req.Blob)),
-	}
-	for i, blob := range req.Blob {
-		hk, err := c.save(ctx, blob)
-		if err != nil {
-			logger.Errorf("save %d failed: %v", i, err)
-		} else {
-			logger.Infof("fileblob save %s", hk)
-		}
-		resp.HashKey[i] = hk
-	}
-	logger.Infof("save local done")
-	if c.Client != nil {
-		logger.Infof("save rpc")
-		_, err := c.Client.StoreFile(ctx, req, opts...)
-		if err != nil {
-			logger.Errorf("save rpc failed: %v", err)
-		}
-	}
-	return resp, nil
-}
diff --git a/file/disk.go b/file/disk.go
deleted file mode 100644
index a3037f6..0000000
--- a/file/disk.go
+++ /dev/null
@@ -1,243 +0,0 @@
-// Copyright 2017 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.
-
-package file
-
-import (
-	"context"
-	"errors"
-	"fmt"
-	"io"
-	"os"
-
-	"google.golang.org/protobuf/proto"
-
-	"go.chromium.org/goma/server/hash"
-	gomapb "go.chromium.org/goma/server/proto/api"
-	filepb "go.chromium.org/goma/server/proto/file"
-)
-
-const (
-	// LargeFileThreshold defines a number of bytes to use FILE_META instead of FILE.
-	LargeFileThreshold = 2 * 1024 * 1024
-
-	// FileChunkSize defines a size of each FILE_CHUNK content.
-	FileChunkSize = 2 * 1024 * 1024
-)
-
-// ToLocal writes FileBlob contents in fname.
-// If FileBlob is FILE_META, it will fetch FILE_CHUNK using FileServiceClient.
-func ToLocal(ctx context.Context, fc filepb.FileServiceClient, blob *gomapb.FileBlob, fname string) error {
-	f, err := os.Create(fname)
-	if err != nil {
-		return err
-	}
-	err = toLocal(ctx, fc, blob, f)
-	cerr := f.Close()
-	if err == nil && cerr != nil {
-		err = cerr
-	}
-	return err
-}
-
-type writerAt interface {
-	io.Writer
-	io.WriterAt
-}
-
-func toLocal(ctx context.Context, fc filepb.FileServiceClient, blob *gomapb.FileBlob, w writerAt) error {
-	var size int64
-	switch blobType := blob.GetBlobType(); blobType {
-	case gomapb.FileBlob_FILE:
-		n, err := w.Write(blob.GetContent())
-		if err != nil {
-			return err
-		}
-		size = int64(n)
-
-	case gomapb.FileBlob_FILE_META:
-		// TODO: streaming?
-		for i, hk := range blob.GetHashKey() {
-			cresp, err := fc.LookupFile(ctx, &gomapb.LookupFileReq{
-				HashKey: []string{hk},
-			})
-			if err != nil {
-				return fmt.Errorf("chunk error: %d: %s: %v", i, hk, err)
-			}
-			if len(cresp.Blob) == 0 || !IsValid(cresp.Blob[0]) {
-				return fmt.Errorf("missing chunk: %d %s", i, hk)
-			}
-			chunk := cresp.Blob[0]
-			if chunk.GetBlobType() != gomapb.FileBlob_FILE_CHUNK {
-				return fmt.Errorf("wrong blob type: %d: %s type=%v", i, hk, chunk.GetBlobType())
-			}
-			content := chunk.GetContent()
-			n, err := w.WriteAt(content, chunk.GetOffset())
-			if err != nil {
-				return err
-			}
-			// assume chunks are in order (might have hole, but no random access).
-			size += int64(n)
-		}
-
-	default:
-		return fmt.Errorf("missing blob: %v", blobType)
-	}
-
-	if size != blob.GetFileSize() {
-		return fmt.Errorf("partial written: %d != %d", size, blob.GetFileSize())
-	}
-	return nil
-}
-
-// FromLocal reads fname and fills in blob, and stores it in FileServiceClient.
-func FromLocal(ctx context.Context, fc filepb.FileServiceClient, fname string, blob *gomapb.FileBlob) (os.FileInfo, error) {
-	f, err := os.Open(fname)
-	if err != nil {
-		return nil, err
-	}
-	defer f.Close()
-	fi, err := f.Stat()
-	blob.FileSize = proto.Int64(fi.Size())
-	return fi, FromReader(ctx, fc, f, blob)
-}
-
-// FromReader reads contents from r and fills in blob, and stores it in FileServiceClient if fc != nil.
-// content size must be blob's FileSize.
-func FromReader(ctx context.Context, fc filepb.FileServiceClient, r io.Reader, blob *gomapb.FileBlob) error {
-	if blob.FileSize == nil {
-		return errors.New("FileSize is not set")
-	}
-	if blob.GetFileSize() < LargeFileThreshold {
-		blob.BlobType = gomapb.FileBlob_FILE.Enum()
-		blob.Content = make([]byte, blob.GetFileSize())
-		_, err := io.ReadFull(r, blob.Content)
-		if err != nil {
-			return err
-		}
-		return nil
-	}
-
-	blob.BlobType = gomapb.FileBlob_FILE_META.Enum()
-	var offset int64
-	for offset < blob.GetFileSize() {
-		chunk := &gomapb.FileBlob{
-			BlobType: gomapb.FileBlob_FILE_CHUNK.Enum(),
-			FileSize: blob.FileSize,
-			Offset:   proto.Int64(offset),
-		}
-		size := blob.GetFileSize() - offset
-		if size > FileChunkSize {
-			size = FileChunkSize
-		}
-		chunk.Content = make([]byte, size)
-		_, err := io.ReadFull(r, chunk.Content)
-		if err != nil {
-			return err
-		}
-		var hk string
-		if fc != nil {
-			cresp, err := fc.StoreFile(ctx, &gomapb.StoreFileReq{
-				Blob: []*gomapb.FileBlob{chunk},
-			})
-			if err != nil {
-				return err
-			}
-			if len(cresp.HashKey) == 0 || cresp.HashKey[0] == "" {
-				return fmt.Errorf("failed to store file offset=%d size=%d", offset, size)
-			}
-			hk = cresp.HashKey[0]
-		} else {
-			hk, err = hash.SHA256Proto(chunk)
-			if err != nil {
-				return fmt.Errorf("failed to compute hash of chunk offset=%d size=%d", offset, size)
-			}
-		}
-		blob.HashKey = append(blob.HashKey, hk)
-		offset += size
-	}
-	if fc == nil {
-		return nil
-	}
-	cresp, err := fc.StoreFile(ctx, &gomapb.StoreFileReq{
-		Blob: []*gomapb.FileBlob{blob},
-	})
-	if err != nil {
-		return err
-	}
-	if len(cresp.HashKey) == 0 || cresp.HashKey[0] == "" {
-		return fmt.Errorf("failed to store file_meta filesize=%d", blob.GetFileSize())
-	}
-	return nil
-}
-
-// BlobSpec represents a file by HashKey and/or Blob.
-type BlobSpec struct {
-	HashKey      string
-	Blob         *gomapb.FileBlob
-	IsExecutable bool
-}
-
-// Init initializes BlobSpec from HashKey or Blob.
-// Either HashKey or Blob must be set before initialization.
-func (b *BlobSpec) Init(ctx context.Context, client filepb.FileServiceClient) error {
-	if b.Blob == nil && b.HashKey == "" {
-		return errors.New("zero BlobSpec")
-	}
-	if b.Blob == nil {
-		cresp, err := client.LookupFile(ctx, &gomapb.LookupFileReq{
-			HashKey: []string{b.HashKey},
-		})
-		if err != nil {
-			return err
-		}
-		if len(cresp.Blob) == 0 || !IsValid(cresp.Blob[0]) {
-			return fmt.Errorf("no blob found for %s", b.HashKey)
-		}
-		b.Blob = cresp.Blob[0]
-		return nil
-	}
-	if b.HashKey == "" {
-		var err error
-		b.HashKey, err = Key(b.Blob)
-		if err != nil {
-			return err
-		}
-		return nil
-	}
-	return nil
-}
-
-// Disk provides convenient methods to convert between local file and FileBlob in goma file service.
-type Disk struct {
-	Client filepb.FileServiceClient
-}
-
-// TODO: hard linkable cache.
-
-// ToLocal creates file named fname from spec.
-func (d Disk) ToLocal(ctx context.Context, spec *BlobSpec, fname string) error {
-	err := spec.Init(ctx, d.Client)
-	if err != nil {
-		return fmt.Errorf("file.Disk: ToLocal %s: spec init failed: %v", fname, err)
-	}
-	err = ToLocal(ctx, d.Client, spec.Blob, fname)
-	if err != nil {
-		return fmt.Errorf("file.Disk: ToLocal %s: %v", fname, err)
-	}
-	return nil
-}
-
-// FromLocal fills spec from fname.
-func (d Disk) FromLocal(ctx context.Context, fname string, spec *BlobSpec) error {
-	spec.Blob = &gomapb.FileBlob{
-		BlobType: gomapb.FileBlob_FILE_UNSPECIFIED.Enum(),
-	}
-	fi, err := FromLocal(ctx, d.Client, fname, spec.Blob)
-	if err != nil {
-		return err
-	}
-	spec.IsExecutable = fi.Mode().Perm()&0111 != 0
-	return spec.Init(ctx, d.Client)
-}
diff --git a/file/doc.go b/file/doc.go
deleted file mode 100644
index 321cb99..0000000
--- a/file/doc.go
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright 2017 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.
-
-/*
-Package file provides goma file service implementation.
-*/
-package file
diff --git a/file/key.go b/file/key.go
deleted file mode 100644
index 68a87d5..0000000
--- a/file/key.go
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright 2017 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.
-
-package file
-
-import (
-	"go.chromium.org/goma/server/hash"
-	gomapb "go.chromium.org/goma/server/proto/api"
-)
-
-// Key returns hash key for blob.
-func Key(blob *gomapb.FileBlob) (string, error) {
-	return hash.SHA256Proto(blob)
-}
diff --git a/file/service.go b/file/service.go
deleted file mode 100644
index 5ec85f8..0000000
--- a/file/service.go
+++ /dev/null
@@ -1,169 +0,0 @@
-// Copyright 2017 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.
-
-package file
-
-import (
-	"context"
-	"sync"
-	"time"
-
-	"go.opencensus.io/trace"
-	"golang.org/x/sync/errgroup"
-	"google.golang.org/grpc/codes"
-	"google.golang.org/grpc/status"
-	"google.golang.org/protobuf/proto"
-
-	"go.chromium.org/goma/server/hash"
-	"go.chromium.org/goma/server/log"
-
-	gomapb "go.chromium.org/goma/server/proto/api"
-	cachepb "go.chromium.org/goma/server/proto/cache"
-	filepb "go.chromium.org/goma/server/proto/file"
-)
-
-const (
-	// DefaultMaxMsgSize is max message size for file service.
-	// file service will handle 2MB chunk * 5 chunks in a request.
-	// grpc's default is 4MB.
-	DefaultMaxMsgSize = 12 * 1024 * 1024
-)
-
-// Service represents goma file service.
-type Service struct {
-	filepb.UnimplementedFileServiceServer
-	// Cache is a fileblob storage.
-	Cache cachepb.CacheServiceClient
-}
-
-// StoreFile stores FileBlob.
-func (s *Service) StoreFile(ctx context.Context, req *gomapb.StoreFileReq) (*gomapb.StoreFileResp, error) {
-	span := trace.FromContext(ctx)
-
-	span.AddAttributes(trace.Int64Attribute("store_num", int64(len(req.GetBlob()))))
-
-	logger := log.FromContext(ctx)
-	start := time.Now()
-
-	resp := &gomapb.StoreFileResp{
-		HashKey: make([]string, len(req.GetBlob())),
-	}
-
-	// if it contains one blob only, report error for blob as rpc error.
-	single := len(req.GetBlob()) == 1
-
-	errg, ctx := errgroup.WithContext(ctx)
-
-	for i, blob := range req.GetBlob() {
-		i, blob := i, blob
-		// TODO: limit goroutine if cache server is overloaded or many request consume many memory.
-		errg.Go(func() error {
-			if !IsValid(blob) {
-				span.Annotatef(nil, "%d: invalid blob", i)
-				logger.Errorf("%d: invalid blob", i)
-				if single {
-					return status.Error(codes.InvalidArgument, "not valid blob")
-				}
-				return nil
-			}
-			t := time.Now()
-			b, err := proto.Marshal(blob)
-			if err != nil {
-				// blob has been marshalled, so it should never fail.
-				span.Annotatef(nil, "%d: proto.Marshal %v", i, err)
-				logger.Errorf("%d: proto.Marshal: %v", i, err)
-				return nil
-			}
-			marshalTime := time.Since(t)
-			t = time.Now()
-			hashKey := hash.SHA256Content(b)
-			hashTime := time.Since(t)
-			t = time.Now()
-			_, err = s.Cache.Put(ctx, &cachepb.PutReq{
-				Kv: &cachepb.KV{
-					Key:   hashKey,
-					Value: b,
-				},
-			})
-			putTime := time.Since(t)
-			span.Annotatef(nil, "%d hashKey=%s: %v", i, hashKey, err)
-			if err != nil {
-				logger.Errorf("%d: cache.Put %s: %v", i, hashKey, err)
-				if single || status.Code(err) == codes.ResourceExhausted {
-					// when resource exhausted, fail whole request, not fail of individual blob.
-					return err
-				}
-				// TODO: report individual error.
-				// client will get empty HashKey for failed blobs.
-				return nil
-			}
-			resp.HashKey[i] = hashKey
-			logger.Infof("%d: cache.Put %s: marshal:%s hash:%s put:%s", i, hashKey, marshalTime, hashTime, putTime)
-			return nil
-		})
-	}
-	err := errg.Wait()
-	if err != nil {
-		logger.Warnf("%s store %d blobs %s: %v", req.GetRequesterInfo(), len(req.GetBlob()), time.Since(start), err)
-		return nil, err
-	}
-	return resp, nil
-}
-
-// LookupFile looks up FileBlob.
-func (s *Service) LookupFile(ctx context.Context, req *gomapb.LookupFileReq) (*gomapb.LookupFileResp, error) {
-	span := trace.FromContext(ctx)
-	span.AddAttributes(trace.Int64Attribute("lookup_num", int64(len(req.GetHashKey()))))
-
-	logger := log.FromContext(ctx)
-
-	logger.Debugf("requester %v", req.GetRequesterInfo())
-	start := time.Now()
-
-	resp := &gomapb.LookupFileResp{
-		Blob: make([]*gomapb.FileBlob, len(req.GetHashKey())),
-	}
-
-	var wg sync.WaitGroup
-
-	for i, hashKey := range req.GetHashKey() {
-		wg.Add(1)
-		// TODO: limit goroutine if cache server is overloaded or many request consume many memory.
-		go func(i int, hashKey string) {
-			defer wg.Done()
-			t := time.Now()
-			resp.Blob[i] = &gomapb.FileBlob{
-				BlobType: gomapb.FileBlob_FILE_UNSPECIFIED.Enum(),
-			}
-			r, err := s.Cache.Get(ctx, &cachepb.GetReq{
-				Key: hashKey,
-			})
-			getTime := time.Since(t)
-			t = time.Now()
-			if err != nil {
-				span.Annotatef(nil, "%d: hashKey=%s: %v", i, hashKey, err)
-				logger.Warnf("%d: cache.Get %s: %v", i, hashKey, err)
-				return
-			}
-			if len(r.Kv.Value) == 0 {
-				span.Annotatef(nil, "%d: hashKey=%s not found", i, hashKey)
-				logger.Errorf("%d: cache.Get %s: no value", i, hashKey)
-				return
-			}
-			err = proto.Unmarshal(r.Kv.Value, resp.Blob[i])
-			unmarshalTime := time.Since(t)
-			if err != nil {
-				span.Annotatef(nil, "%d: hashKey=%s: proto.Unmarshal %v", i, hashKey, err)
-				logger.Errorf("%d: proto.Unmarshal %s: %v", i, hashKey, err)
-				return
-			}
-			logger.Infof("%d: cache.Get %s: get:%s unmarshal:%s", i, hashKey, getTime, unmarshalTime)
-		}(i, hashKey)
-	}
-	logger.Debugf("waiting lookup %d blobs", len(req.GetHashKey()))
-	wg.Wait()
-	logger.Debugf("lookup %d blobs %s", len(req.GetHashKey()), time.Since(start))
-
-	return resp, nil
-}
diff --git a/file/value.go b/file/value.go
deleted file mode 100644
index fccaccf..0000000
--- a/file/value.go
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2017 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.
-
-package file
-
-import (
-	gomapb "go.chromium.org/goma/server/proto/api"
-)
-
-// IsValid checks blob is valid FileBlob.
-func IsValid(blob *gomapb.FileBlob) bool {
-	if blob == nil {
-		return false
-	}
-	if blob.GetBlobType() == gomapb.FileBlob_FILE_UNSPECIFIED {
-		return false
-	}
-	// TODO: add check more
-	return true
-}
diff --git a/frontend/doc.go b/frontend/doc.go
deleted file mode 100644
index bb87d1b..0000000
--- a/frontend/doc.go
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright 2017 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.
-
-/*
-Package frontend is goma service frontend (/cxx-compiler-service/*).
-*/
-package frontend
diff --git a/frontend/frontend.go b/frontend/frontend.go
deleted file mode 100644
index 1366124..0000000
--- a/frontend/frontend.go
+++ /dev/null
@@ -1,204 +0,0 @@
-// Copyright 2017 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.
-
-package frontend
-
-import (
-	"bytes"
-	"context"
-	"fmt"
-	"net/http"
-	"os"
-	"regexp"
-
-	"go.opencensus.io/plugin/ochttp"
-	"go.opencensus.io/plugin/ochttp/propagation/tracecontext"
-	"go.opencensus.io/stats"
-	"go.opencensus.io/stats/view"
-	"go.opencensus.io/tag"
-	"go.opencensus.io/trace"
-
-	"go.chromium.org/goma/server/httprpc"
-	"go.chromium.org/goma/server/log"
-	"go.chromium.org/goma/server/log/errorreporter"
-)
-
-const (
-	// PathPrefix is goma endpoint prefix.
-	PathPrefix = "/cxx-compiler-service/"
-)
-
-var (
-	hostname string
-
-	pingRequests = stats.Int64(
-		"go.chromium.org/goma/server/frontend.ping_count",
-		"Number of ping requests",
-		stats.UnitDimensionless)
-
-	// commit hash and time specified in user-agent.
-	// the value of these tag can be controlled by the client,
-	// so you need to watch out for potentially generating high-cardinality
-	// labels in your metrics backend if you use this tag in views.
-	userAgentCommitHashKey = tag.MustNewKey("useragent.hash")
-	userAgentCommitTimeKey = tag.MustNewKey("useragent.time")
-
-	// user-agent format
-	//  compiler-proxy built by <username> at <commitHash>@<commitTime>
-	//  on <date-time>.
-	// see https://chromium.googlesource.com/infra/goma/client/+/70685d6cbb19c108d8abf2235edd2d02bed8dded/client/generate_compiler_proxy_info.py#87
-	userAgentRE = regexp.MustCompile(`compiler-proxy built by \S+ at ([[:xdigit:]]+)@([[:digit:]]+) on .*`)
-
-	// DefaultViews are the default views provided by this package.
-	// You need to register he view for data to actually be collected.
-	DefaultViews = []*view.View{
-		{
-			Name:        "go.chromium.org/goma/server/frontend.ping_count_by_useragent",
-			Description: "ping request count by user-agent",
-			TagKeys: []tag.Key{
-				userAgentCommitHashKey,
-				userAgentCommitTimeKey,
-			},
-			Measure:     pingRequests,
-			Aggregation: view.Count(),
-		},
-	}
-)
-
-func init() {
-	var err error
-	hostname, err = os.Hostname()
-	if err != nil {
-		hostname = "unknown"
-	}
-}
-
-func parseUserAgent(header string) (commitHash, commitTime string, err error) {
-	m := userAgentRE.FindStringSubmatch(header)
-	if len(m) == 0 {
-		return "", "", fmt.Errorf("unexpected client: %s", header)
-	}
-	return m[1], m[2], nil
-}
-
-func checkUserAgent(ctx context.Context, req *http.Request) error {
-	var tags []tag.Mutator
-	commitHash, commitTime, err := parseUserAgent(req.Header.Get("User-Agent"))
-	if err != nil {
-		// TODO: reject requests from unexpected client?
-		logger := log.FromContext(ctx)
-		logger.Errorf("user-agent: %v", err)
-		tags = append(tags,
-			tag.Upsert(userAgentCommitHashKey, "error"),
-			tag.Upsert(userAgentCommitTimeKey, "error"))
-	} else {
-		// TODO: reject requests from too old client? http://b/110381625
-		tags = append(tags,
-			tag.Upsert(userAgentCommitHashKey, commitHash),
-			tag.Upsert(userAgentCommitTimeKey, commitTime))
-	}
-	ctx, err = tag.New(ctx, tags...)
-	if err != nil {
-		return err
-	}
-	stats.Record(ctx, pingRequests.M(1))
-	return nil
-}
-
-// Backend represents backend of goma frontend.
-type Backend interface {
-	Ping() http.Handler
-	Exec() http.Handler
-	ByteStream() http.Handler
-	StoreFile() http.Handler
-	LookupFile() http.Handler
-	Execlog() http.Handler
-}
-
-// Frontend represents goma frontend.
-type Frontend struct {
-	AC      httprpc.AdmissionController
-	Backend Backend
-
-	TraceLabels map[string]string
-
-	// TODO: health status?
-	// TODO: downloadurl?
-	// TODO: compilers? - drop support?
-}
-
-// Handler creates http.Handler from Frontend.
-func Handler(f Frontend) http.Handler {
-	mux := http.NewServeMux()
-	mux.Handle("/ping", http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
-		ctx := req.Context()
-		ctx, span := trace.StartSpan(ctx, "go.chromium.org/goma/server/frontend.Handler.ping")
-		defer span.End()
-
-		var buf bytes.Buffer
-		err := req.Write(&buf)
-		span.AddAttributes(trace.StringAttribute("header", buf.String()))
-		if err != nil {
-			span.AddAttributes(trace.StringAttribute("err", err.Error()))
-		}
-		err = checkUserAgent(ctx, req)
-		if err != nil {
-			logger := log.FromContext(ctx)
-			logger.Errorf("failed to record user-agent: %v", err)
-		}
-
-		f.Backend.Ping().ServeHTTP(w, req)
-	}))
-	mux.Handle("/e", f.Backend.Exec())
-	mux.Handle("/blobs/", f.Backend.ByteStream())
-	mux.Handle("/s", f.Backend.StoreFile())
-	mux.Handle("/l", f.Backend.LookupFile())
-	mux.Handle("/sl", f.Backend.Execlog())
-	// TODO: /downloadurl etc?
-
-	h := httprpc.AdmissionControl(f.AC, mux)
-	return h
-}
-
-type reportingResponseWriter struct {
-	http.ResponseWriter
-	er  errorreporter.ErrorReporter
-	req *http.Request
-}
-
-func (w reportingResponseWriter) WriteHeader(statusCode int) {
-	w.ResponseWriter.WriteHeader(statusCode)
-	if statusCode < http.StatusBadRequest || statusCode == 499 {
-		// 499 is Canceled. go/http-canonical-mapping
-		return
-	}
-	ctx := w.req.Context()
-	logger := log.FromContext(ctx)
-	logger.Errorf("http %d %s", statusCode, http.StatusText(statusCode))
-}
-
-func (f Frontend) errorReport(h http.Handler) http.Handler {
-	if errorreporter.Enabled() {
-		return h
-	}
-	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
-		defer errorreporter.Do(req, nil)
-		h.ServeHTTP(reportingResponseWriter{
-			ResponseWriter: w,
-			req:            req,
-		}, req)
-	})
-}
-
-// Register registers Frontend under PathPrefix (/cxx-compiler-service).
-func Register(mux *http.ServeMux, f Frontend) {
-	h := Handler(f)
-	h = http.StripPrefix(PathPrefix[:len(PathPrefix)-1], h)
-	h = httprpc.Trace(h, f.TraceLabels)
-	h = f.errorReport(h)
-	mux.Handle(PathPrefix, &ochttp.Handler{
-		Propagation: &tracecontext.HTTPFormat{},
-		Handler:     h,
-	})
-}
diff --git a/frontend/frontend_test.go b/frontend/frontend_test.go
deleted file mode 100644
index 23e219d..0000000
--- a/frontend/frontend_test.go
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2018 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.
-
-package frontend
-
-import "testing"
-
-func TestParseUserAgent(t *testing.T) {
-	for _, tc := range []struct {
-		header   string
-		wantHash string
-		wantTime string
-	}{
-		{
-			header:   "compiler-proxy built by chrome-bot at 7f258745e612a85dcc7896ccc09c22785ecccdb8@1539330943 on 2018-10-12T08:06:33.429908Z",
-			wantHash: "7f258745e612a85dcc7896ccc09c22785ecccdb8",
-			wantTime: "1539330943",
-		},
-	} {
-		gotHash, gotTime, err := parseUserAgent(tc.header)
-		if err != nil {
-			t.Errorf("parseUserAgent(%q)=%q, %q, %v; want nil error", tc.header, gotHash, gotTime, err)
-			continue
-		}
-		if gotHash != tc.wantHash || gotTime != tc.wantTime {
-			t.Errorf("parseUserAgent(%q)=%q, %q, nil; want %q, %q, nil", tc.header, gotHash, gotTime, tc.wantHash, tc.wantTime)
-		}
-
-	}
-}
-
-func TestParseUserAgentError(t *testing.T) {
-	for _, tc := range []string{
-		"compiler-proxy built by chrome-bot at 19698870 on 2011-02-25T06:29:16.365771Z",
-		"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36",
-	} {
-		gotHash, gotTime, err := parseUserAgent(tc)
-		if err == nil {
-			t.Errorf("parseUserAgent(%q)=%q, %q, nil; want error", tc, gotHash, gotTime)
-		}
-	}
-}
diff --git a/fswatch/fswatch.go b/fswatch/fswatch.go
deleted file mode 100644
index 61865f8..0000000
--- a/fswatch/fswatch.go
+++ /dev/null
@@ -1,85 +0,0 @@
-// Copyright 2018 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.
-
-/*
-Package fswatch watches directory with fsnotify.
-*/
-package fswatch
-
-import (
-	"context"
-
-	"github.com/fsnotify/fsnotify"
-)
-
-// Watcher watches a directory.
-type Watcher struct {
-	w      *fsnotify.Watcher
-	ch     chan resp
-	cancel func()
-	errch  chan error
-}
-
-type resp struct {
-	Event fsnotify.Event
-	Err   error
-}
-
-// New creates new watcher watching directory.
-// It will close when ctx is cancelled.
-func New(ctx context.Context, dir string) (*Watcher, error) {
-	w, err := fsnotify.NewWatcher()
-	if err != nil {
-		return nil, err
-	}
-	ch := make(chan resp, 10)
-	errch := make(chan error)
-	ctx, cancel := context.WithCancel(ctx)
-	watcher := &Watcher{
-		w:      w,
-		ch:     ch,
-		cancel: cancel,
-		errch:  errch,
-	}
-	go watcher.loop(ctx)
-	err = w.Add(dir)
-	if err != nil {
-		watcher.Close()
-		return nil, err
-	}
-	return watcher, nil
-}
-
-// Close stops watcher.
-func (w *Watcher) Close() error {
-	w.cancel()
-	return <-w.errch
-}
-
-func (w *Watcher) loop(ctx context.Context) {
-	defer func() {
-		w.errch <- w.w.Close()
-	}()
-	for {
-		select {
-		case <-ctx.Done():
-			return
-		case event := <-w.w.Events:
-			w.ch <- resp{Event: event}
-		case err := <-w.w.Errors:
-			w.ch <- resp{Err: err}
-		}
-	}
-}
-
-// Next gets next watched event.
-func (w *Watcher) Next(ctx context.Context) (fsnotify.Event, error) {
-	select {
-	case <-ctx.Done():
-		return fsnotify.Event{}, ctx.Err()
-
-	case resp := <-w.ch:
-		return resp.Event, resp.Err
-	}
-}
diff --git a/fswatch/fswatch_test.go b/fswatch/fswatch_test.go
deleted file mode 100644
index 1fb499a..0000000
--- a/fswatch/fswatch_test.go
+++ /dev/null
@@ -1,110 +0,0 @@
-// Copyright 2018 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.
-
-package fswatch
-
-import (
-	"context"
-	"fmt"
-	"io/ioutil"
-	"os"
-	"path/filepath"
-	"reflect"
-	"testing"
-	"time"
-
-	"github.com/fsnotify/fsnotify"
-)
-
-func TestNext(t *testing.T) {
-	dir, err := ioutil.TempDir("", "fswatch.TestNext.")
-	if err != nil {
-		t.Fatal(err)
-	}
-	defer os.RemoveAll(dir)
-
-	ctx := context.Background()
-	fmt.Println("new")
-	w, err := New(ctx, dir)
-	if err != nil {
-		t.Fatalf("New(ctx, dir)=_, %v; want nil-err", err)
-	}
-	defer w.Close()
-	timeout := 100 * time.Millisecond
-
-	{
-		t.Logf("no event in %s", timeout)
-		ctx, cancel := context.WithTimeout(ctx, timeout)
-		defer cancel()
-		_, err := w.Next(ctx)
-		if err != context.DeadlineExceeded {
-			t.Fatalf("w.Next(ctx)=_, %v; want DeadlineExceeded", err)
-		}
-	}
-	fname := filepath.Join(dir, "foo")
-	{
-		t.Logf("add new file")
-		err = ioutil.WriteFile(fname, []byte("1"), 0644)
-		if err != nil {
-			t.Fatalf("WriteFile(%q)=%v; want=nil error", fname, err)
-		}
-		ctx, cancel := context.WithTimeout(ctx, timeout)
-		defer cancel()
-		ev, err := w.Next(ctx)
-		want := fsnotify.Event{Name: fname, Op: fsnotify.Create}
-		if err != nil || !reflect.DeepEqual(ev, want) {
-			t.Fatalf("w.Next(ctx)=%v, %v; want=%v, nil", ev, err, want)
-		}
-		ev, err = w.Next(ctx)
-		want = fsnotify.Event{Name: fname, Op: fsnotify.Write}
-		if err != nil || !reflect.DeepEqual(ev, want) {
-			t.Fatalf("w.Next(ctx)=%v, %v; want=%v, nil", ev, err, want)
-		}
-		_, err = w.Next(ctx)
-		if err != context.DeadlineExceeded {
-			t.Fatalf("w.Next(ctx)=_, %v; want DeadlineExceeded", err)
-		}
-	}
-	{
-		t.Logf("update file")
-		err = ioutil.WriteFile(fname, []byte("2"), 0644)
-		if err != nil {
-			t.Fatalf("WriteFile(%q)=%v; want=nil error", fname, err)
-		}
-		ctx, cancel := context.WithTimeout(ctx, timeout)
-		defer cancel()
-		ev, err := w.Next(ctx)
-		want := fsnotify.Event{Name: fname, Op: fsnotify.Write}
-		if err != nil || !reflect.DeepEqual(ev, want) {
-			t.Fatalf("w.Next(ctx)=%v, %v; want=%v, nil", ev, err, want)
-		}
-
-		for {
-			ev, err = w.Next(ctx)
-			if err == context.DeadlineExceeded {
-				break
-			}
-			// may receive several Write.
-			t.Logf("w.Next(ctx)=%v, %v", ev, err)
-		}
-	}
-	{
-		t.Logf("remove file")
-		err = os.Remove(fname)
-		if err != nil {
-			t.Fatalf("Remove(%q)=%v; want=nil error", fname, err)
-		}
-		ctx, cancel := context.WithTimeout(ctx, timeout)
-		defer cancel()
-		ev, err := w.Next(ctx)
-		want := fsnotify.Event{Name: fname, Op: fsnotify.Remove}
-		if err != nil || !reflect.DeepEqual(ev, want) {
-			t.Fatalf("w.Next(ctx)=%v, %v; want=%v, nil", ev, err, want)
-		}
-		_, err = w.Next(ctx)
-		if err != context.DeadlineExceeded {
-			t.Fatalf("w.Next(ctx)=_, %v; want DeadlineExceeded", err)
-		}
-	}
-}
diff --git a/go.mod b/go.mod
deleted file mode 100644
index 01c98cc..0000000
--- a/go.mod
+++ /dev/null
@@ -1,38 +0,0 @@
-module go.chromium.org/goma/server
-
-go 1.16
-
-require (
-	cloud.google.com/go/compute/metadata v0.2.3
-	cloud.google.com/go/errorreporting v0.3.0
-	cloud.google.com/go/monitoring v1.15.1
-	cloud.google.com/go/profiler v0.3.1
-	cloud.google.com/go/pubsub v1.33.0
-	cloud.google.com/go/storage v1.31.0
-	contrib.go.opencensus.io/exporter/stackdriver v0.13.14
-	github.com/aws/aws-sdk-go v1.44.317 // indirect
-	github.com/bazelbuild/remote-apis v0.0.0-20210718193713-0ecef08215cf
-	github.com/bazelbuild/remote-apis-sdks v0.0.0-20220429154201-6c8489803a6f
-	github.com/fsnotify/fsnotify v1.6.0
-	github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da
-	github.com/gomodule/redigo v1.8.9
-	github.com/google/go-cmp v0.5.9
-	github.com/google/uuid v1.3.0
-	github.com/googleapis/gax-go/v2 v2.12.0
-	github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8
-	github.com/grpc-ecosystem/go-grpc-middleware v1.4.0
-	github.com/pborman/uuid v1.2.1 // indirect
-	go.opencensus.io v0.24.0
-	go.uber.org/goleak v1.2.1 // indirect
-	go.uber.org/zap v1.25.0
-	golang.org/x/build v0.0.0-20191031202223-0706ea4fce0c
-	golang.org/x/net v0.17.0
-	golang.org/x/oauth2 v0.11.0
-	golang.org/x/sync v0.3.0
-	google.golang.org/api v0.134.0
-	google.golang.org/genproto v0.0.0-20230706204954-ccb25ca9f130
-	google.golang.org/genproto/googleapis/bytestream v0.0.0-20230720185612-659f7aaaa771
-	google.golang.org/genproto/googleapis/rpc v0.0.0-20230720185612-659f7aaaa771
-	google.golang.org/grpc v1.57.0
-	google.golang.org/protobuf v1.31.0
-)
diff --git a/go.sum b/go.sum
deleted file mode 100644
index 4d67337..0000000
--- a/go.sum
+++ /dev/null
@@ -1,2965 +0,0 @@
-bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8=
-bazil.org/fuse v0.0.0-20200407214033-5883e5a4b512/go.mod h1:FbcW6z/2VytnFDhZfumh8Ss8zxHE6qpMP5sHTRe0EaM=
-cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
-cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
-cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
-cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
-cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
-cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
-cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
-cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
-cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
-cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
-cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
-cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
-cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
-cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
-cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
-cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
-cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
-cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
-cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=
-cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
-cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
-cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
-cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY=
-cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM=
-cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY=
-cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ=
-cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=
-cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4=
-cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc=
-cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA=
-cloud.google.com/go v0.100.1/go.mod h1:fs4QogzfH5n2pBXBP9vRiU+eCny7lD2vmFZy79Iuw1U=
-cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A=
-cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc=
-cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU=
-cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA=
-cloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFOKM=
-cloud.google.com/go v0.107.0/go.mod h1:wpc2eNrD7hXUTy8EKS10jkxpZBjASrORK7goS+3YX2I=
-cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY=
-cloud.google.com/go v0.110.2/go.mod h1:k04UEeEtb6ZBRTv3dZz4CeJC3jKGxyhl0sAiVVquxiw=
-cloud.google.com/go v0.110.4 h1:1JYyxKMN9hd5dR2MYTPWkGUgcoxVVhg0LKNKEo0qvmk=
-cloud.google.com/go v0.110.4/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI=
-cloud.google.com/go/accessapproval v1.4.0/go.mod h1:zybIuC3KpDOvotz59lFe5qxRZx6C75OtwbisN56xYB4=
-cloud.google.com/go/accessapproval v1.5.0/go.mod h1:HFy3tuiGvMdcd/u+Cu5b9NkO1pEICJ46IR82PoUdplw=
-cloud.google.com/go/accessapproval v1.6.0/go.mod h1:R0EiYnwV5fsRFiKZkPHr6mwyk2wxUJ30nL4j2pcFY2E=
-cloud.google.com/go/accessapproval v1.7.1/go.mod h1:JYczztsHRMK7NTXb6Xw+dwbs/WnOJxbo/2mTI+Kgg68=
-cloud.google.com/go/accesscontextmanager v1.3.0/go.mod h1:TgCBehyr5gNMz7ZaH9xubp+CE8dkrszb4oK9CWyvD4o=
-cloud.google.com/go/accesscontextmanager v1.4.0/go.mod h1:/Kjh7BBu/Gh83sv+K60vN9QE5NJcd80sU33vIe2IFPE=
-cloud.google.com/go/accesscontextmanager v1.6.0/go.mod h1:8XCvZWfYw3K/ji0iVnp+6pu7huxoQTLmxAbVjbloTtM=
-cloud.google.com/go/accesscontextmanager v1.7.0/go.mod h1:CEGLewx8dwa33aDAZQujl7Dx+uYhS0eay198wB/VumQ=
-cloud.google.com/go/accesscontextmanager v1.8.0/go.mod h1:uI+AI/r1oyWK99NN8cQ3UK76AMelMzgZCvJfsi2c+ps=
-cloud.google.com/go/accesscontextmanager v1.8.1/go.mod h1:JFJHfvuaTC+++1iL1coPiG1eu5D24db2wXCDWDjIrxo=
-cloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw=
-cloud.google.com/go/aiplatform v1.24.0/go.mod h1:67UUvRBKG6GTayHKV8DBv2RtR1t93YRu5B1P3x99mYY=
-cloud.google.com/go/aiplatform v1.27.0/go.mod h1:Bvxqtl40l0WImSb04d0hXFU7gDOiq9jQmorivIiWcKg=
-cloud.google.com/go/aiplatform v1.35.0/go.mod h1:7MFT/vCaOyZT/4IIFfxH4ErVg/4ku6lKv3w0+tFTgXQ=
-cloud.google.com/go/aiplatform v1.36.1/go.mod h1:WTm12vJRPARNvJ+v6P52RDHCNe4AhvjcIZ/9/RRHy/k=
-cloud.google.com/go/aiplatform v1.37.0/go.mod h1:IU2Cv29Lv9oCn/9LkFiiuKfwrRTq+QQMbW+hPCxJGZw=
-cloud.google.com/go/aiplatform v1.45.0/go.mod h1:Iu2Q7sC7QGhXUeOhAj/oCK9a+ULz1O4AotZiqjQ8MYA=
-cloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI=
-cloud.google.com/go/analytics v0.12.0/go.mod h1:gkfj9h6XRf9+TS4bmuhPEShsh3hH8PAZzm/41OOhQd4=
-cloud.google.com/go/analytics v0.17.0/go.mod h1:WXFa3WSym4IZ+JiKmavYdJwGG/CvpqiqczmL59bTD9M=
-cloud.google.com/go/analytics v0.18.0/go.mod h1:ZkeHGQlcIPkw0R/GW+boWHhCOR43xz9RN/jn7WcqfIE=
-cloud.google.com/go/analytics v0.19.0/go.mod h1:k8liqf5/HCnOUkbawNtrWWc+UAzyDlW89doe8TtoDsE=
-cloud.google.com/go/analytics v0.21.2/go.mod h1:U8dcUtmDmjrmUTnnnRnI4m6zKn/yaA5N9RlEkYFHpQo=
-cloud.google.com/go/apigateway v1.3.0/go.mod h1:89Z8Bhpmxu6AmUxuVRg/ECRGReEdiP3vQtk4Z1J9rJk=
-cloud.google.com/go/apigateway v1.4.0/go.mod h1:pHVY9MKGaH9PQ3pJ4YLzoj6U5FUDeDFBllIz7WmzJoc=
-cloud.google.com/go/apigateway v1.5.0/go.mod h1:GpnZR3Q4rR7LVu5951qfXPJCHquZt02jf7xQx7kpqN8=
-cloud.google.com/go/apigateway v1.6.1/go.mod h1:ufAS3wpbRjqfZrzpvLC2oh0MFlpRJm2E/ts25yyqmXA=
-cloud.google.com/go/apigeeconnect v1.3.0/go.mod h1:G/AwXFAKo0gIXkPTVfZDd2qA1TxBXJ3MgMRBQkIi9jc=
-cloud.google.com/go/apigeeconnect v1.4.0/go.mod h1:kV4NwOKqjvt2JYR0AoIWo2QGfoRtn/pkS3QlHp0Ni04=
-cloud.google.com/go/apigeeconnect v1.5.0/go.mod h1:KFaCqvBRU6idyhSNyn3vlHXc8VMDJdRmwDF6JyFRqZ8=
-cloud.google.com/go/apigeeconnect v1.6.1/go.mod h1:C4awq7x0JpLtrlQCr8AzVIzAaYgngRqWf9S5Uhg+wWs=
-cloud.google.com/go/apigeeregistry v0.4.0/go.mod h1:EUG4PGcsZvxOXAdyEghIdXwAEi/4MEaoqLMLDMIwKXY=
-cloud.google.com/go/apigeeregistry v0.5.0/go.mod h1:YR5+s0BVNZfVOUkMa5pAR2xGd0A473vA5M7j247o1wM=
-cloud.google.com/go/apigeeregistry v0.6.0/go.mod h1:BFNzW7yQVLZ3yj0TKcwzb8n25CFBri51GVGOEUcgQsc=
-cloud.google.com/go/apigeeregistry v0.7.1/go.mod h1:1XgyjZye4Mqtw7T9TsY4NW10U7BojBvG4RMD+vRDrIw=
-cloud.google.com/go/apikeys v0.4.0/go.mod h1:XATS/yqZbaBK0HOssf+ALHp8jAlNHUgyfprvNcBIszU=
-cloud.google.com/go/apikeys v0.5.0/go.mod h1:5aQfwY4D+ewMMWScd3hm2en3hCj+BROlyrt3ytS7KLI=
-cloud.google.com/go/apikeys v0.6.0/go.mod h1:kbpXu5upyiAlGkKrJgQl8A0rKNNJ7dQ377pdroRSSi8=
-cloud.google.com/go/appengine v1.4.0/go.mod h1:CS2NhuBuDXM9f+qscZ6V86m1MIIqPj3WC/UoEuR1Sno=
-cloud.google.com/go/appengine v1.5.0/go.mod h1:TfasSozdkFI0zeoxW3PTBLiNqRmzraodCWatWI9Dmak=
-cloud.google.com/go/appengine v1.6.0/go.mod h1:hg6i0J/BD2cKmDJbaFSYHFyZkgBEfQrDg/X0V5fJn84=
-cloud.google.com/go/appengine v1.7.0/go.mod h1:eZqpbHFCqRGa2aCdope7eC0SWLV1j0neb/QnMJVWx6A=
-cloud.google.com/go/appengine v1.7.1/go.mod h1:IHLToyb/3fKutRysUlFO0BPt5j7RiQ45nrzEJmKTo6E=
-cloud.google.com/go/appengine v1.8.1/go.mod h1:6NJXGLVhZCN9aQ/AEDvmfzKEfoYBlfB80/BHiKVputY=
-cloud.google.com/go/area120 v0.5.0/go.mod h1:DE/n4mp+iqVyvxHN41Vf1CR602GiHQjFPusMFW6bGR4=
-cloud.google.com/go/area120 v0.6.0/go.mod h1:39yFJqWVgm0UZqWTOdqkLhjoC7uFfgXRC8g/ZegeAh0=
-cloud.google.com/go/area120 v0.7.0/go.mod h1:a3+8EUD1SX5RUcCs3MY5YasiO1z6yLiNLRiFrykbynY=
-cloud.google.com/go/area120 v0.7.1/go.mod h1:j84i4E1RboTWjKtZVWXPqvK5VHQFJRF2c1Nm69pWm9k=
-cloud.google.com/go/area120 v0.8.1/go.mod h1:BVfZpGpB7KFVNxPiQBuHkX6Ed0rS51xIgmGyjrAfzsg=
-cloud.google.com/go/artifactregistry v1.6.0/go.mod h1:IYt0oBPSAGYj/kprzsBjZ/4LnG/zOcHyFHjWPCi6SAQ=
-cloud.google.com/go/artifactregistry v1.7.0/go.mod h1:mqTOFOnGZx8EtSqK/ZWcsm/4U8B77rbcLP6ruDU2Ixk=
-cloud.google.com/go/artifactregistry v1.8.0/go.mod h1:w3GQXkJX8hiKN0v+at4b0qotwijQbYUqF2GWkZzAhC0=
-cloud.google.com/go/artifactregistry v1.9.0/go.mod h1:2K2RqvA2CYvAeARHRkLDhMDJ3OXy26h3XW+3/Jh2uYc=
-cloud.google.com/go/artifactregistry v1.11.1/go.mod h1:lLYghw+Itq9SONbCa1YWBoWs1nOucMH0pwXN1rOBZFI=
-cloud.google.com/go/artifactregistry v1.11.2/go.mod h1:nLZns771ZGAwVLzTX/7Al6R9ehma4WUEhZGWV6CeQNQ=
-cloud.google.com/go/artifactregistry v1.12.0/go.mod h1:o6P3MIvtzTOnmvGagO9v/rOjjA0HmhJ+/6KAXrmYDCI=
-cloud.google.com/go/artifactregistry v1.13.0/go.mod h1:uy/LNfoOIivepGhooAUpL1i30Hgee3Cu0l4VTWHUC08=
-cloud.google.com/go/artifactregistry v1.14.1/go.mod h1:nxVdG19jTaSTu7yA7+VbWL346r3rIdkZ142BSQqhn5E=
-cloud.google.com/go/asset v1.5.0/go.mod h1:5mfs8UvcM5wHhqtSv8J1CtxxaQq3AdBxxQi2jGW/K4o=
-cloud.google.com/go/asset v1.7.0/go.mod h1:YbENsRK4+xTiL+Ofoj5Ckf+O17kJtgp3Y3nn4uzZz5s=
-cloud.google.com/go/asset v1.8.0/go.mod h1:mUNGKhiqIdbr8X7KNayoYvyc4HbbFO9URsjbytpUaW0=
-cloud.google.com/go/asset v1.9.0/go.mod h1:83MOE6jEJBMqFKadM9NLRcs80Gdw76qGuHn8m3h8oHQ=
-cloud.google.com/go/asset v1.10.0/go.mod h1:pLz7uokL80qKhzKr4xXGvBQXnzHn5evJAEAtZiIb0wY=
-cloud.google.com/go/asset v1.11.1/go.mod h1:fSwLhbRvC9p9CXQHJ3BgFeQNM4c9x10lqlrdEUYXlJo=
-cloud.google.com/go/asset v1.12.0/go.mod h1:h9/sFOa4eDIyKmH6QMpm4eUK3pDojWnUhTgJlk762Hg=
-cloud.google.com/go/asset v1.13.0/go.mod h1:WQAMyYek/b7NBpYq/K4KJWcRqzoalEsxz/t/dTk4THw=
-cloud.google.com/go/asset v1.14.1/go.mod h1:4bEJ3dnHCqWCDbWJ/6Vn7GVI9LerSi7Rfdi03hd+WTQ=
-cloud.google.com/go/assuredworkloads v1.5.0/go.mod h1:n8HOZ6pff6re5KYfBXcFvSViQjDwxFkAkmUFffJRbbY=
-cloud.google.com/go/assuredworkloads v1.6.0/go.mod h1:yo2YOk37Yc89Rsd5QMVECvjaMKymF9OP+QXWlKXUkXw=
-cloud.google.com/go/assuredworkloads v1.7.0/go.mod h1:z/736/oNmtGAyU47reJgGN+KVoYoxeLBoj4XkKYscNI=
-cloud.google.com/go/assuredworkloads v1.8.0/go.mod h1:AsX2cqyNCOvEQC8RMPnoc0yEarXQk6WEKkxYfL6kGIo=
-cloud.google.com/go/assuredworkloads v1.9.0/go.mod h1:kFuI1P78bplYtT77Tb1hi0FMxM0vVpRC7VVoJC3ZoT0=
-cloud.google.com/go/assuredworkloads v1.10.0/go.mod h1:kwdUQuXcedVdsIaKgKTp9t0UJkE5+PAVNhdQm4ZVq2E=
-cloud.google.com/go/assuredworkloads v1.11.1/go.mod h1:+F04I52Pgn5nmPG36CWFtxmav6+7Q+c5QyJoL18Lry0=
-cloud.google.com/go/automl v1.5.0/go.mod h1:34EjfoFGMZ5sgJ9EoLsRtdPSNZLcfflJR39VbVNS2M0=
-cloud.google.com/go/automl v1.6.0/go.mod h1:ugf8a6Fx+zP0D59WLhqgTDsQI9w07o64uf/Is3Nh5p8=
-cloud.google.com/go/automl v1.7.0/go.mod h1:RL9MYCCsJEOmt0Wf3z9uzG0a7adTT1fe+aObgSpkCt8=
-cloud.google.com/go/automl v1.8.0/go.mod h1:xWx7G/aPEe/NP+qzYXktoBSDfjO+vnKMGgsApGJJquM=
-cloud.google.com/go/automl v1.12.0/go.mod h1:tWDcHDp86aMIuHmyvjuKeeHEGq76lD7ZqfGLN6B0NuU=
-cloud.google.com/go/automl v1.13.1/go.mod h1:1aowgAHWYZU27MybSCFiukPO7xnyawv7pt3zK4bheQE=
-cloud.google.com/go/baremetalsolution v0.3.0/go.mod h1:XOrocE+pvK1xFfleEnShBlNAXf+j5blPPxrhjKgnIFc=
-cloud.google.com/go/baremetalsolution v0.4.0/go.mod h1:BymplhAadOO/eBa7KewQ0Ppg4A4Wplbn+PsFKRLo0uI=
-cloud.google.com/go/baremetalsolution v0.5.0/go.mod h1:dXGxEkmR9BMwxhzBhV0AioD0ULBmuLZI8CdwalUxuss=
-cloud.google.com/go/batch v0.3.0/go.mod h1:TR18ZoAekj1GuirsUsR1ZTKN3FC/4UDnScjT8NXImFE=
-cloud.google.com/go/batch v0.4.0/go.mod h1:WZkHnP43R/QCGQsZ+0JyG4i79ranE2u8xvjq/9+STPE=
-cloud.google.com/go/batch v0.7.0/go.mod h1:vLZN95s6teRUqRQ4s3RLDsH8PvboqBK+rn1oevL159g=
-cloud.google.com/go/beyondcorp v0.2.0/go.mod h1:TB7Bd+EEtcw9PCPQhCJtJGjk/7TC6ckmnSFS+xwTfm4=
-cloud.google.com/go/beyondcorp v0.3.0/go.mod h1:E5U5lcrcXMsCuoDNyGrpyTm/hn7ne941Jz2vmksAxW8=
-cloud.google.com/go/beyondcorp v0.4.0/go.mod h1:3ApA0mbhHx6YImmuubf5pyW8srKnCEPON32/5hj+RmM=
-cloud.google.com/go/beyondcorp v0.5.0/go.mod h1:uFqj9X+dSfrheVp7ssLTaRHd2EHqSL4QZmH4e8WXGGU=
-cloud.google.com/go/beyondcorp v0.6.1/go.mod h1:YhxDWw946SCbmcWo3fAhw3V4XZMSpQ/VYfcKGAEU8/4=
-cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
-cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
-cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
-cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
-cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
-cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
-cloud.google.com/go/bigquery v1.42.0/go.mod h1:8dRTJxhtG+vwBKzE5OseQn/hiydoQN3EedCaOdYmxRA=
-cloud.google.com/go/bigquery v1.43.0/go.mod h1:ZMQcXHsl+xmU1z36G2jNGZmKp9zNY5BUua5wDgmNCfw=
-cloud.google.com/go/bigquery v1.44.0/go.mod h1:0Y33VqXTEsbamHJvJHdFmtqHvMIY28aK1+dFsvaChGc=
-cloud.google.com/go/bigquery v1.47.0/go.mod h1:sA9XOgy0A8vQK9+MWhEQTY6Tix87M/ZurWFIxmF9I/E=
-cloud.google.com/go/bigquery v1.48.0/go.mod h1:QAwSz+ipNgfL5jxiaK7weyOhzdoAy1zFm0Nf1fysJac=
-cloud.google.com/go/bigquery v1.49.0/go.mod h1:Sv8hMmTFFYBlt/ftw2uN6dFdQPzBlREY9yBh7Oy7/4Q=
-cloud.google.com/go/bigquery v1.50.0/go.mod h1:YrleYEh2pSEbgTBZYMJ5SuSr0ML3ypjRB1zgf7pvQLU=
-cloud.google.com/go/bigquery v1.52.0/go.mod h1:3b/iXjRQGU4nKa87cXeg6/gogLjO8C6PmuM8i5Bi/u4=
-cloud.google.com/go/billing v1.4.0/go.mod h1:g9IdKBEFlItS8bTtlrZdVLWSSdSyFUZKXNS02zKMOZY=
-cloud.google.com/go/billing v1.5.0/go.mod h1:mztb1tBc3QekhjSgmpf/CV4LzWXLzCArwpLmP2Gm88s=
-cloud.google.com/go/billing v1.6.0/go.mod h1:WoXzguj+BeHXPbKfNWkqVtDdzORazmCjraY+vrxcyvI=
-cloud.google.com/go/billing v1.7.0/go.mod h1:q457N3Hbj9lYwwRbnlD7vUpyjq6u5U1RAOArInEiD5Y=
-cloud.google.com/go/billing v1.12.0/go.mod h1:yKrZio/eu+okO/2McZEbch17O5CB5NpZhhXG6Z766ss=
-cloud.google.com/go/billing v1.13.0/go.mod h1:7kB2W9Xf98hP9Sr12KfECgfGclsH3CQR0R08tnRlRbc=
-cloud.google.com/go/billing v1.16.0/go.mod h1:y8vx09JSSJG02k5QxbycNRrN7FGZB6F3CAcgum7jvGA=
-cloud.google.com/go/binaryauthorization v1.1.0/go.mod h1:xwnoWu3Y84jbuHa0zd526MJYmtnVXn0syOjaJgy4+dM=
-cloud.google.com/go/binaryauthorization v1.2.0/go.mod h1:86WKkJHtRcv5ViNABtYMhhNWRrD1Vpi//uKEy7aYEfI=
-cloud.google.com/go/binaryauthorization v1.3.0/go.mod h1:lRZbKgjDIIQvzYQS1p99A7/U1JqvqeZg0wiI5tp6tg0=
-cloud.google.com/go/binaryauthorization v1.4.0/go.mod h1:tsSPQrBd77VLplV70GUhBf/Zm3FsKmgSqgm4UmiDItk=
-cloud.google.com/go/binaryauthorization v1.5.0/go.mod h1:OSe4OU1nN/VswXKRBmciKpo9LulY41gch5c68htf3/Q=
-cloud.google.com/go/binaryauthorization v1.6.1/go.mod h1:TKt4pa8xhowwffiBmbrbcxijJRZED4zrqnwZ1lKH51U=
-cloud.google.com/go/certificatemanager v1.3.0/go.mod h1:n6twGDvcUBFu9uBgt4eYvvf3sQ6My8jADcOVwHmzadg=
-cloud.google.com/go/certificatemanager v1.4.0/go.mod h1:vowpercVFyqs8ABSmrdV+GiFf2H/ch3KyudYQEMM590=
-cloud.google.com/go/certificatemanager v1.6.0/go.mod h1:3Hh64rCKjRAX8dXgRAyOcY5vQ/fE1sh8o+Mdd6KPgY8=
-cloud.google.com/go/certificatemanager v1.7.1/go.mod h1:iW8J3nG6SaRYImIa+wXQ0g8IgoofDFRp5UMzaNk1UqI=
-cloud.google.com/go/channel v1.8.0/go.mod h1:W5SwCXDJsq/rg3tn3oG0LOxpAo6IMxNa09ngphpSlnk=
-cloud.google.com/go/channel v1.9.0/go.mod h1:jcu05W0my9Vx4mt3/rEHpfxc9eKi9XwsdDL8yBMbKUk=
-cloud.google.com/go/channel v1.11.0/go.mod h1:IdtI0uWGqhEeatSB62VOoJ8FSUhJ9/+iGkJVqp74CGE=
-cloud.google.com/go/channel v1.12.0/go.mod h1:VkxCGKASi4Cq7TbXxlaBezonAYpp1GCnKMY6tnMQnLU=
-cloud.google.com/go/channel v1.16.0/go.mod h1:eN/q1PFSl5gyu0dYdmxNXscY/4Fi7ABmeHCJNf/oHmc=
-cloud.google.com/go/cloudbuild v1.3.0/go.mod h1:WequR4ULxlqvMsjDEEEFnOG5ZSRSgWOywXYDb1vPE6U=
-cloud.google.com/go/cloudbuild v1.4.0/go.mod h1:5Qwa40LHiOXmz3386FrjrYM93rM/hdRr7b53sySrTqA=
-cloud.google.com/go/cloudbuild v1.6.0/go.mod h1:UIbc/w9QCbH12xX+ezUsgblrWv+Cv4Tw83GiSMHOn9M=
-cloud.google.com/go/cloudbuild v1.7.0/go.mod h1:zb5tWh2XI6lR9zQmsm1VRA+7OCuve5d8S+zJUul8KTg=
-cloud.google.com/go/cloudbuild v1.9.0/go.mod h1:qK1d7s4QlO0VwfYn5YuClDGg2hfmLZEb4wQGAbIgL1s=
-cloud.google.com/go/cloudbuild v1.10.1/go.mod h1:lyJg7v97SUIPq4RC2sGsz/9tNczhyv2AjML/ci4ulzU=
-cloud.google.com/go/clouddms v1.3.0/go.mod h1:oK6XsCDdW4Ib3jCCBugx+gVjevp2TMXFtgxvPSee3OM=
-cloud.google.com/go/clouddms v1.4.0/go.mod h1:Eh7sUGCC+aKry14O1NRljhjyrr0NFC0G2cjwX0cByRk=
-cloud.google.com/go/clouddms v1.5.0/go.mod h1:QSxQnhikCLUw13iAbffF2CZxAER3xDGNHjsTAkQJcQA=
-cloud.google.com/go/clouddms v1.6.1/go.mod h1:Ygo1vL52Ov4TBZQquhz5fiw2CQ58gvu+PlS6PVXCpZI=
-cloud.google.com/go/cloudtasks v1.5.0/go.mod h1:fD92REy1x5woxkKEkLdvavGnPJGEn8Uic9nWuLzqCpY=
-cloud.google.com/go/cloudtasks v1.6.0/go.mod h1:C6Io+sxuke9/KNRkbQpihnW93SWDU3uXt92nu85HkYI=
-cloud.google.com/go/cloudtasks v1.7.0/go.mod h1:ImsfdYWwlWNJbdgPIIGJWC+gemEGTBK/SunNQQNCAb4=
-cloud.google.com/go/cloudtasks v1.8.0/go.mod h1:gQXUIwCSOI4yPVK7DgTVFiiP0ZW/eQkydWzwVMdHxrI=
-cloud.google.com/go/cloudtasks v1.9.0/go.mod h1:w+EyLsVkLWHcOaqNEyvcKAsWp9p29dL6uL9Nst1cI7Y=
-cloud.google.com/go/cloudtasks v1.10.0/go.mod h1:NDSoTLkZ3+vExFEWu2UJV1arUyzVDAiZtdWcsUyNwBs=
-cloud.google.com/go/cloudtasks v1.11.1/go.mod h1:a9udmnou9KO2iulGscKR0qBYjreuX8oHwpmFsKspEvM=
-cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow=
-cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM=
-cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M=
-cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s=
-cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU=
-cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U=
-cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU=
-cloud.google.com/go/compute v1.12.0/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU=
-cloud.google.com/go/compute v1.12.1/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU=
-cloud.google.com/go/compute v1.13.0/go.mod h1:5aPTS0cUNMIc1CE546K+Th6weJUNQErARyZtRXDJ8GE=
-cloud.google.com/go/compute v1.14.0/go.mod h1:YfLtxrj9sU4Yxv+sXzZkyPjEyPBZfXHUvjxega5vAdo=
-cloud.google.com/go/compute v1.15.1/go.mod h1:bjjoF/NtFUrkD/urWfdHaKuOPDR5nWIs63rR+SXhcpA=
-cloud.google.com/go/compute v1.18.0/go.mod h1:1X7yHxec2Ga+Ss6jPyjxRxpu2uu7PLgsOVXvgU0yacs=
-cloud.google.com/go/compute v1.19.0/go.mod h1:rikpw2y+UMidAe9tISo04EHNOIf42RLYF/q8Bs93scU=
-cloud.google.com/go/compute v1.19.1/go.mod h1:6ylj3a05WF8leseCdIf77NK0g1ey+nj5IKd5/kvShxE=
-cloud.google.com/go/compute v1.19.3/go.mod h1:qxvISKp/gYnXkSAD1ppcSOveRAmzxicEv/JlizULFrI=
-cloud.google.com/go/compute v1.20.1 h1:6aKEtlUiwEpJzM001l0yFkpXmUVXaN8W+fbkb2AZNbg=
-cloud.google.com/go/compute v1.20.1/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM=
-cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZEXYonfTBHHFPO/4UU=
-cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
-cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM=
-cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
-cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
-cloud.google.com/go/contactcenterinsights v1.3.0/go.mod h1:Eu2oemoePuEFc/xKFPjbTuPSj0fYJcPls9TFlPNnHHY=
-cloud.google.com/go/contactcenterinsights v1.4.0/go.mod h1:L2YzkGbPsv+vMQMCADxJoT9YiTTnSEd6fEvCeHTYVck=
-cloud.google.com/go/contactcenterinsights v1.6.0/go.mod h1:IIDlT6CLcDoyv79kDv8iWxMSTZhLxSCofVV5W6YFM/w=
-cloud.google.com/go/contactcenterinsights v1.9.1/go.mod h1:bsg/R7zGLYMVxFFzfh9ooLTruLRCG9fnzhH9KznHhbM=
-cloud.google.com/go/container v1.6.0/go.mod h1:Xazp7GjJSeUYo688S+6J5V+n/t+G5sKBTFkKNudGRxg=
-cloud.google.com/go/container v1.7.0/go.mod h1:Dp5AHtmothHGX3DwwIHPgq45Y8KmNsgN3amoYfxVkLo=
-cloud.google.com/go/container v1.13.1/go.mod h1:6wgbMPeQRw9rSnKBCAJXnds3Pzj03C4JHamr8asWKy4=
-cloud.google.com/go/container v1.14.0/go.mod h1:3AoJMPhHfLDxLvrlVWaK57IXzaPnLaZq63WX59aQBfM=
-cloud.google.com/go/container v1.15.0/go.mod h1:ft+9S0WGjAyjDggg5S06DXj+fHJICWg8L7isCQe9pQA=
-cloud.google.com/go/container v1.22.1/go.mod h1:lTNExE2R7f+DLbAN+rJiKTisauFCaoDq6NURZ83eVH4=
-cloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I=
-cloud.google.com/go/containeranalysis v0.6.0/go.mod h1:HEJoiEIu+lEXM+k7+qLCci0h33lX3ZqoYFdmPcoO7s4=
-cloud.google.com/go/containeranalysis v0.7.0/go.mod h1:9aUL+/vZ55P2CXfuZjS4UjQ9AgXoSw8Ts6lemfmxBxI=
-cloud.google.com/go/containeranalysis v0.9.0/go.mod h1:orbOANbwk5Ejoom+s+DUCTTJ7IBdBQJDcSylAx/on9s=
-cloud.google.com/go/containeranalysis v0.10.1/go.mod h1:Ya2jiILITMY68ZLPaogjmOMNkwsDrWBSTyBubGXO7j0=
-cloud.google.com/go/datacatalog v1.3.0/go.mod h1:g9svFY6tuR+j+hrTw3J2dNcmI0dzmSiyOzm8kpLq0a0=
-cloud.google.com/go/datacatalog v1.5.0/go.mod h1:M7GPLNQeLfWqeIm3iuiruhPzkt65+Bx8dAKvScX8jvs=
-cloud.google.com/go/datacatalog v1.6.0/go.mod h1:+aEyF8JKg+uXcIdAmmaMUmZ3q1b/lKLtXCmXdnc0lbc=
-cloud.google.com/go/datacatalog v1.7.0/go.mod h1:9mEl4AuDYWw81UGc41HonIHH7/sn52H0/tc8f8ZbZIE=
-cloud.google.com/go/datacatalog v1.8.0/go.mod h1:KYuoVOv9BM8EYz/4eMFxrr4DUKhGIOXxZoKYF5wdISM=
-cloud.google.com/go/datacatalog v1.8.1/go.mod h1:RJ58z4rMp3gvETA465Vg+ag8BGgBdnRPEMMSTr5Uv+M=
-cloud.google.com/go/datacatalog v1.12.0/go.mod h1:CWae8rFkfp6LzLumKOnmVh4+Zle4A3NXLzVJ1d1mRm0=
-cloud.google.com/go/datacatalog v1.13.0/go.mod h1:E4Rj9a5ZtAxcQJlEBTLgMTphfP11/lNaAshpoBgemX8=
-cloud.google.com/go/datacatalog v1.14.0/go.mod h1:h0PrGtlihoutNMp/uvwhawLQ9+c63Kz65UFqh49Yo+E=
-cloud.google.com/go/datacatalog v1.14.1/go.mod h1:d2CevwTG4yedZilwe+v3E3ZBDRMobQfSG/a6cCCN5R4=
-cloud.google.com/go/dataflow v0.6.0/go.mod h1:9QwV89cGoxjjSR9/r7eFDqqjtvbKxAK2BaYU6PVk9UM=
-cloud.google.com/go/dataflow v0.7.0/go.mod h1:PX526vb4ijFMesO1o202EaUmouZKBpjHsTlCtB4parQ=
-cloud.google.com/go/dataflow v0.8.0/go.mod h1:Rcf5YgTKPtQyYz8bLYhFoIV/vP39eL7fWNcSOyFfLJE=
-cloud.google.com/go/dataflow v0.9.1/go.mod h1:Wp7s32QjYuQDWqJPFFlnBKhkAtiFpMTdg00qGbnIHVw=
-cloud.google.com/go/dataform v0.3.0/go.mod h1:cj8uNliRlHpa6L3yVhDOBrUXH+BPAO1+KFMQQNSThKo=
-cloud.google.com/go/dataform v0.4.0/go.mod h1:fwV6Y4Ty2yIFL89huYlEkwUPtS7YZinZbzzj5S9FzCE=
-cloud.google.com/go/dataform v0.5.0/go.mod h1:GFUYRe8IBa2hcomWplodVmUx/iTL0FrsauObOM3Ipr0=
-cloud.google.com/go/dataform v0.6.0/go.mod h1:QPflImQy33e29VuapFdf19oPbE4aYTJxr31OAPV+ulA=
-cloud.google.com/go/dataform v0.7.0/go.mod h1:7NulqnVozfHvWUBpMDfKMUESr+85aJsC/2O0o3jWPDE=
-cloud.google.com/go/dataform v0.8.1/go.mod h1:3BhPSiw8xmppbgzeBbmDvmSWlwouuJkXsXsb8UBih9M=
-cloud.google.com/go/datafusion v1.4.0/go.mod h1:1Zb6VN+W6ALo85cXnM1IKiPw+yQMKMhB9TsTSRDo/38=
-cloud.google.com/go/datafusion v1.5.0/go.mod h1:Kz+l1FGHB0J+4XF2fud96WMmRiq/wj8N9u007vyXZ2w=
-cloud.google.com/go/datafusion v1.6.0/go.mod h1:WBsMF8F1RhSXvVM8rCV3AeyWVxcC2xY6vith3iw3S+8=
-cloud.google.com/go/datafusion v1.7.1/go.mod h1:KpoTBbFmoToDExJUso/fcCiguGDk7MEzOWXUsJo0wsI=
-cloud.google.com/go/datalabeling v0.5.0/go.mod h1:TGcJ0G2NzcsXSE/97yWjIZO0bXj0KbVlINXMG9ud42I=
-cloud.google.com/go/datalabeling v0.6.0/go.mod h1:WqdISuk/+WIGeMkpw/1q7bK/tFEZxsrFJOJdY2bXvTQ=
-cloud.google.com/go/datalabeling v0.7.0/go.mod h1:WPQb1y08RJbmpM3ww0CSUAGweL0SxByuW2E+FU+wXcM=
-cloud.google.com/go/datalabeling v0.8.1/go.mod h1:XS62LBSVPbYR54GfYQsPXZjTW8UxCK2fkDciSrpRFdY=
-cloud.google.com/go/dataplex v1.3.0/go.mod h1:hQuRtDg+fCiFgC8j0zV222HvzFQdRd+SVX8gdmFcZzA=
-cloud.google.com/go/dataplex v1.4.0/go.mod h1:X51GfLXEMVJ6UN47ESVqvlsRplbLhcsAt0kZCCKsU0A=
-cloud.google.com/go/dataplex v1.5.2/go.mod h1:cVMgQHsmfRoI5KFYq4JtIBEUbYwc3c7tXmIDhRmNNVQ=
-cloud.google.com/go/dataplex v1.6.0/go.mod h1:bMsomC/aEJOSpHXdFKFGQ1b0TDPIeL28nJObeO1ppRs=
-cloud.google.com/go/dataplex v1.8.1/go.mod h1:7TyrDT6BCdI8/38Uvp0/ZxBslOslP2X2MPDucliyvSE=
-cloud.google.com/go/dataproc v1.7.0/go.mod h1:CKAlMjII9H90RXaMpSxQ8EU6dQx6iAYNPcYPOkSbi8s=
-cloud.google.com/go/dataproc v1.8.0/go.mod h1:5OW+zNAH0pMpw14JVrPONsxMQYMBqJuzORhIBfBn9uI=
-cloud.google.com/go/dataproc v1.12.0/go.mod h1:zrF3aX0uV3ikkMz6z4uBbIKyhRITnxvr4i3IjKsKrw4=
-cloud.google.com/go/dataqna v0.5.0/go.mod h1:90Hyk596ft3zUQ8NkFfvICSIfHFh1Bc7C4cK3vbhkeo=
-cloud.google.com/go/dataqna v0.6.0/go.mod h1:1lqNpM7rqNLVgWBJyk5NF6Uen2PHym0jtVJonplVsDA=
-cloud.google.com/go/dataqna v0.7.0/go.mod h1:Lx9OcIIeqCrw1a6KdO3/5KMP1wAmTc0slZWwP12Qq3c=
-cloud.google.com/go/dataqna v0.8.1/go.mod h1:zxZM0Bl6liMePWsHA8RMGAfmTG34vJMapbHAxQ5+WA8=
-cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
-cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
-cloud.google.com/go/datastore v1.10.0/go.mod h1:PC5UzAmDEkAmkfaknstTYbNpgE49HAgW2J1gcgUfmdM=
-cloud.google.com/go/datastore v1.11.0/go.mod h1:TvGxBIHCS50u8jzG+AW/ppf87v1of8nwzFNgEZU1D3c=
-cloud.google.com/go/datastore v1.12.0/go.mod h1:KjdB88W897MRITkvWWJrg2OUtrR5XVj1EoLgSp6/N70=
-cloud.google.com/go/datastream v1.2.0/go.mod h1:i/uTP8/fZwgATHS/XFu0TcNUhuA0twZxxQ3EyCUQMwo=
-cloud.google.com/go/datastream v1.3.0/go.mod h1:cqlOX8xlyYF/uxhiKn6Hbv6WjwPPuI9W2M9SAXwaLLQ=
-cloud.google.com/go/datastream v1.4.0/go.mod h1:h9dpzScPhDTs5noEMQVWP8Wx8AFBRyS0s8KWPx/9r0g=
-cloud.google.com/go/datastream v1.5.0/go.mod h1:6TZMMNPwjUqZHBKPQ1wwXpb0d5VDVPl2/XoS5yi88q4=
-cloud.google.com/go/datastream v1.6.0/go.mod h1:6LQSuswqLa7S4rPAOZFVjHIG3wJIjZcZrw8JDEDJuIs=
-cloud.google.com/go/datastream v1.7.0/go.mod h1:uxVRMm2elUSPuh65IbZpzJNMbuzkcvu5CjMqVIUHrww=
-cloud.google.com/go/datastream v1.9.1/go.mod h1:hqnmr8kdUBmrnk65k5wNRoHSCYksvpdZIcZIEl8h43Q=
-cloud.google.com/go/deploy v1.4.0/go.mod h1:5Xghikd4VrmMLNaF6FiRFDlHb59VM59YoDQnOUdsH/c=
-cloud.google.com/go/deploy v1.5.0/go.mod h1:ffgdD0B89tToyW/U/D2eL0jN2+IEV/3EMuXHA0l4r+s=
-cloud.google.com/go/deploy v1.6.0/go.mod h1:f9PTHehG/DjCom3QH0cntOVRm93uGBDt2vKzAPwpXQI=
-cloud.google.com/go/deploy v1.8.0/go.mod h1:z3myEJnA/2wnB4sgjqdMfgxCA0EqC3RBTNcVPs93mtQ=
-cloud.google.com/go/deploy v1.11.0/go.mod h1:tKuSUV5pXbn67KiubiUNUejqLs4f5cxxiCNCeyl0F2g=
-cloud.google.com/go/dialogflow v1.15.0/go.mod h1:HbHDWs33WOGJgn6rfzBW1Kv807BE3O1+xGbn59zZWI4=
-cloud.google.com/go/dialogflow v1.16.1/go.mod h1:po6LlzGfK+smoSmTBnbkIZY2w8ffjz/RcGSS+sh1el0=
-cloud.google.com/go/dialogflow v1.17.0/go.mod h1:YNP09C/kXA1aZdBgC/VtXX74G/TKn7XVCcVumTflA+8=
-cloud.google.com/go/dialogflow v1.18.0/go.mod h1:trO7Zu5YdyEuR+BhSNOqJezyFQ3aUzz0njv7sMx/iek=
-cloud.google.com/go/dialogflow v1.19.0/go.mod h1:JVmlG1TwykZDtxtTXujec4tQ+D8SBFMoosgy+6Gn0s0=
-cloud.google.com/go/dialogflow v1.29.0/go.mod h1:b+2bzMe+k1s9V+F2jbJwpHPzrnIyHihAdRFMtn2WXuM=
-cloud.google.com/go/dialogflow v1.31.0/go.mod h1:cuoUccuL1Z+HADhyIA7dci3N5zUssgpBJmCzI6fNRB4=
-cloud.google.com/go/dialogflow v1.32.0/go.mod h1:jG9TRJl8CKrDhMEcvfcfFkkpp8ZhgPz3sBGmAUYJ2qE=
-cloud.google.com/go/dialogflow v1.38.0/go.mod h1:L7jnH+JL2mtmdChzAIcXQHXMvQkE3U4hTaNltEuxXn4=
-cloud.google.com/go/dlp v1.6.0/go.mod h1:9eyB2xIhpU0sVwUixfBubDoRwP+GjeUoxxeueZmqvmM=
-cloud.google.com/go/dlp v1.7.0/go.mod h1:68ak9vCiMBjbasxeVD17hVPxDEck+ExiHavX8kiHG+Q=
-cloud.google.com/go/dlp v1.9.0/go.mod h1:qdgmqgTyReTz5/YNSSuueR8pl7hO0o9bQ39ZhtgkWp4=
-cloud.google.com/go/dlp v1.10.1/go.mod h1:IM8BWz1iJd8njcNcG0+Kyd9OPnqnRNkDV8j42VT5KOI=
-cloud.google.com/go/documentai v1.7.0/go.mod h1:lJvftZB5NRiFSX4moiye1SMxHx0Bc3x1+p9e/RfXYiU=
-cloud.google.com/go/documentai v1.8.0/go.mod h1:xGHNEB7CtsnySCNrCFdCyyMz44RhFEEX2Q7UD0c5IhU=
-cloud.google.com/go/documentai v1.9.0/go.mod h1:FS5485S8R00U10GhgBC0aNGrJxBP8ZVpEeJ7PQDZd6k=
-cloud.google.com/go/documentai v1.10.0/go.mod h1:vod47hKQIPeCfN2QS/jULIvQTugbmdc0ZvxxfQY1bg4=
-cloud.google.com/go/documentai v1.16.0/go.mod h1:o0o0DLTEZ+YnJZ+J4wNfTxmDVyrkzFvttBXXtYRMHkM=
-cloud.google.com/go/documentai v1.18.0/go.mod h1:F6CK6iUH8J81FehpskRmhLq/3VlwQvb7TvwOceQ2tbs=
-cloud.google.com/go/documentai v1.20.0/go.mod h1:yJkInoMcK0qNAEdRnqY/D5asy73tnPe88I1YTZT+a8E=
-cloud.google.com/go/domains v0.6.0/go.mod h1:T9Rz3GasrpYk6mEGHh4rymIhjlnIuB4ofT1wTxDeT4Y=
-cloud.google.com/go/domains v0.7.0/go.mod h1:PtZeqS1xjnXuRPKE/88Iru/LdfoRyEHYA9nFQf4UKpg=
-cloud.google.com/go/domains v0.8.0/go.mod h1:M9i3MMDzGFXsydri9/vW+EWz9sWb4I6WyHqdlAk0idE=
-cloud.google.com/go/domains v0.9.1/go.mod h1:aOp1c0MbejQQ2Pjf1iJvnVyT+z6R6s8pX66KaCSDYfE=
-cloud.google.com/go/edgecontainer v0.1.0/go.mod h1:WgkZ9tp10bFxqO8BLPqv2LlfmQF1X8lZqwW4r1BTajk=
-cloud.google.com/go/edgecontainer v0.2.0/go.mod h1:RTmLijy+lGpQ7BXuTDa4C4ssxyXT34NIuHIgKuP4s5w=
-cloud.google.com/go/edgecontainer v0.3.0/go.mod h1:FLDpP4nykgwwIfcLt6zInhprzw0lEi2P1fjO6Ie0qbc=
-cloud.google.com/go/edgecontainer v1.0.0/go.mod h1:cttArqZpBB2q58W/upSG++ooo6EsblxDIolxa3jSjbY=
-cloud.google.com/go/edgecontainer v1.1.1/go.mod h1:O5bYcS//7MELQZs3+7mabRqoWQhXCzenBu0R8bz2rwk=
-cloud.google.com/go/errorreporting v0.3.0 h1:kj1XEWMu8P0qlLhm3FwcaFsUvXChV/OraZwA70trRR0=
-cloud.google.com/go/errorreporting v0.3.0/go.mod h1:xsP2yaAp+OAW4OIm60An2bbLpqIhKXdWR/tawvl7QzU=
-cloud.google.com/go/essentialcontacts v1.3.0/go.mod h1:r+OnHa5jfj90qIfZDO/VztSFqbQan7HV75p8sA+mdGI=
-cloud.google.com/go/essentialcontacts v1.4.0/go.mod h1:8tRldvHYsmnBCHdFpvU+GL75oWiBKl80BiqlFh9tp+8=
-cloud.google.com/go/essentialcontacts v1.5.0/go.mod h1:ay29Z4zODTuwliK7SnX8E86aUF2CTzdNtvv42niCX0M=
-cloud.google.com/go/essentialcontacts v1.6.2/go.mod h1:T2tB6tX+TRak7i88Fb2N9Ok3PvY3UNbUsMag9/BARh4=
-cloud.google.com/go/eventarc v1.7.0/go.mod h1:6ctpF3zTnaQCxUjHUdcfgcA1A2T309+omHZth7gDfmc=
-cloud.google.com/go/eventarc v1.8.0/go.mod h1:imbzxkyAU4ubfsaKYdQg04WS1NvncblHEup4kvF+4gw=
-cloud.google.com/go/eventarc v1.10.0/go.mod h1:u3R35tmZ9HvswGRBnF48IlYgYeBcPUCjkr4BTdem2Kw=
-cloud.google.com/go/eventarc v1.11.0/go.mod h1:PyUjsUKPWoRBCHeOxZd/lbOOjahV41icXyUY5kSTvVY=
-cloud.google.com/go/eventarc v1.12.1/go.mod h1:mAFCW6lukH5+IZjkvrEss+jmt2kOdYlN8aMx3sRJiAI=
-cloud.google.com/go/filestore v1.3.0/go.mod h1:+qbvHGvXU1HaKX2nD0WEPo92TP/8AQuCVEBXNY9z0+w=
-cloud.google.com/go/filestore v1.4.0/go.mod h1:PaG5oDfo9r224f8OYXURtAsY+Fbyq/bLYoINEK8XQAI=
-cloud.google.com/go/filestore v1.5.0/go.mod h1:FqBXDWBp4YLHqRnVGveOkHDf8svj9r5+mUDLupOWEDs=
-cloud.google.com/go/filestore v1.6.0/go.mod h1:di5unNuss/qfZTw2U9nhFqo8/ZDSc466dre85Kydllg=
-cloud.google.com/go/filestore v1.7.1/go.mod h1:y10jsorq40JJnjR/lQ8AfFbbcGlw3g+Dp8oN7i7FjV4=
-cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
-cloud.google.com/go/firestore v1.9.0/go.mod h1:HMkjKHNTtRyZNiMzu7YAsLr9K3X2udY2AMwDaMEQiiE=
-cloud.google.com/go/firestore v1.11.0/go.mod h1:b38dKhgzlmNNGTNZZwe7ZRFEuRab1Hay3/DBsIGKKy4=
-cloud.google.com/go/functions v1.6.0/go.mod h1:3H1UA3qiIPRWD7PeZKLvHZ9SaQhR26XIJcC0A5GbvAk=
-cloud.google.com/go/functions v1.7.0/go.mod h1:+d+QBcWM+RsrgZfV9xo6KfA1GlzJfxcfZcRPEhDDfzg=
-cloud.google.com/go/functions v1.8.0/go.mod h1:RTZ4/HsQjIqIYP9a9YPbU+QFoQsAlYgrwOXJWHn1POY=
-cloud.google.com/go/functions v1.9.0/go.mod h1:Y+Dz8yGguzO3PpIjhLTbnqV1CWmgQ5UwtlpzoyquQ08=
-cloud.google.com/go/functions v1.10.0/go.mod h1:0D3hEOe3DbEvCXtYOZHQZmD+SzYsi1YbI7dGvHfldXw=
-cloud.google.com/go/functions v1.12.0/go.mod h1:AXWGrF3e2C/5ehvwYo/GH6O5s09tOPksiKhz+hH8WkA=
-cloud.google.com/go/functions v1.13.0/go.mod h1:EU4O007sQm6Ef/PwRsI8N2umygGqPBS/IZQKBQBcJ3c=
-cloud.google.com/go/functions v1.15.1/go.mod h1:P5yNWUTkyU+LvW/S9O6V+V423VZooALQlqoXdoPz5AE=
-cloud.google.com/go/gaming v1.5.0/go.mod h1:ol7rGcxP/qHTRQE/RO4bxkXq+Fix0j6D4LFPzYTIrDM=
-cloud.google.com/go/gaming v1.6.0/go.mod h1:YMU1GEvA39Qt3zWGyAVA9bpYz/yAhTvaQ1t2sK4KPUA=
-cloud.google.com/go/gaming v1.7.0/go.mod h1:LrB8U7MHdGgFG851iHAfqUdLcKBdQ55hzXy9xBJz0+w=
-cloud.google.com/go/gaming v1.8.0/go.mod h1:xAqjS8b7jAVW0KFYeRUxngo9My3f33kFmua++Pi+ggM=
-cloud.google.com/go/gaming v1.9.0/go.mod h1:Fc7kEmCObylSWLO334NcO+O9QMDyz+TKC4v1D7X+Bc0=
-cloud.google.com/go/gaming v1.10.1/go.mod h1:XQQvtfP8Rb9Rxnxm5wFVpAp9zCQkJi2bLIb7iHGwB3s=
-cloud.google.com/go/gkebackup v0.2.0/go.mod h1:XKvv/4LfG829/B8B7xRkk8zRrOEbKtEam6yNfuQNH60=
-cloud.google.com/go/gkebackup v0.3.0/go.mod h1:n/E671i1aOQvUxT541aTkCwExO/bTer2HDlj4TsBRAo=
-cloud.google.com/go/gkebackup v0.4.0/go.mod h1:byAyBGUwYGEEww7xsbnUTBHIYcOPy/PgUWUtOeRm9Vg=
-cloud.google.com/go/gkeconnect v0.5.0/go.mod h1:c5lsNAg5EwAy7fkqX/+goqFsU1Da/jQFqArp+wGNr/o=
-cloud.google.com/go/gkeconnect v0.6.0/go.mod h1:Mln67KyU/sHJEBY8kFZ0xTeyPtzbq9StAVvEULYK16A=
-cloud.google.com/go/gkeconnect v0.7.0/go.mod h1:SNfmVqPkaEi3bF/B3CNZOAYPYdg7sU+obZ+QTky2Myw=
-cloud.google.com/go/gkeconnect v0.8.1/go.mod h1:KWiK1g9sDLZqhxB2xEuPV8V9NYzrqTUmQR9shJHpOZw=
-cloud.google.com/go/gkehub v0.9.0/go.mod h1:WYHN6WG8w9bXU0hqNxt8rm5uxnk8IH+lPY9J2TV7BK0=
-cloud.google.com/go/gkehub v0.10.0/go.mod h1:UIPwxI0DsrpsVoWpLB0stwKCP+WFVG9+y977wO+hBH0=
-cloud.google.com/go/gkehub v0.11.0/go.mod h1:JOWHlmN+GHyIbuWQPl47/C2RFhnFKH38jH9Ascu3n0E=
-cloud.google.com/go/gkehub v0.12.0/go.mod h1:djiIwwzTTBrF5NaXCGv3mf7klpEMcST17VBTVVDcuaw=
-cloud.google.com/go/gkehub v0.14.1/go.mod h1:VEXKIJZ2avzrbd7u+zeMtW00Y8ddk/4V9511C9CQGTY=
-cloud.google.com/go/gkemulticloud v0.3.0/go.mod h1:7orzy7O0S+5kq95e4Hpn7RysVA7dPs8W/GgfUtsPbrA=
-cloud.google.com/go/gkemulticloud v0.4.0/go.mod h1:E9gxVBnseLWCk24ch+P9+B2CoDFJZTyIgLKSalC7tuI=
-cloud.google.com/go/gkemulticloud v0.5.0/go.mod h1:W0JDkiyi3Tqh0TJr//y19wyb1yf8llHVto2Htf2Ja3Y=
-cloud.google.com/go/gkemulticloud v0.6.1/go.mod h1:kbZ3HKyTsiwqKX7Yw56+wUGwwNZViRnxWK2DVknXWfw=
-cloud.google.com/go/grafeas v0.2.0/go.mod h1:KhxgtF2hb0P191HlY5besjYm6MqTSTj3LSI+M+ByZHc=
-cloud.google.com/go/grafeas v0.3.0/go.mod h1:P7hgN24EyONOTMyeJH6DxG4zD7fwiYa5Q6GUgyFSOU8=
-cloud.google.com/go/gsuiteaddons v1.3.0/go.mod h1:EUNK/J1lZEZO8yPtykKxLXI6JSVN2rg9bN8SXOa0bgM=
-cloud.google.com/go/gsuiteaddons v1.4.0/go.mod h1:rZK5I8hht7u7HxFQcFei0+AtfS9uSushomRlg+3ua1o=
-cloud.google.com/go/gsuiteaddons v1.5.0/go.mod h1:TFCClYLd64Eaa12sFVmUyG62tk4mdIsI7pAnSXRkcFo=
-cloud.google.com/go/gsuiteaddons v1.6.1/go.mod h1:CodrdOqRZcLp5WOwejHWYBjZvfY0kOphkAKpF/3qdZY=
-cloud.google.com/go/iam v0.1.0/go.mod h1:vcUNEa0pEm0qRVpmWepWaFMIAI8/hjB9mO8rNCJtF6c=
-cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY=
-cloud.google.com/go/iam v0.5.0/go.mod h1:wPU9Vt0P4UmCux7mqtRu6jcpPAb74cP1fh50J3QpkUc=
-cloud.google.com/go/iam v0.6.0/go.mod h1:+1AH33ueBne5MzYccyMHtEKqLE4/kJOibtffMHDMFMc=
-cloud.google.com/go/iam v0.7.0/go.mod h1:H5Br8wRaDGNc8XP3keLc4unfUUZeyH3Sfl9XpQEYOeg=
-cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGESjkE=
-cloud.google.com/go/iam v0.11.0/go.mod h1:9PiLDanza5D+oWFZiH1uG+RnRCfEGKoyl6yo4cgWZGY=
-cloud.google.com/go/iam v0.12.0/go.mod h1:knyHGviacl11zrtZUoDuYpDgLjvr28sLQaG0YB2GYAY=
-cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0=
-cloud.google.com/go/iam v1.0.1/go.mod h1:yR3tmSL8BcZB4bxByRv2jkSIahVmCtfKZwLYGBalRE8=
-cloud.google.com/go/iam v1.1.0 h1:67gSqaPukx7O8WLLHMa0PNs3EBGd2eE4d+psbO/CO94=
-cloud.google.com/go/iam v1.1.0/go.mod h1:nxdHjaKfCr7fNYx/HJMM8LgiMugmveWlkatear5gVyk=
-cloud.google.com/go/iap v1.4.0/go.mod h1:RGFwRJdihTINIe4wZ2iCP0zF/qu18ZwyKxrhMhygBEc=
-cloud.google.com/go/iap v1.5.0/go.mod h1:UH/CGgKd4KyohZL5Pt0jSKE4m3FR51qg6FKQ/z/Ix9A=
-cloud.google.com/go/iap v1.6.0/go.mod h1:NSuvI9C/j7UdjGjIde7t7HBz+QTwBcapPE07+sSRcLk=
-cloud.google.com/go/iap v1.7.0/go.mod h1:beqQx56T9O1G1yNPph+spKpNibDlYIiIixiqsQXxLIo=
-cloud.google.com/go/iap v1.7.1/go.mod h1:WapEwPc7ZxGt2jFGB/C/bm+hP0Y6NXzOYGjpPnmMS74=
-cloud.google.com/go/iap v1.8.1/go.mod h1:sJCbeqg3mvWLqjZNsI6dfAtbbV1DL2Rl7e1mTyXYREQ=
-cloud.google.com/go/ids v1.1.0/go.mod h1:WIuwCaYVOzHIj2OhN9HAwvW+DBdmUAdcWlFxRl+KubM=
-cloud.google.com/go/ids v1.2.0/go.mod h1:5WXvp4n25S0rA/mQWAg1YEEBBq6/s+7ml1RDCW1IrcY=
-cloud.google.com/go/ids v1.3.0/go.mod h1:JBdTYwANikFKaDP6LtW5JAi4gubs57SVNQjemdt6xV4=
-cloud.google.com/go/ids v1.4.1/go.mod h1:np41ed8YMU8zOgv53MMMoCntLTn2lF+SUzlM+O3u/jw=
-cloud.google.com/go/iot v1.3.0/go.mod h1:r7RGh2B61+B8oz0AGE+J72AhA0G7tdXItODWsaA2oLs=
-cloud.google.com/go/iot v1.4.0/go.mod h1:dIDxPOn0UvNDUMD8Ger7FIaTuvMkj+aGk94RPP0iV+g=
-cloud.google.com/go/iot v1.5.0/go.mod h1:mpz5259PDl3XJthEmh9+ap0affn/MqNSP4My77Qql9o=
-cloud.google.com/go/iot v1.6.0/go.mod h1:IqdAsmE2cTYYNO1Fvjfzo9po179rAtJeVGUvkLN3rLE=
-cloud.google.com/go/iot v1.7.1/go.mod h1:46Mgw7ev1k9KqK1ao0ayW9h0lI+3hxeanz+L1zmbbbk=
-cloud.google.com/go/kms v1.4.0/go.mod h1:fajBHndQ+6ubNw6Ss2sSd+SWvjL26RNo/dr7uxsnnOA=
-cloud.google.com/go/kms v1.5.0/go.mod h1:QJS2YY0eJGBg3mnDfuaCyLauWwBJiHRboYxJ++1xJNg=
-cloud.google.com/go/kms v1.6.0/go.mod h1:Jjy850yySiasBUDi6KFUwUv2n1+o7QZFyuUJg6OgjA0=
-cloud.google.com/go/kms v1.8.0/go.mod h1:4xFEhYFqvW+4VMELtZyxomGSYtSQKzM178ylFW4jMAg=
-cloud.google.com/go/kms v1.9.0/go.mod h1:qb1tPTgfF9RQP8e1wq4cLFErVuTJv7UsSC915J8dh3w=
-cloud.google.com/go/kms v1.10.0/go.mod h1:ng3KTUtQQU9bPX3+QGLsflZIHlkbn8amFAMY63m8d24=
-cloud.google.com/go/kms v1.10.1/go.mod h1:rIWk/TryCkR59GMC3YtHtXeLzd634lBbKenvyySAyYI=
-cloud.google.com/go/kms v1.11.0/go.mod h1:hwdiYC0xjnWsKQQCQQmIQnS9asjYVSK6jtXm+zFqXLM=
-cloud.google.com/go/kms v1.12.1 h1:xZmZuwy2cwzsocmKDOPu4BL7umg8QXagQx6fKVmf45U=
-cloud.google.com/go/kms v1.12.1/go.mod h1:c9J991h5DTl+kg7gi3MYomh12YEENGrf48ee/N/2CDM=
-cloud.google.com/go/language v1.4.0/go.mod h1:F9dRpNFQmJbkaop6g0JhSBXCNlO90e1KWx5iDdxbWic=
-cloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQnWM3mdEbhI=
-cloud.google.com/go/language v1.7.0/go.mod h1:DJ6dYN/W+SQOjF8e1hLQXMF21AkH2w9wiPzPCJa2MIE=
-cloud.google.com/go/language v1.8.0/go.mod h1:qYPVHf7SPoNNiCL2Dr0FfEFNil1qi3pQEyygwpgVKB8=
-cloud.google.com/go/language v1.9.0/go.mod h1:Ns15WooPM5Ad/5no/0n81yUetis74g3zrbeJBE+ptUY=
-cloud.google.com/go/language v1.10.1/go.mod h1:CPp94nsdVNiQEt1CNjF5WkTcisLiHPyIbMhvR8H2AW0=
-cloud.google.com/go/lifesciences v0.5.0/go.mod h1:3oIKy8ycWGPUyZDR/8RNnTOYevhaMLqh5vLUXs9zvT8=
-cloud.google.com/go/lifesciences v0.6.0/go.mod h1:ddj6tSX/7BOnhxCSd3ZcETvtNr8NZ6t/iPhY2Tyfu08=
-cloud.google.com/go/lifesciences v0.8.0/go.mod h1:lFxiEOMqII6XggGbOnKiyZ7IBwoIqA84ClvoezaA/bo=
-cloud.google.com/go/lifesciences v0.9.1/go.mod h1:hACAOd1fFbCGLr/+weUKRAJas82Y4vrL3O5326N//Wc=
-cloud.google.com/go/logging v1.6.1/go.mod h1:5ZO0mHHbvm8gEmeEUHrmDlTDSu5imF6MUP9OfilNXBw=
-cloud.google.com/go/logging v1.7.0/go.mod h1:3xjP2CjkM3ZkO73aj4ASA5wRPGGCRrPIAeNqVNkzY8M=
-cloud.google.com/go/longrunning v0.1.1/go.mod h1:UUFxuDWkv22EuY93jjmDMFT5GPQKeFVJBIF6QlTqdsE=
-cloud.google.com/go/longrunning v0.3.0/go.mod h1:qth9Y41RRSUE69rDcOn6DdK3HfQfsUI0YSmW3iIlLJc=
-cloud.google.com/go/longrunning v0.4.1/go.mod h1:4iWDqhBZ70CvZ6BfETbvam3T8FMvLK+eFj0E6AaRQTo=
-cloud.google.com/go/longrunning v0.4.2/go.mod h1:OHrnaYyLUV6oqwh0xiS7e5sLQhP1m0QU9R+WhGDMgIQ=
-cloud.google.com/go/longrunning v0.5.0/go.mod h1:0JNuqRShmscVAhIACGtskSAWtqtOoPkwP0YF1oVEchc=
-cloud.google.com/go/longrunning v0.5.1 h1:Fr7TXftcqTudoyRJa113hyaqlGdiBQkp0Gq7tErFDWI=
-cloud.google.com/go/longrunning v0.5.1/go.mod h1:spvimkwdz6SPWKEt/XBij79E9fiTkHSQl/fRUUQJYJc=
-cloud.google.com/go/managedidentities v1.3.0/go.mod h1:UzlW3cBOiPrzucO5qWkNkh0w33KFtBJU281hacNvsdE=
-cloud.google.com/go/managedidentities v1.4.0/go.mod h1:NWSBYbEMgqmbZsLIyKvxrYbtqOsxY1ZrGM+9RgDqInM=
-cloud.google.com/go/managedidentities v1.5.0/go.mod h1:+dWcZ0JlUmpuxpIDfyP5pP5y0bLdRwOS4Lp7gMni/LA=
-cloud.google.com/go/managedidentities v1.6.1/go.mod h1:h/irGhTN2SkZ64F43tfGPMbHnypMbu4RB3yl8YcuEak=
-cloud.google.com/go/maps v0.1.0/go.mod h1:BQM97WGyfw9FWEmQMpZ5T6cpovXXSd1cGmFma94eubI=
-cloud.google.com/go/maps v0.6.0/go.mod h1:o6DAMMfb+aINHz/p/jbcY+mYeXBoZoxTfdSQ8VAJaCw=
-cloud.google.com/go/maps v0.7.0/go.mod h1:3GnvVl3cqeSvgMcpRlQidXsPYuDGQ8naBis7MVzpXsY=
-cloud.google.com/go/mediatranslation v0.5.0/go.mod h1:jGPUhGTybqsPQn91pNXw0xVHfuJ3leR1wj37oU3y1f4=
-cloud.google.com/go/mediatranslation v0.6.0/go.mod h1:hHdBCTYNigsBxshbznuIMFNe5QXEowAuNmmC7h8pu5w=
-cloud.google.com/go/mediatranslation v0.7.0/go.mod h1:LCnB/gZr90ONOIQLgSXagp8XUW1ODs2UmUMvcgMfI2I=
-cloud.google.com/go/mediatranslation v0.8.1/go.mod h1:L/7hBdEYbYHQJhX2sldtTO5SZZ1C1vkapubj0T2aGig=
-cloud.google.com/go/memcache v1.4.0/go.mod h1:rTOfiGZtJX1AaFUrOgsMHX5kAzaTQ8azHiuDoTPzNsE=
-cloud.google.com/go/memcache v1.5.0/go.mod h1:dk3fCK7dVo0cUU2c36jKb4VqKPS22BTkf81Xq617aWM=
-cloud.google.com/go/memcache v1.6.0/go.mod h1:XS5xB0eQZdHtTuTF9Hf8eJkKtR3pVRCcvJwtm68T3rA=
-cloud.google.com/go/memcache v1.7.0/go.mod h1:ywMKfjWhNtkQTxrWxCkCFkoPjLHPW6A7WOTVI8xy3LY=
-cloud.google.com/go/memcache v1.9.0/go.mod h1:8oEyzXCu+zo9RzlEaEjHl4KkgjlNDaXbCQeQWlzNFJM=
-cloud.google.com/go/memcache v1.10.1/go.mod h1:47YRQIarv4I3QS5+hoETgKO40InqzLP6kpNLvyXuyaA=
-cloud.google.com/go/metastore v1.5.0/go.mod h1:2ZNrDcQwghfdtCwJ33nM0+GrBGlVuh8rakL3vdPY3XY=
-cloud.google.com/go/metastore v1.6.0/go.mod h1:6cyQTls8CWXzk45G55x57DVQ9gWg7RiH65+YgPsNh9s=
-cloud.google.com/go/metastore v1.7.0/go.mod h1:s45D0B4IlsINu87/AsWiEVYbLaIMeUSoxlKKDqBGFS8=
-cloud.google.com/go/metastore v1.8.0/go.mod h1:zHiMc4ZUpBiM7twCIFQmJ9JMEkDSyZS9U12uf7wHqSI=
-cloud.google.com/go/metastore v1.10.0/go.mod h1:fPEnH3g4JJAk+gMRnrAnoqyv2lpUCqJPWOodSaf45Eo=
-cloud.google.com/go/metastore v1.11.1/go.mod h1:uZuSo80U3Wd4zi6C22ZZliOUJ3XeM/MlYi/z5OAOWRA=
-cloud.google.com/go/monitoring v1.1.0/go.mod h1:L81pzz7HKn14QCMaCs6NTQkdBnE87TElyanS95vIcl4=
-cloud.google.com/go/monitoring v1.7.0/go.mod h1:HpYse6kkGo//7p6sT0wsIC6IBDET0RhIsnmlA53dvEk=
-cloud.google.com/go/monitoring v1.8.0/go.mod h1:E7PtoMJ1kQXWxPjB6mv2fhC5/15jInuulFdYYtlcvT4=
-cloud.google.com/go/monitoring v1.12.0/go.mod h1:yx8Jj2fZNEkL/GYZyTLS4ZtZEZN8WtDEiEqG4kLK50w=
-cloud.google.com/go/monitoring v1.13.0/go.mod h1:k2yMBAB1H9JT/QETjNkgdCGD9bPF712XiLTVr+cBrpw=
-cloud.google.com/go/monitoring v1.15.1 h1:65JhLMd+JiYnXr6j5Z63dUYCuOg770p8a/VC+gil/58=
-cloud.google.com/go/monitoring v1.15.1/go.mod h1:lADlSAlFdbqQuwwpaImhsJXu1QSdd3ojypXrFSMr2rM=
-cloud.google.com/go/networkconnectivity v1.4.0/go.mod h1:nOl7YL8odKyAOtzNX73/M5/mGZgqqMeryi6UPZTk/rA=
-cloud.google.com/go/networkconnectivity v1.5.0/go.mod h1:3GzqJx7uhtlM3kln0+x5wyFvuVH1pIBJjhCpjzSt75o=
-cloud.google.com/go/networkconnectivity v1.6.0/go.mod h1:OJOoEXW+0LAxHh89nXd64uGG+FbQoeH8DtxCHVOMlaM=
-cloud.google.com/go/networkconnectivity v1.7.0/go.mod h1:RMuSbkdbPwNMQjB5HBWD5MpTBnNm39iAVpC3TmsExt8=
-cloud.google.com/go/networkconnectivity v1.10.0/go.mod h1:UP4O4sWXJG13AqrTdQCD9TnLGEbtNRqjuaaA7bNjF5E=
-cloud.google.com/go/networkconnectivity v1.11.0/go.mod h1:iWmDD4QF16VCDLXUqvyspJjIEtBR/4zq5hwnY2X3scM=
-cloud.google.com/go/networkconnectivity v1.12.1/go.mod h1:PelxSWYM7Sh9/guf8CFhi6vIqf19Ir/sbfZRUwXh92E=
-cloud.google.com/go/networkmanagement v1.4.0/go.mod h1:Q9mdLLRn60AsOrPc8rs8iNV6OHXaGcDdsIQe1ohekq8=
-cloud.google.com/go/networkmanagement v1.5.0/go.mod h1:ZnOeZ/evzUdUsnvRt792H0uYEnHQEMaz+REhhzJRcf4=
-cloud.google.com/go/networkmanagement v1.6.0/go.mod h1:5pKPqyXjB/sgtvB5xqOemumoQNB7y95Q7S+4rjSOPYY=
-cloud.google.com/go/networkmanagement v1.8.0/go.mod h1:Ho/BUGmtyEqrttTgWEe7m+8vDdK74ibQc+Be0q7Fof0=
-cloud.google.com/go/networksecurity v0.5.0/go.mod h1:xS6fOCoqpVC5zx15Z/MqkfDwH4+m/61A3ODiDV1xmiQ=
-cloud.google.com/go/networksecurity v0.6.0/go.mod h1:Q5fjhTr9WMI5mbpRYEbiexTzROf7ZbDzvzCrNl14nyU=
-cloud.google.com/go/networksecurity v0.7.0/go.mod h1:mAnzoxx/8TBSyXEeESMy9OOYwo1v+gZ5eMRnsT5bC8k=
-cloud.google.com/go/networksecurity v0.8.0/go.mod h1:B78DkqsxFG5zRSVuwYFRZ9Xz8IcQ5iECsNrPn74hKHU=
-cloud.google.com/go/networksecurity v0.9.1/go.mod h1:MCMdxOKQ30wsBI1eI659f9kEp4wuuAueoC9AJKSPWZQ=
-cloud.google.com/go/notebooks v1.2.0/go.mod h1:9+wtppMfVPUeJ8fIWPOq1UnATHISkGXGqTkxeieQ6UY=
-cloud.google.com/go/notebooks v1.3.0/go.mod h1:bFR5lj07DtCPC7YAAJ//vHskFBxA5JzYlH68kXVdk34=
-cloud.google.com/go/notebooks v1.4.0/go.mod h1:4QPMngcwmgb6uw7Po99B2xv5ufVoIQ7nOGDyL4P8AgA=
-cloud.google.com/go/notebooks v1.5.0/go.mod h1:q8mwhnP9aR8Hpfnrc5iN5IBhrXUy8S2vuYs+kBJ/gu0=
-cloud.google.com/go/notebooks v1.7.0/go.mod h1:PVlaDGfJgj1fl1S3dUwhFMXFgfYGhYQt2164xOMONmE=
-cloud.google.com/go/notebooks v1.8.0/go.mod h1:Lq6dYKOYOWUCTvw5t2q1gp1lAp0zxAxRycayS0iJcqQ=
-cloud.google.com/go/notebooks v1.9.1/go.mod h1:zqG9/gk05JrzgBt4ghLzEepPHNwE5jgPcHZRKhlC1A8=
-cloud.google.com/go/optimization v1.1.0/go.mod h1:5po+wfvX5AQlPznyVEZjGJTMr4+CAkJf2XSTQOOl9l4=
-cloud.google.com/go/optimization v1.2.0/go.mod h1:Lr7SOHdRDENsh+WXVmQhQTrzdu9ybg0NecjHidBq6xs=
-cloud.google.com/go/optimization v1.3.1/go.mod h1:IvUSefKiwd1a5p0RgHDbWCIbDFgKuEdB+fPPuP0IDLI=
-cloud.google.com/go/optimization v1.4.1/go.mod h1:j64vZQP7h9bO49m2rVaTVoNM0vEBEN5eKPUPbZyXOrk=
-cloud.google.com/go/orchestration v1.3.0/go.mod h1:Sj5tq/JpWiB//X/q3Ngwdl5K7B7Y0KZ7bfv0wL6fqVA=
-cloud.google.com/go/orchestration v1.4.0/go.mod h1:6W5NLFWs2TlniBphAViZEVhrXRSMgUGDfW7vrWKvsBk=
-cloud.google.com/go/orchestration v1.6.0/go.mod h1:M62Bevp7pkxStDfFfTuCOaXgaaqRAga1yKyoMtEoWPQ=
-cloud.google.com/go/orchestration v1.8.1/go.mod h1:4sluRF3wgbYVRqz7zJ1/EUNc90TTprliq9477fGobD8=
-cloud.google.com/go/orgpolicy v1.4.0/go.mod h1:xrSLIV4RePWmP9P3tBl8S93lTmlAxjm06NSm2UTmKvE=
-cloud.google.com/go/orgpolicy v1.5.0/go.mod h1:hZEc5q3wzwXJaKrsx5+Ewg0u1LxJ51nNFlext7Tanwc=
-cloud.google.com/go/orgpolicy v1.10.0/go.mod h1:w1fo8b7rRqlXlIJbVhOMPrwVljyuW5mqssvBtU18ONc=
-cloud.google.com/go/orgpolicy v1.11.0/go.mod h1:2RK748+FtVvnfuynxBzdnyu7sygtoZa1za/0ZfpOs1M=
-cloud.google.com/go/orgpolicy v1.11.1/go.mod h1:8+E3jQcpZJQliP+zaFfayC2Pg5bmhuLK755wKhIIUCE=
-cloud.google.com/go/osconfig v1.7.0/go.mod h1:oVHeCeZELfJP7XLxcBGTMBvRO+1nQ5tFG9VQTmYS2Fs=
-cloud.google.com/go/osconfig v1.8.0/go.mod h1:EQqZLu5w5XA7eKizepumcvWx+m8mJUhEwiPqWiZeEdg=
-cloud.google.com/go/osconfig v1.9.0/go.mod h1:Yx+IeIZJ3bdWmzbQU4fxNl8xsZ4amB+dygAwFPlvnNo=
-cloud.google.com/go/osconfig v1.10.0/go.mod h1:uMhCzqC5I8zfD9zDEAfvgVhDS8oIjySWh+l4WK6GnWw=
-cloud.google.com/go/osconfig v1.11.0/go.mod h1:aDICxrur2ogRd9zY5ytBLV89KEgT2MKB2L/n6x1ooPw=
-cloud.google.com/go/osconfig v1.12.0/go.mod h1:8f/PaYzoS3JMVfdfTubkowZYGmAhUCjjwnjqWI7NVBc=
-cloud.google.com/go/osconfig v1.12.1/go.mod h1:4CjBxND0gswz2gfYRCUoUzCm9zCABp91EeTtWXyz0tE=
-cloud.google.com/go/oslogin v1.4.0/go.mod h1:YdgMXWRaElXz/lDk1Na6Fh5orF7gvmJ0FGLIs9LId4E=
-cloud.google.com/go/oslogin v1.5.0/go.mod h1:D260Qj11W2qx/HVF29zBg+0fd6YCSjSqLUkY/qEenQU=
-cloud.google.com/go/oslogin v1.6.0/go.mod h1:zOJ1O3+dTU8WPlGEkFSh7qeHPPSoxrcMbbK1Nm2iX70=
-cloud.google.com/go/oslogin v1.7.0/go.mod h1:e04SN0xO1UNJ1M5GP0vzVBFicIe4O53FOfcixIqTyXo=
-cloud.google.com/go/oslogin v1.9.0/go.mod h1:HNavntnH8nzrn8JCTT5fj18FuJLFJc4NaZJtBnQtKFs=
-cloud.google.com/go/oslogin v1.10.1/go.mod h1:x692z7yAue5nE7CsSnoG0aaMbNoRJRXO4sn73R+ZqAs=
-cloud.google.com/go/phishingprotection v0.5.0/go.mod h1:Y3HZknsK9bc9dMi+oE8Bim0lczMU6hrX0UpADuMefr0=
-cloud.google.com/go/phishingprotection v0.6.0/go.mod h1:9Y3LBLgy0kDTcYET8ZH3bq/7qni15yVUoAxiFxnlSUA=
-cloud.google.com/go/phishingprotection v0.7.0/go.mod h1:8qJI4QKHoda/sb/7/YmMQ2omRLSLYSu9bU0EKCNI+Lk=
-cloud.google.com/go/phishingprotection v0.8.1/go.mod h1:AxonW7GovcA8qdEk13NfHq9hNx5KPtfxXNeUxTDxB6I=
-cloud.google.com/go/policytroubleshooter v1.3.0/go.mod h1:qy0+VwANja+kKrjlQuOzmlvscn4RNsAc0e15GGqfMxg=
-cloud.google.com/go/policytroubleshooter v1.4.0/go.mod h1:DZT4BcRw3QoO8ota9xw/LKtPa8lKeCByYeKTIf/vxdE=
-cloud.google.com/go/policytroubleshooter v1.5.0/go.mod h1:Rz1WfV+1oIpPdN2VvvuboLVRsB1Hclg3CKQ53j9l8vw=
-cloud.google.com/go/policytroubleshooter v1.6.0/go.mod h1:zYqaPTsmfvpjm5ULxAyD/lINQxJ0DDsnWOP/GZ7xzBc=
-cloud.google.com/go/policytroubleshooter v1.7.1/go.mod h1:0NaT5v3Ag1M7U5r0GfDCpUFkWd9YqpubBWsQlhanRv0=
-cloud.google.com/go/privatecatalog v0.5.0/go.mod h1:XgosMUvvPyxDjAVNDYxJ7wBW8//hLDDYmnsNcMGq1K0=
-cloud.google.com/go/privatecatalog v0.6.0/go.mod h1:i/fbkZR0hLN29eEWiiwue8Pb+GforiEIBnV9yrRUOKI=
-cloud.google.com/go/privatecatalog v0.7.0/go.mod h1:2s5ssIFO69F5csTXcwBP7NPFTZvps26xGzvQ2PQaBYg=
-cloud.google.com/go/privatecatalog v0.8.0/go.mod h1:nQ6pfaegeDAq/Q5lrfCQzQLhubPiZhSaNhIgfJlnIXs=
-cloud.google.com/go/privatecatalog v0.9.1/go.mod h1:0XlDXW2unJXdf9zFz968Hp35gl/bhF4twwpXZAW50JA=
-cloud.google.com/go/profiler v0.3.1 h1:b5got9Be9Ia0HVvyt7PavWxXEht15B9lWnigdvHtxOc=
-cloud.google.com/go/profiler v0.3.1/go.mod h1:GsG14VnmcMFQ9b+kq71wh3EKMZr3WRMgLzNiFRpW7tE=
-cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
-cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
-cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
-cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
-cloud.google.com/go/pubsub v1.26.0/go.mod h1:QgBH3U/jdJy/ftjPhTkyXNj543Tin1pRYcdcPRnFIRI=
-cloud.google.com/go/pubsub v1.27.1/go.mod h1:hQN39ymbV9geqBnfQq6Xf63yNhUAhv9CZhzp5O6qsW0=
-cloud.google.com/go/pubsub v1.28.0/go.mod h1:vuXFpwaVoIPQMGXqRyUQigu/AX1S3IWugR9xznmcXX8=
-cloud.google.com/go/pubsub v1.30.0/go.mod h1:qWi1OPS0B+b5L+Sg6Gmc9zD1Y+HaM0MdUr7LsupY1P4=
-cloud.google.com/go/pubsub v1.32.0/go.mod h1:f+w71I33OMyxf9VpMVcZbnG5KSUkCOUHYpFd5U1GdRc=
-cloud.google.com/go/pubsub v1.33.0 h1:6SPCPvWav64tj0sVX/+npCBKhUi/UjJehy9op/V3p2g=
-cloud.google.com/go/pubsub v1.33.0/go.mod h1:f+w71I33OMyxf9VpMVcZbnG5KSUkCOUHYpFd5U1GdRc=
-cloud.google.com/go/pubsublite v1.5.0/go.mod h1:xapqNQ1CuLfGi23Yda/9l4bBCKz/wC3KIJ5gKcxveZg=
-cloud.google.com/go/pubsublite v1.6.0/go.mod h1:1eFCS0U11xlOuMFV/0iBqw3zP12kddMeCbj/F3FSj9k=
-cloud.google.com/go/pubsublite v1.7.0/go.mod h1:8hVMwRXfDfvGm3fahVbtDbiLePT3gpoiJYJY+vxWxVM=
-cloud.google.com/go/pubsublite v1.8.1/go.mod h1:fOLdU4f5xldK4RGJrBMm+J7zMWNj/k4PxwEZXy39QS0=
-cloud.google.com/go/recaptchaenterprise v1.3.1/go.mod h1:OdD+q+y4XGeAlxRaMn1Y7/GveP6zmq76byL6tjPE7d4=
-cloud.google.com/go/recaptchaenterprise/v2 v2.1.0/go.mod h1:w9yVqajwroDNTfGuhmOjPDN//rZGySaf6PtFVcSCa7o=
-cloud.google.com/go/recaptchaenterprise/v2 v2.2.0/go.mod h1:/Zu5jisWGeERrd5HnlS3EUGb/D335f9k51B/FVil0jk=
-cloud.google.com/go/recaptchaenterprise/v2 v2.3.0/go.mod h1:O9LwGCjrhGHBQET5CA7dd5NwwNQUErSgEDit1DLNTdo=
-cloud.google.com/go/recaptchaenterprise/v2 v2.4.0/go.mod h1:Am3LHfOuBstrLrNCBrlI5sbwx9LBg3te2N6hGvHn2mE=
-cloud.google.com/go/recaptchaenterprise/v2 v2.5.0/go.mod h1:O8LzcHXN3rz0j+LBC91jrwI3R+1ZSZEWrfL7XHgNo9U=
-cloud.google.com/go/recaptchaenterprise/v2 v2.6.0/go.mod h1:RPauz9jeLtB3JVzg6nCbe12qNoaa8pXc4d/YukAmcnA=
-cloud.google.com/go/recaptchaenterprise/v2 v2.7.0/go.mod h1:19wVj/fs5RtYtynAPJdDTb69oW0vNHYDBTbB4NvMD9c=
-cloud.google.com/go/recaptchaenterprise/v2 v2.7.2/go.mod h1:kR0KjsJS7Jt1YSyWFkseQ756D45kaYNTlDPPaRAvDBU=
-cloud.google.com/go/recommendationengine v0.5.0/go.mod h1:E5756pJcVFeVgaQv3WNpImkFP8a+RptV6dDLGPILjvg=
-cloud.google.com/go/recommendationengine v0.6.0/go.mod h1:08mq2umu9oIqc7tDy8sx+MNJdLG0fUi3vaSVbztHgJ4=
-cloud.google.com/go/recommendationengine v0.7.0/go.mod h1:1reUcE3GIu6MeBz/h5xZJqNLuuVjNg1lmWMPyjatzac=
-cloud.google.com/go/recommendationengine v0.8.1/go.mod h1:MrZihWwtFYWDzE6Hz5nKcNz3gLizXVIDI/o3G1DLcrE=
-cloud.google.com/go/recommender v1.5.0/go.mod h1:jdoeiBIVrJe9gQjwd759ecLJbxCDED4A6p+mqoqDvTg=
-cloud.google.com/go/recommender v1.6.0/go.mod h1:+yETpm25mcoiECKh9DEScGzIRyDKpZ0cEhWGo+8bo+c=
-cloud.google.com/go/recommender v1.7.0/go.mod h1:XLHs/W+T8olwlGOgfQenXBTbIseGclClff6lhFVe9Bs=
-cloud.google.com/go/recommender v1.8.0/go.mod h1:PkjXrTT05BFKwxaUxQmtIlrtj0kph108r02ZZQ5FE70=
-cloud.google.com/go/recommender v1.9.0/go.mod h1:PnSsnZY7q+VL1uax2JWkt/UegHssxjUVVCrX52CuEmQ=
-cloud.google.com/go/recommender v1.10.1/go.mod h1:XFvrE4Suqn5Cq0Lf+mCP6oBHD/yRMA8XxP5sb7Q7gpA=
-cloud.google.com/go/redis v1.7.0/go.mod h1:V3x5Jq1jzUcg+UNsRvdmsfuFnit1cfe3Z/PGyq/lm4Y=
-cloud.google.com/go/redis v1.8.0/go.mod h1:Fm2szCDavWzBk2cDKxrkmWBqoCiL1+Ctwq7EyqBCA/A=
-cloud.google.com/go/redis v1.9.0/go.mod h1:HMYQuajvb2D0LvMgZmLDZW8V5aOC/WxstZHiy4g8OiA=
-cloud.google.com/go/redis v1.10.0/go.mod h1:ThJf3mMBQtW18JzGgh41/Wld6vnDDc/F/F35UolRZPM=
-cloud.google.com/go/redis v1.11.0/go.mod h1:/X6eicana+BWcUda5PpwZC48o37SiFVTFSs0fWAJ7uQ=
-cloud.google.com/go/redis v1.13.1/go.mod h1:VP7DGLpE91M6bcsDdMuyCm2hIpB6Vp2hI090Mfd1tcg=
-cloud.google.com/go/resourcemanager v1.3.0/go.mod h1:bAtrTjZQFJkiWTPDb1WBjzvc6/kifjj4QBYuKCCoqKA=
-cloud.google.com/go/resourcemanager v1.4.0/go.mod h1:MwxuzkumyTX7/a3n37gmsT3py7LIXwrShilPh3P1tR0=
-cloud.google.com/go/resourcemanager v1.5.0/go.mod h1:eQoXNAiAvCf5PXxWxXjhKQoTMaUSNrEfg+6qdf/wots=
-cloud.google.com/go/resourcemanager v1.6.0/go.mod h1:YcpXGRs8fDzcUl1Xw8uOVmI8JEadvhRIkoXXUNVYcVo=
-cloud.google.com/go/resourcemanager v1.7.0/go.mod h1:HlD3m6+bwhzj9XCouqmeiGuni95NTrExfhoSrkC/3EI=
-cloud.google.com/go/resourcemanager v1.9.1/go.mod h1:dVCuosgrh1tINZ/RwBufr8lULmWGOkPS8gL5gqyjdT8=
-cloud.google.com/go/resourcesettings v1.3.0/go.mod h1:lzew8VfESA5DQ8gdlHwMrqZs1S9V87v3oCnKCWoOuQU=
-cloud.google.com/go/resourcesettings v1.4.0/go.mod h1:ldiH9IJpcrlC3VSuCGvjR5of/ezRrOxFtpJoJo5SmXg=
-cloud.google.com/go/resourcesettings v1.5.0/go.mod h1:+xJF7QSG6undsQDfsCJyqWXyBwUoJLhetkRMDRnIoXA=
-cloud.google.com/go/resourcesettings v1.6.1/go.mod h1:M7mk9PIZrC5Fgsu1kZJci6mpgN8o0IUzVx3eJU3y4Jw=
-cloud.google.com/go/retail v1.8.0/go.mod h1:QblKS8waDmNUhghY2TI9O3JLlFk8jybHeV4BF19FrE4=
-cloud.google.com/go/retail v1.9.0/go.mod h1:g6jb6mKuCS1QKnH/dpu7isX253absFl6iE92nHwlBUY=
-cloud.google.com/go/retail v1.10.0/go.mod h1:2gDk9HsL4HMS4oZwz6daui2/jmKvqShXKQuB2RZ+cCc=
-cloud.google.com/go/retail v1.11.0/go.mod h1:MBLk1NaWPmh6iVFSz9MeKG/Psyd7TAgm6y/9L2B4x9Y=
-cloud.google.com/go/retail v1.12.0/go.mod h1:UMkelN/0Z8XvKymXFbD4EhFJlYKRx1FGhQkVPU5kF14=
-cloud.google.com/go/retail v1.14.1/go.mod h1:y3Wv3Vr2k54dLNIrCzenyKG8g8dhvhncT2NcNjb/6gE=
-cloud.google.com/go/run v0.2.0/go.mod h1:CNtKsTA1sDcnqqIFR3Pb5Tq0usWxJJvsWOCPldRU3Do=
-cloud.google.com/go/run v0.3.0/go.mod h1:TuyY1+taHxTjrD0ZFk2iAR+xyOXEA0ztb7U3UNA0zBo=
-cloud.google.com/go/run v0.8.0/go.mod h1:VniEnuBwqjigv0A7ONfQUaEItaiCRVujlMqerPPiktM=
-cloud.google.com/go/run v0.9.0/go.mod h1:Wwu+/vvg8Y+JUApMwEDfVfhetv30hCG4ZwDR/IXl2Qg=
-cloud.google.com/go/scheduler v1.4.0/go.mod h1:drcJBmxF3aqZJRhmkHQ9b3uSSpQoltBPGPxGAWROx6s=
-cloud.google.com/go/scheduler v1.5.0/go.mod h1:ri073ym49NW3AfT6DZi21vLZrG07GXr5p3H1KxN5QlI=
-cloud.google.com/go/scheduler v1.6.0/go.mod h1:SgeKVM7MIwPn3BqtcBntpLyrIJftQISRrYB5ZtT+KOk=
-cloud.google.com/go/scheduler v1.7.0/go.mod h1:jyCiBqWW956uBjjPMMuX09n3x37mtyPJegEWKxRsn44=
-cloud.google.com/go/scheduler v1.8.0/go.mod h1:TCET+Y5Gp1YgHT8py4nlg2Sew8nUHMqcpousDgXJVQc=
-cloud.google.com/go/scheduler v1.9.0/go.mod h1:yexg5t+KSmqu+njTIh3b7oYPheFtBWGcbVUYF1GGMIc=
-cloud.google.com/go/scheduler v1.10.1/go.mod h1:R63Ldltd47Bs4gnhQkmNDse5w8gBRrhObZ54PxgR2Oo=
-cloud.google.com/go/secretmanager v1.6.0/go.mod h1:awVa/OXF6IiyaU1wQ34inzQNc4ISIDIrId8qE5QGgKA=
-cloud.google.com/go/secretmanager v1.8.0/go.mod h1:hnVgi/bN5MYHd3Gt0SPuTPPp5ENina1/LxM+2W9U9J4=
-cloud.google.com/go/secretmanager v1.9.0/go.mod h1:b71qH2l1yHmWQHt9LC80akm86mX8AL6X1MA01dW8ht4=
-cloud.google.com/go/secretmanager v1.10.0/go.mod h1:MfnrdvKMPNra9aZtQFvBcvRU54hbPD8/HayQdlUgJpU=
-cloud.google.com/go/secretmanager v1.11.1/go.mod h1:znq9JlXgTNdBeQk9TBW/FnR/W4uChEKGeqQWAJ8SXFw=
-cloud.google.com/go/security v1.5.0/go.mod h1:lgxGdyOKKjHL4YG3/YwIL2zLqMFCKs0UbQwgyZmfJl4=
-cloud.google.com/go/security v1.7.0/go.mod h1:mZklORHl6Bg7CNnnjLH//0UlAlaXqiG7Lb9PsPXLfD0=
-cloud.google.com/go/security v1.8.0/go.mod h1:hAQOwgmaHhztFhiQ41CjDODdWP0+AE1B3sX4OFlq+GU=
-cloud.google.com/go/security v1.9.0/go.mod h1:6Ta1bO8LXI89nZnmnsZGp9lVoVWXqsVbIq/t9dzI+2Q=
-cloud.google.com/go/security v1.10.0/go.mod h1:QtOMZByJVlibUT2h9afNDWRZ1G96gVywH8T5GUSb9IA=
-cloud.google.com/go/security v1.12.0/go.mod h1:rV6EhrpbNHrrxqlvW0BWAIawFWq3X90SduMJdFwtLB8=
-cloud.google.com/go/security v1.13.0/go.mod h1:Q1Nvxl1PAgmeW0y3HTt54JYIvUdtcpYKVfIB8AOMZ+0=
-cloud.google.com/go/security v1.15.1/go.mod h1:MvTnnbsWnehoizHi09zoiZob0iCHVcL4AUBj76h9fXA=
-cloud.google.com/go/securitycenter v1.13.0/go.mod h1:cv5qNAqjY84FCN6Y9z28WlkKXyWsgLO832YiWwkCWcU=
-cloud.google.com/go/securitycenter v1.14.0/go.mod h1:gZLAhtyKv85n52XYWt6RmeBdydyxfPeTrpToDPw4Auc=
-cloud.google.com/go/securitycenter v1.15.0/go.mod h1:PeKJ0t8MoFmmXLXWm41JidyzI3PJjd8sXWaVqg43WWk=
-cloud.google.com/go/securitycenter v1.16.0/go.mod h1:Q9GMaLQFUD+5ZTabrbujNWLtSLZIZF7SAR0wWECrjdk=
-cloud.google.com/go/securitycenter v1.18.1/go.mod h1:0/25gAzCM/9OL9vVx4ChPeM/+DlfGQJDwBy/UC8AKK0=
-cloud.google.com/go/securitycenter v1.19.0/go.mod h1:LVLmSg8ZkkyaNy4u7HCIshAngSQ8EcIRREP3xBnyfag=
-cloud.google.com/go/securitycenter v1.23.0/go.mod h1:8pwQ4n+Y9WCWM278R8W3nF65QtY172h4S8aXyI9/hsQ=
-cloud.google.com/go/servicecontrol v1.4.0/go.mod h1:o0hUSJ1TXJAmi/7fLJAedOovnujSEvjKCAFNXPQ1RaU=
-cloud.google.com/go/servicecontrol v1.5.0/go.mod h1:qM0CnXHhyqKVuiZnGKrIurvVImCs8gmqWsDoqe9sU1s=
-cloud.google.com/go/servicecontrol v1.10.0/go.mod h1:pQvyvSRh7YzUF2efw7H87V92mxU8FnFDawMClGCNuAA=
-cloud.google.com/go/servicecontrol v1.11.0/go.mod h1:kFmTzYzTUIuZs0ycVqRHNaNhgR+UMUpw9n02l/pY+mc=
-cloud.google.com/go/servicecontrol v1.11.1/go.mod h1:aSnNNlwEFBY+PWGQ2DoM0JJ/QUXqV5/ZD9DOLB7SnUk=
-cloud.google.com/go/servicedirectory v1.4.0/go.mod h1:gH1MUaZCgtP7qQiI+F+A+OpeKF/HQWgtAddhTbhL2bs=
-cloud.google.com/go/servicedirectory v1.5.0/go.mod h1:QMKFL0NUySbpZJ1UZs3oFAmdvVxhhxB6eJ/Vlp73dfg=
-cloud.google.com/go/servicedirectory v1.6.0/go.mod h1:pUlbnWsLH9c13yGkxCmfumWEPjsRs1RlmJ4pqiNjVL4=
-cloud.google.com/go/servicedirectory v1.7.0/go.mod h1:5p/U5oyvgYGYejufvxhgwjL8UVXjkuw7q5XcG10wx1U=
-cloud.google.com/go/servicedirectory v1.8.0/go.mod h1:srXodfhY1GFIPvltunswqXpVxFPpZjf8nkKQT7XcXaY=
-cloud.google.com/go/servicedirectory v1.9.0/go.mod h1:29je5JjiygNYlmsGz8k6o+OZ8vd4f//bQLtvzkPPT/s=
-cloud.google.com/go/servicedirectory v1.10.1/go.mod h1:Xv0YVH8s4pVOwfM/1eMTl0XJ6bzIOSLDt8f8eLaGOxQ=
-cloud.google.com/go/servicemanagement v1.4.0/go.mod h1:d8t8MDbezI7Z2R1O/wu8oTggo3BI2GKYbdG4y/SJTco=
-cloud.google.com/go/servicemanagement v1.5.0/go.mod h1:XGaCRe57kfqu4+lRxaFEAuqmjzF0r+gWHjWqKqBvKFo=
-cloud.google.com/go/servicemanagement v1.6.0/go.mod h1:aWns7EeeCOtGEX4OvZUWCCJONRZeFKiptqKf1D0l/Jc=
-cloud.google.com/go/servicemanagement v1.8.0/go.mod h1:MSS2TDlIEQD/fzsSGfCdJItQveu9NXnUniTrq/L8LK4=
-cloud.google.com/go/serviceusage v1.3.0/go.mod h1:Hya1cozXM4SeSKTAgGXgj97GlqUvF5JaoXacR1JTP/E=
-cloud.google.com/go/serviceusage v1.4.0/go.mod h1:SB4yxXSaYVuUBYUml6qklyONXNLt83U0Rb+CXyhjEeU=
-cloud.google.com/go/serviceusage v1.5.0/go.mod h1:w8U1JvqUqwJNPEOTQjrMHkw3IaIFLoLsPLvsE3xueec=
-cloud.google.com/go/serviceusage v1.6.0/go.mod h1:R5wwQcbOWsyuOfbP9tGdAnCAc6B9DRwPG1xtWMDeuPA=
-cloud.google.com/go/shell v1.3.0/go.mod h1:VZ9HmRjZBsjLGXusm7K5Q5lzzByZmJHf1d0IWHEN5X4=
-cloud.google.com/go/shell v1.4.0/go.mod h1:HDxPzZf3GkDdhExzD/gs8Grqk+dmYcEjGShZgYa9URw=
-cloud.google.com/go/shell v1.6.0/go.mod h1:oHO8QACS90luWgxP3N9iZVuEiSF84zNyLytb+qE2f9A=
-cloud.google.com/go/shell v1.7.1/go.mod h1:u1RaM+huXFaTojTbW4g9P5emOrrmLE69KrxqQahKn4g=
-cloud.google.com/go/spanner v1.41.0/go.mod h1:MLYDBJR/dY4Wt7ZaMIQ7rXOTLjYrmxLE/5ve9vFfWos=
-cloud.google.com/go/spanner v1.44.0/go.mod h1:G8XIgYdOK+Fbcpbs7p2fiprDw4CaZX63whnSMLVBxjk=
-cloud.google.com/go/spanner v1.45.0/go.mod h1:FIws5LowYz8YAE1J8fOS7DJup8ff7xJeetWEo5REA2M=
-cloud.google.com/go/spanner v1.47.0/go.mod h1:IXsJwVW2j4UKs0eYDqodab6HgGuA1bViSqW4uH9lfUI=
-cloud.google.com/go/speech v1.6.0/go.mod h1:79tcr4FHCimOp56lwC01xnt/WPJZc4v3gzyT7FoBkCM=
-cloud.google.com/go/speech v1.7.0/go.mod h1:KptqL+BAQIhMsj1kOP2la5DSEEerPDuOP/2mmkhHhZQ=
-cloud.google.com/go/speech v1.8.0/go.mod h1:9bYIl1/tjsAnMgKGHKmBZzXKEkGgtU+MpdDPTE9f7y0=
-cloud.google.com/go/speech v1.9.0/go.mod h1:xQ0jTcmnRFFM2RfX/U+rk6FQNUF6DQlydUSyoooSpco=
-cloud.google.com/go/speech v1.14.1/go.mod h1:gEosVRPJ9waG7zqqnsHpYTOoAS4KouMRLDFMekpJ0J0=
-cloud.google.com/go/speech v1.15.0/go.mod h1:y6oH7GhqCaZANH7+Oe0BhgIogsNInLlz542tg3VqeYI=
-cloud.google.com/go/speech v1.17.1/go.mod h1:8rVNzU43tQvxDaGvqOhpDqgkJTFowBpDvCJ14kGlJYo=
-cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
-cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
-cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
-cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
-cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
-cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
-cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y=
-cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc=
-cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s=
-cloud.google.com/go/storage v1.28.1/go.mod h1:Qnisd4CqDdo6BGs2AD5LLnEsmSQ80wQ5ogcBBKhU86Y=
-cloud.google.com/go/storage v1.29.0/go.mod h1:4puEjyTKnku6gfKoTfNOU/W+a9JyuVNxjpS5GBrB8h4=
-cloud.google.com/go/storage v1.30.1/go.mod h1:NfxhC0UJE1aXSx7CIIbCf7y9HKT7BiccwkR7+P7gN8E=
-cloud.google.com/go/storage v1.31.0 h1:+S3LjjEN2zZ+L5hOwj4+1OkGCsLVe0NzpXKQ1pSdTCI=
-cloud.google.com/go/storage v1.31.0/go.mod h1:81ams1PrhW16L4kF7qg+4mTq7SRs5HsbDTM0bWvrwJ0=
-cloud.google.com/go/storagetransfer v1.5.0/go.mod h1:dxNzUopWy7RQevYFHewchb29POFv3/AaBgnhqzqiK0w=
-cloud.google.com/go/storagetransfer v1.6.0/go.mod h1:y77xm4CQV/ZhFZH75PLEXY0ROiS7Gh6pSKrM8dJyg6I=
-cloud.google.com/go/storagetransfer v1.7.0/go.mod h1:8Giuj1QNb1kfLAiWM1bN6dHzfdlDAVC9rv9abHot2W4=
-cloud.google.com/go/storagetransfer v1.8.0/go.mod h1:JpegsHHU1eXg7lMHkvf+KE5XDJ7EQu0GwNJbbVGanEw=
-cloud.google.com/go/storagetransfer v1.10.0/go.mod h1:DM4sTlSmGiNczmV6iZyceIh2dbs+7z2Ayg6YAiQlYfA=
-cloud.google.com/go/talent v1.1.0/go.mod h1:Vl4pt9jiHKvOgF9KoZo6Kob9oV4lwd/ZD5Cto54zDRw=
-cloud.google.com/go/talent v1.2.0/go.mod h1:MoNF9bhFQbiJ6eFD3uSsg0uBALw4n4gaCaEjBw9zo8g=
-cloud.google.com/go/talent v1.3.0/go.mod h1:CmcxwJ/PKfRgd1pBjQgU6W3YBwiewmUzQYH5HHmSCmM=
-cloud.google.com/go/talent v1.4.0/go.mod h1:ezFtAgVuRf8jRsvyE6EwmbTK5LKciD4KVnHuDEFmOOA=
-cloud.google.com/go/talent v1.5.0/go.mod h1:G+ODMj9bsasAEJkQSzO2uHQWXHHXUomArjWQQYkqK6c=
-cloud.google.com/go/talent v1.6.2/go.mod h1:CbGvmKCG61mkdjcqTcLOkb2ZN1SrQI8MDyma2l7VD24=
-cloud.google.com/go/texttospeech v1.4.0/go.mod h1:FX8HQHA6sEpJ7rCMSfXuzBcysDAuWusNNNvN9FELDd8=
-cloud.google.com/go/texttospeech v1.5.0/go.mod h1:oKPLhR4n4ZdQqWKURdwxMy0uiTS1xU161C8W57Wkea4=
-cloud.google.com/go/texttospeech v1.6.0/go.mod h1:YmwmFT8pj1aBblQOI3TfKmwibnsfvhIBzPXcW4EBovc=
-cloud.google.com/go/texttospeech v1.7.1/go.mod h1:m7QfG5IXxeneGqTapXNxv2ItxP/FS0hCZBwXYqucgSk=
-cloud.google.com/go/tpu v1.3.0/go.mod h1:aJIManG0o20tfDQlRIej44FcwGGl/cD0oiRyMKG19IQ=
-cloud.google.com/go/tpu v1.4.0/go.mod h1:mjZaX8p0VBgllCzF6wcU2ovUXN9TONFLd7iz227X2Xg=
-cloud.google.com/go/tpu v1.5.0/go.mod h1:8zVo1rYDFuW2l4yZVY0R0fb/v44xLh3llq7RuV61fPM=
-cloud.google.com/go/tpu v1.6.1/go.mod h1:sOdcHVIgDEEOKuqUoi6Fq53MKHJAtOwtz0GuKsWSH3E=
-cloud.google.com/go/trace v1.0.0/go.mod h1:4iErSByzxkyHWzzlAj63/Gmjz0NH1ASqhJguHpGcr6A=
-cloud.google.com/go/trace v1.3.0/go.mod h1:FFUE83d9Ca57C+K8rDl/Ih8LwOzWIV1krKgxg6N0G28=
-cloud.google.com/go/trace v1.4.0/go.mod h1:UG0v8UBqzusp+z63o7FK74SdFE+AXpCLdFb1rshXG+Y=
-cloud.google.com/go/trace v1.8.0/go.mod h1:zH7vcsbAhklH8hWFig58HvxcxyQbaIqMarMg9hn5ECA=
-cloud.google.com/go/trace v1.9.0/go.mod h1:lOQqpE5IaWY0Ixg7/r2SjixMuc6lfTFeO4QGM4dQWOk=
-cloud.google.com/go/trace v1.10.1 h1:EwGdOLCNfYOOPtgqo+D2sDLZmRCEO1AagRTJCU6ztdg=
-cloud.google.com/go/trace v1.10.1/go.mod h1:gbtL94KE5AJLH3y+WVpfWILmqgc6dXcqgNXdOPAQTYk=
-cloud.google.com/go/translate v1.3.0/go.mod h1:gzMUwRjvOqj5i69y/LYLd8RrNQk+hOmIXTi9+nb3Djs=
-cloud.google.com/go/translate v1.4.0/go.mod h1:06Dn/ppvLD6WvA5Rhdp029IX2Mi3Mn7fpMRLPvXT5Wg=
-cloud.google.com/go/translate v1.5.0/go.mod h1:29YDSYveqqpA1CQFD7NQuP49xymq17RXNaUDdc0mNu0=
-cloud.google.com/go/translate v1.6.0/go.mod h1:lMGRudH1pu7I3n3PETiOB2507gf3HnfLV8qlkHZEyos=
-cloud.google.com/go/translate v1.7.0/go.mod h1:lMGRudH1pu7I3n3PETiOB2507gf3HnfLV8qlkHZEyos=
-cloud.google.com/go/translate v1.8.1/go.mod h1:d1ZH5aaOA0CNhWeXeC8ujd4tdCFw8XoNWRljklu5RHs=
-cloud.google.com/go/video v1.8.0/go.mod h1:sTzKFc0bUSByE8Yoh8X0mn8bMymItVGPfTuUBUyRgxk=
-cloud.google.com/go/video v1.9.0/go.mod h1:0RhNKFRF5v92f8dQt0yhaHrEuH95m068JYOvLZYnJSw=
-cloud.google.com/go/video v1.12.0/go.mod h1:MLQew95eTuaNDEGriQdcYn0dTwf9oWiA4uYebxM5kdg=
-cloud.google.com/go/video v1.13.0/go.mod h1:ulzkYlYgCp15N2AokzKjy7MQ9ejuynOJdf1tR5lGthk=
-cloud.google.com/go/video v1.14.0/go.mod h1:SkgaXwT+lIIAKqWAJfktHT/RbgjSuY6DobxEp0C5yTQ=
-cloud.google.com/go/video v1.15.0/go.mod h1:SkgaXwT+lIIAKqWAJfktHT/RbgjSuY6DobxEp0C5yTQ=
-cloud.google.com/go/video v1.17.1/go.mod h1:9qmqPqw/Ib2tLqaeHgtakU+l5TcJxCJbhFXM7UJjVzU=
-cloud.google.com/go/videointelligence v1.6.0/go.mod h1:w0DIDlVRKtwPCn/C4iwZIJdvC69yInhW0cfi+p546uU=
-cloud.google.com/go/videointelligence v1.7.0/go.mod h1:k8pI/1wAhjznARtVT9U1llUaFNPh7muw8QyOUpavru4=
-cloud.google.com/go/videointelligence v1.8.0/go.mod h1:dIcCn4gVDdS7yte/w+koiXn5dWVplOZkE+xwG9FgK+M=
-cloud.google.com/go/videointelligence v1.9.0/go.mod h1:29lVRMPDYHikk3v8EdPSaL8Ku+eMzDljjuvRs105XoU=
-cloud.google.com/go/videointelligence v1.10.0/go.mod h1:LHZngX1liVtUhZvi2uNS0VQuOzNi2TkY1OakiuoUOjU=
-cloud.google.com/go/videointelligence v1.11.1/go.mod h1:76xn/8InyQHarjTWsBR058SmlPCwQjgcvoW0aZykOvo=
-cloud.google.com/go/vision v1.2.0/go.mod h1:SmNwgObm5DpFBme2xpyOyasvBc1aPdjvMk2bBk0tKD0=
-cloud.google.com/go/vision/v2 v2.2.0/go.mod h1:uCdV4PpN1S0jyCyq8sIM42v2Y6zOLkZs+4R9LrGYwFo=
-cloud.google.com/go/vision/v2 v2.3.0/go.mod h1:UO61abBx9QRMFkNBbf1D8B1LXdS2cGiiCRx0vSpZoUo=
-cloud.google.com/go/vision/v2 v2.4.0/go.mod h1:VtI579ll9RpVTrdKdkMzckdnwMyX2JILb+MhPqRbPsY=
-cloud.google.com/go/vision/v2 v2.5.0/go.mod h1:MmaezXOOE+IWa+cS7OhRRLK2cNv1ZL98zhqFFZaaH2E=
-cloud.google.com/go/vision/v2 v2.6.0/go.mod h1:158Hes0MvOS9Z/bDMSFpjwsUrZ5fPrdwuyyvKSGAGMY=
-cloud.google.com/go/vision/v2 v2.7.0/go.mod h1:H89VysHy21avemp6xcf9b9JvZHVehWbET0uT/bcuY/0=
-cloud.google.com/go/vision/v2 v2.7.2/go.mod h1:jKa8oSYBWhYiXarHPvP4USxYANYUEdEsQrloLjrSwJU=
-cloud.google.com/go/vmmigration v1.2.0/go.mod h1:IRf0o7myyWFSmVR1ItrBSFLFD/rJkfDCUTO4vLlJvsE=
-cloud.google.com/go/vmmigration v1.3.0/go.mod h1:oGJ6ZgGPQOFdjHuocGcLqX4lc98YQ7Ygq8YQwHh9A7g=
-cloud.google.com/go/vmmigration v1.5.0/go.mod h1:E4YQ8q7/4W9gobHjQg4JJSgXXSgY21nA5r8swQV+Xxc=
-cloud.google.com/go/vmmigration v1.6.0/go.mod h1:bopQ/g4z+8qXzichC7GW1w2MjbErL54rk3/C843CjfY=
-cloud.google.com/go/vmmigration v1.7.1/go.mod h1:WD+5z7a/IpZ5bKK//YmT9E047AD+rjycCAvyMxGJbro=
-cloud.google.com/go/vmwareengine v0.1.0/go.mod h1:RsdNEf/8UDvKllXhMz5J40XxDrNJNN4sagiox+OI208=
-cloud.google.com/go/vmwareengine v0.2.2/go.mod h1:sKdctNJxb3KLZkE/6Oui94iw/xs9PRNC2wnNLXsHvH8=
-cloud.google.com/go/vmwareengine v0.3.0/go.mod h1:wvoyMvNWdIzxMYSpH/R7y2h5h3WFkx6d+1TIsP39WGY=
-cloud.google.com/go/vmwareengine v0.4.1/go.mod h1:Px64x+BvjPZwWuc4HdmVhoygcXqEkGHXoa7uyfTgSI0=
-cloud.google.com/go/vpcaccess v1.4.0/go.mod h1:aQHVbTWDYUR1EbTApSVvMq1EnT57ppDmQzZ3imqIk4w=
-cloud.google.com/go/vpcaccess v1.5.0/go.mod h1:drmg4HLk9NkZpGfCmZ3Tz0Bwnm2+DKqViEpeEpOq0m8=
-cloud.google.com/go/vpcaccess v1.6.0/go.mod h1:wX2ILaNhe7TlVa4vC5xce1bCnqE3AeH27RV31lnmZes=
-cloud.google.com/go/vpcaccess v1.7.1/go.mod h1:FogoD46/ZU+JUBX9D606X21EnxiszYi2tArQwLY4SXs=
-cloud.google.com/go/webrisk v1.4.0/go.mod h1:Hn8X6Zr+ziE2aNd8SliSDWpEnSS1u4R9+xXZmFiHmGE=
-cloud.google.com/go/webrisk v1.5.0/go.mod h1:iPG6fr52Tv7sGk0H6qUFzmL3HHZev1htXuWDEEsqMTg=
-cloud.google.com/go/webrisk v1.6.0/go.mod h1:65sW9V9rOosnc9ZY7A7jsy1zoHS5W9IAXv6dGqhMQMc=
-cloud.google.com/go/webrisk v1.7.0/go.mod h1:mVMHgEYH0r337nmt1JyLthzMr6YxwN1aAIEc2fTcq7A=
-cloud.google.com/go/webrisk v1.8.0/go.mod h1:oJPDuamzHXgUc+b8SiHRcVInZQuybnvEW72PqTc7sSg=
-cloud.google.com/go/webrisk v1.9.1/go.mod h1:4GCmXKcOa2BZcZPn6DCEvE7HypmEJcJkr4mtM+sqYPc=
-cloud.google.com/go/websecurityscanner v1.3.0/go.mod h1:uImdKm2wyeXQevQJXeh8Uun/Ym1VqworNDlBXQevGMo=
-cloud.google.com/go/websecurityscanner v1.4.0/go.mod h1:ebit/Fp0a+FWu5j4JOmJEV8S8CzdTkAS77oDsiSqYWQ=
-cloud.google.com/go/websecurityscanner v1.5.0/go.mod h1:Y6xdCPy81yi0SQnDY1xdNTNpfY1oAgXUlcfN3B3eSng=
-cloud.google.com/go/websecurityscanner v1.6.1/go.mod h1:Njgaw3rttgRHXzwCB8kgCYqv5/rGpFCsBOvPbYgszpg=
-cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0=
-cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M=
-cloud.google.com/go/workflows v1.8.0/go.mod h1:ysGhmEajwZxGn1OhGOGKsTXc5PyxOc0vfKf5Af+to4M=
-cloud.google.com/go/workflows v1.9.0/go.mod h1:ZGkj1aFIOd9c8Gerkjjq7OW7I5+l6cSvT3ujaO/WwSA=
-cloud.google.com/go/workflows v1.10.0/go.mod h1:fZ8LmRmZQWacon9UCX1r/g/DfAXx5VcPALq2CxzdePw=
-cloud.google.com/go/workflows v1.11.1/go.mod h1:Z+t10G1wF7h8LgdY/EmRcQY8ptBD/nvofaL6FqlET6g=
-contrib.go.opencensus.io/exporter/stackdriver v0.13.14 h1:zBakwHardp9Jcb8sQHcHpXy/0+JIb1M8KjigCJzx7+4=
-contrib.go.opencensus.io/exporter/stackdriver v0.13.14/go.mod h1:5pSSGY0Bhuk7waTHuDf4aQ8D2DrhgETRo9fy6k3Xlzc=
-dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
-gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8=
-git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc=
-github.com/AdaLogics/go-fuzz-headers v0.0.0-20210715213245-6c3934b029d8/go.mod h1:CzsSbkDixRphAF5hS6wbMKq0eI6ccJRb7/A0M6JBnwg=
-github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
-github.com/Azure/azure-sdk-for-go v63.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
-github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
-github.com/Azure/go-ansiterm v0.0.0-20210608223527-2377c96fe795/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
-github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
-github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
-github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
-github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw=
-github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA=
-github.com/Azure/go-autorest/autorest v0.11.25/go.mod h1:7l8ybrIdUmGqZMTD0sRtAr8NvbHjfofbf8RSP2q7w7U=
-github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg=
-github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A=
-github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M=
-github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ=
-github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=
-github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
-github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
-github.com/Azure/go-autorest/autorest/mocks v0.4.2/go.mod h1:Vy7OitM9Kei0i1Oj+LvyAWMXJHeKH1MVlzFugfVrmyU=
-github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE=
-github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E=
-github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
-github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
-github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
-github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
-github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
-github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
-github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk=
-github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
-github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
-github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
-github.com/Microsoft/go-winio v0.4.16-0.20201130162521-d1ffc52c7331/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
-github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
-github.com/Microsoft/go-winio v0.4.17-0.20210211115548-6eac466e5fa3/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
-github.com/Microsoft/go-winio v0.4.17-0.20210324224401-5516f17a5958/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
-github.com/Microsoft/go-winio v0.4.17/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
-github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
-github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
-github.com/Microsoft/hcsshim v0.8.7-0.20190325164909-8abdbb8205e4/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
-github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ=
-github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg38RRsjT5y8=
-github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg=
-github.com/Microsoft/hcsshim v0.8.15/go.mod h1:x38A4YbHbdxJtc0sF6oIz+RG0npwSCAvn69iY6URG00=
-github.com/Microsoft/hcsshim v0.8.16/go.mod h1:o5/SZqmR7x9JNKsW3pu+nqHm0MF8vbA+VxGOoXdC600=
-github.com/Microsoft/hcsshim v0.8.20/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4=
-github.com/Microsoft/hcsshim v0.8.21/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4=
-github.com/Microsoft/hcsshim v0.8.23/go.mod h1:4zegtUJth7lAvFyc6cH2gGQ5B3OFQim01nnU2M8jKDg=
-github.com/Microsoft/hcsshim v0.9.2/go.mod h1:7pLA8lDk46WKDWlVsENo92gC0XFa8rbKfyFRBqxEbCc=
-github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU=
-github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY=
-github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
-github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
-github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
-github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
-github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
-github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
-github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
-github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=
-github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY=
-github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk=
-github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
-github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b/go.mod h1:1KcenG0jGWcpt8ov532z81sp/kMMUG485J2InIOyADM=
-github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
-github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
-github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
-github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
-github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
-github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=
-github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0=
-github.com/alexflint/go-filemutex v1.1.0/go.mod h1:7P4iRhttt/nUvUOrYIhcpMzv2G6CY9UnI16Z+UJqRyk=
-github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
-github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
-github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
-github.com/apache/arrow/go/v10 v10.0.1/go.mod h1:YvhnlEePVnBS4+0z3fhPfUy7W1Ikj0Ih0vcRo/gZ1M0=
-github.com/apache/arrow/go/v11 v11.0.0/go.mod h1:Eg5OsL5H+e299f7u5ssuXsuHQVEGC4xei5aX110hRiI=
-github.com/apache/arrow/go/v12 v12.0.0/go.mod h1:d+tV/eHZZ7Dz7RPrFKtPK02tpr+c9/PEd/zm8mDS9Vg=
-github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU=
-github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
-github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
-github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
-github.com/armon/go-metrics v0.3.3/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc=
-github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
-github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
-github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
-github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
-github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
-github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0=
-github.com/aws/aws-sdk-go v1.38.35/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
-github.com/aws/aws-sdk-go v1.43.11/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
-github.com/aws/aws-sdk-go v1.43.31/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
-github.com/aws/aws-sdk-go v1.44.317 h1:+8XWrLmGMwPPXSRSLPzhgcGnzJ2mYkgkrcB9C/GnSOU=
-github.com/aws/aws-sdk-go v1.44.317/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
-github.com/bazelbuild/remote-apis v0.0.0-20210718193713-0ecef08215cf h1:DjbO/OLNTvELsPJRy5qU/aIsozQxBQVek+vTO49ybus=
-github.com/bazelbuild/remote-apis v0.0.0-20210718193713-0ecef08215cf/go.mod h1:ry8Y6CkQqCVcYsjPOlLXDX2iRVjOnjogdNwhvHmRcz8=
-github.com/bazelbuild/remote-apis-sdks v0.0.0-20220429154201-6c8489803a6f h1:ABNvjq+c2YgovfGJ2DxASBXGyb+uhXNunvOyjvtSGAQ=
-github.com/bazelbuild/remote-apis-sdks v0.0.0-20220429154201-6c8489803a6f/go.mod h1:p6PH8Kyjfm/hhbwC8ymX8SarB7CQTUiW6J0T/zbEKj8=
-github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM=
-github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
-github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
-github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
-github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
-github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
-github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
-github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
-github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
-github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
-github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
-github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
-github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
-github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
-github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
-github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
-github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
-github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
-github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk=
-github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
-github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
-github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8=
-github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50=
-github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
-github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
-github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
-github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
-github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
-github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g=
-github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw=
-github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
-github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
-github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
-github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
-github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
-github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
-github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw=
-github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M=
-github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E=
-github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
-github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY=
-github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
-github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic=
-github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
-github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
-github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg=
-github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775/go.mod h1:7cR51M8ViRLIdUjrmSXlK9pkrsDlLHbO8jiB8X8JnOc=
-github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs=
-github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs=
-github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs=
-github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA=
-github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
-github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
-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/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
-github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
-github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
-github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
-github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
-github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
-github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
-github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
-github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
-github.com/cncf/xds/go v0.0.0-20220314180256-7f1daf1720fc/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
-github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
-github.com/cncf/xds/go v0.0.0-20230310173818-32f1caf87195/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
-github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
-github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
-github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo=
-github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA=
-github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI=
-github.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE=
-github.com/containerd/aufs v0.0.0-20201003224125-76a6863f2989/go.mod h1:AkGGQs9NM2vtYHaUen+NljV0/baGCAPELGm2q9ZXpWU=
-github.com/containerd/aufs v0.0.0-20210316121734-20793ff83c97/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU=
-github.com/containerd/aufs v1.0.0/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU=
-github.com/containerd/btrfs v0.0.0-20201111183144-404b9149801e/go.mod h1:jg2QkJcsabfHugurUvvPhS3E08Oxiuh5W/g1ybB4e0E=
-github.com/containerd/btrfs v0.0.0-20210316141732-918d888fb676/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss=
-github.com/containerd/btrfs v1.0.0/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss=
-github.com/containerd/cgroups v0.0.0-20190717030353-c4b9ac5c7601/go.mod h1:X9rLEHIqSf/wfK8NsPqxJmeZgW4pcfzdXITDrUSJ6uI=
-github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko=
-github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1pT8KYB3TCXK/ocprsh7MAkoW8bZVzPdih9snmM=
-github.com/containerd/cgroups v0.0.0-20200710171044-318312a37340/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo=
-github.com/containerd/cgroups v0.0.0-20200824123100-0b889c03f102/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo=
-github.com/containerd/cgroups v0.0.0-20210114181951-8a68de567b68/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE=
-github.com/containerd/cgroups v1.0.1/go.mod h1:0SJrPIenamHDcZhEcJMNBB85rHcUsw4f25ZfBiPYRkU=
-github.com/containerd/cgroups v1.0.3/go.mod h1:/ofk34relqNjSGyqPrmEULrO4Sc8LJhvJmWbUCUKqj8=
-github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw=
-github.com/containerd/console v0.0.0-20181022165439-0650fd9eeb50/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw=
-github.com/containerd/console v0.0.0-20191206165004-02ecf6a7291e/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE=
-github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw=
-github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ=
-github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U=
-github.com/containerd/containerd v1.2.10/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
-github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
-github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
-github.com/containerd/containerd v1.3.1-0.20191213020239-082f7e3aed57/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
-github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
-github.com/containerd/containerd v1.4.0-beta.2.0.20200729163537-40b22ef07410/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
-github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
-github.com/containerd/containerd v1.4.3/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
-github.com/containerd/containerd v1.4.9/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
-github.com/containerd/containerd v1.5.0-beta.1/go.mod h1:5HfvG1V2FsKesEGQ17k5/T7V960Tmcumvqn8Mc+pCYQ=
-github.com/containerd/containerd v1.5.0-beta.3/go.mod h1:/wr9AVtEM7x9c+n0+stptlo/uBBoBORwEx6ardVcmKU=
-github.com/containerd/containerd v1.5.0-beta.4/go.mod h1:GmdgZd2zA2GYIBZ0w09ZvgqEq8EfBp/m3lcVZIvPHhI=
-github.com/containerd/containerd v1.5.0-rc.0/go.mod h1:V/IXoMqNGgBlabz3tHD2TWDoTJseu1FGOKuoA4nNb2s=
-github.com/containerd/containerd v1.5.1/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g=
-github.com/containerd/containerd v1.5.7/go.mod h1:gyvv6+ugqY25TiXxcZC3L5yOeYgEw0QMhscqVp1AR9c=
-github.com/containerd/containerd v1.5.8/go.mod h1:YdFSv5bTFLpG2HIYmfqDpSYYTDX+mc5qtSuYx1YUb/s=
-github.com/containerd/containerd v1.6.1/go.mod h1:1nJz5xCZPusx6jJU8Frfct988y0NpumIq9ODB0kLtoE=
-github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
-github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
-github.com/containerd/continuity v0.0.0-20191127005431-f65d91d395eb/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
-github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe/go.mod h1:cECdGN1O8G9bgKTlLhuPJimka6Xb/Gg7vYzCTNVxhvo=
-github.com/containerd/continuity v0.0.0-20201208142359-180525291bb7/go.mod h1:kR3BEg7bDFaEddKm54WSmrol1fKWDU1nKYkgrcgZT7Y=
-github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e/go.mod h1:EXlVlkqNba9rJe3j7w3Xa924itAMLgZH4UD/Q4PExuQ=
-github.com/containerd/continuity v0.1.0/go.mod h1:ICJu0PwR54nI0yPEnJ6jcS+J7CZAUXrLh8lPo2knzsM=
-github.com/containerd/continuity v0.2.2/go.mod h1:pWygW9u7LtS1o4N/Tn0FoCFDIXZ7rxcMX7HX1Dmibvk=
-github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI=
-github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI=
-github.com/containerd/fifo v0.0.0-20200410184934-f15a3290365b/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0=
-github.com/containerd/fifo v0.0.0-20201026212402-0724c46b320c/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0=
-github.com/containerd/fifo v0.0.0-20210316144830-115abcc95a1d/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4=
-github.com/containerd/fifo v1.0.0/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4=
-github.com/containerd/go-cni v1.0.1/go.mod h1:+vUpYxKvAF72G9i1WoDOiPGRtQpqsNW/ZHtSlv++smU=
-github.com/containerd/go-cni v1.0.2/go.mod h1:nrNABBHzu0ZwCug9Ije8hL2xBCYh/pjfMb1aZGrrohk=
-github.com/containerd/go-cni v1.1.0/go.mod h1:Rflh2EJ/++BA2/vY5ao3K6WJRR/bZKsX123aPk+kUtA=
-github.com/containerd/go-cni v1.1.3/go.mod h1:Rflh2EJ/++BA2/vY5ao3K6WJRR/bZKsX123aPk+kUtA=
-github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0=
-github.com/containerd/go-runc v0.0.0-20190911050354-e029b79d8cda/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0=
-github.com/containerd/go-runc v0.0.0-20200220073739-7016d3ce2328/go.mod h1:PpyHrqVs8FTi9vpyHwPwiNEGaACDxT/N/pLcvMSRA9g=
-github.com/containerd/go-runc v0.0.0-20201020171139-16b287bc67d0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok=
-github.com/containerd/go-runc v1.0.0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok=
-github.com/containerd/imgcrypt v1.0.1/go.mod h1:mdd8cEPW7TPgNG4FpuP3sGBiQ7Yi/zak9TYCG3juvb0=
-github.com/containerd/imgcrypt v1.0.4-0.20210301171431-0ae5c75f59ba/go.mod h1:6TNsg0ctmizkrOgXRNQjAPFWpMYRWuiB6dSF4Pfa5SA=
-github.com/containerd/imgcrypt v1.1.1-0.20210312161619-7ed62a527887/go.mod h1:5AZJNI6sLHJljKuI9IHnw1pWqo/F0nGDOuR9zgTs7ow=
-github.com/containerd/imgcrypt v1.1.1/go.mod h1:xpLnwiQmEUJPvQoAapeb2SNCxz7Xr6PJrXQb0Dpc4ms=
-github.com/containerd/imgcrypt v1.1.3/go.mod h1:/TPA1GIDXMzbj01yd8pIbQiLdQxed5ue1wb8bP7PQu4=
-github.com/containerd/nri v0.0.0-20201007170849-eb1350a75164/go.mod h1:+2wGSDGFYfE5+So4M5syatU0N0f0LbWpuqyMi4/BE8c=
-github.com/containerd/nri v0.0.0-20210316161719-dbaa18c31c14/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY=
-github.com/containerd/nri v0.1.0/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY=
-github.com/containerd/stargz-snapshotter/estargz v0.4.1/go.mod h1:x7Q9dg9QYb4+ELgxmo4gBUeJB0tl5dqH1Sdz0nJU1QM=
-github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o=
-github.com/containerd/ttrpc v0.0.0-20190828172938-92c8520ef9f8/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o=
-github.com/containerd/ttrpc v0.0.0-20191028202541-4f1b8fe65a5c/go.mod h1:LPm1u0xBw8r8NOKoOdNMeVHSawSsltak+Ihv+etqsE8=
-github.com/containerd/ttrpc v1.0.1/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y=
-github.com/containerd/ttrpc v1.0.2/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y=
-github.com/containerd/ttrpc v1.1.0/go.mod h1:XX4ZTnoOId4HklF4edwc4DcqskFZuvXB1Evzy5KFQpQ=
-github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc=
-github.com/containerd/typeurl v0.0.0-20190911142611-5eb25027c9fd/go.mod h1:GeKYzf2pQcqv7tJ0AoCuuhtnqhva5LNU3U+OyKxxJpk=
-github.com/containerd/typeurl v1.0.1/go.mod h1:TB1hUtrpaiO88KEK56ijojHS1+NeF0izUACaJW2mdXg=
-github.com/containerd/typeurl v1.0.2/go.mod h1:9trJWW2sRlGub4wZJRTW83VtbOLS6hwcDZXTn6oPz9s=
-github.com/containerd/zfs v0.0.0-20200918131355-0a33824f23a2/go.mod h1:8IgZOBdv8fAgXddBT4dBXJPtxyRsejFIpXoklgxgEjw=
-github.com/containerd/zfs v0.0.0-20210301145711-11e8f1707f62/go.mod h1:A9zfAbMlQwE+/is6hi0Xw8ktpL+6glmqZYtevJgaB8Y=
-github.com/containerd/zfs v0.0.0-20210315114300-dde8f0fda960/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY=
-github.com/containerd/zfs v0.0.0-20210324211415-d5c4544f0433/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY=
-github.com/containerd/zfs v1.0.0/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY=
-github.com/containernetworking/cni v0.7.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=
-github.com/containernetworking/cni v0.8.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=
-github.com/containernetworking/cni v0.8.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=
-github.com/containernetworking/cni v1.0.1/go.mod h1:AKuhXbN5EzmD4yTNtfSsX3tPcmtrBI6QcRV0NiNt15Y=
-github.com/containernetworking/plugins v0.8.6/go.mod h1:qnw5mN19D8fIwkqW7oHHYDHVlzhJpcY6TQxn/fUyDDM=
-github.com/containernetworking/plugins v0.9.1/go.mod h1:xP/idU2ldlzN6m4p5LmGiwRDjeJr6FLK6vuiUwoH7P8=
-github.com/containernetworking/plugins v1.0.1/go.mod h1:QHCfGpaTwYTbbH+nZXKVTxNBDZcxSOplJT5ico8/FLE=
-github.com/containers/ocicrypt v1.0.1/go.mod h1:MeJDzk1RJHv89LjsH0Sp5KTY3ZYkjXO/C+bKAeWFIrc=
-github.com/containers/ocicrypt v1.1.0/go.mod h1:b8AOe0YR67uU8OqfVNcznfFpAzu3rdgUV4GP9qXPfu4=
-github.com/containers/ocicrypt v1.1.1/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B0oB3dj3jFxY=
-github.com/containers/ocicrypt v1.1.2/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B0oB3dj3jFxY=
-github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
-github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
-github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
-github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU=
-github.com/coreos/go-iptables v0.5.0/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU=
-github.com/coreos/go-iptables v0.6.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q=
-github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
-github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
-github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
-github.com/coreos/go-systemd v0.0.0-20161114122254-48702e0da86b/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
-github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
-github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
-github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
-github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
-github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
-github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
-github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
-github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
-github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
-github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
-github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
-github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
-github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
-github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
-github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
-github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ=
-github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s=
-github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8=
-github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjIciD2oAxI7DmWRx6gbeqrkoLqv3MV0vzNad+I=
-github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
-github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/dennwc/varint v1.0.0/go.mod h1:hnItb35rvZvJrbTALZtY/iQfDs48JKRG1RPpgziApxA=
-github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0=
-github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
-github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
-github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
-github.com/dgryski/go-sip13 v0.0.0-20200911182023-62edffca9245/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
-github.com/digitalocean/godo v1.78.0/go.mod h1:GBmu8MkjZmNARE7IXRPmkbbnocNN8+uBm0xbEVw2LCs=
-github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
-github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
-github.com/docker/cli v0.0.0-20191017083524-a8ff7f821017/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
-github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY=
-github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
-github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
-github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
-github.com/docker/docker v20.10.14+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
-github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y=
-github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
-github.com/docker/go-events v0.0.0-20170721190031-9461782956ad/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=
-github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=
-github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI=
-github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw=
-github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
-github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE=
-github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
-github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
-github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
-github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
-github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
-github.com/edsrzf/mmap-go v1.1.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q=
-github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
-github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
-github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
-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/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
-github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
-github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
-github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
-github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
-github.com/envoyproxy/go-control-plane v0.10.1/go.mod h1:AY7fTTXNdv/aJ2O5jwpxAPOWUZ7hQAEvzN5Pf27BkQQ=
-github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
-github.com/envoyproxy/go-control-plane v0.10.3/go.mod h1:fJJn/j26vwOu972OllsvAgJJM//w9BV6Fxbg2LuVd34=
-github.com/envoyproxy/go-control-plane v0.11.0/go.mod h1:VnHyVMpzcLvCFt9yUz1UnCwHLhwx1WguiVDV7pTG/tI=
-github.com/envoyproxy/go-control-plane v0.11.1-0.20230524094728-9239064ad72f/go.mod h1:sfYdkwUW4BA3PbKjySwjJy+O4Pu0h62rlqCMHNk+K+Q=
-github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
-github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo=
-github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w=
-github.com/envoyproxy/protoc-gen-validate v0.10.0/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss=
-github.com/envoyproxy/protoc-gen-validate v0.10.1/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss=
-github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
-github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
-github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
-github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
-github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
-github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
-github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
-github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
-github.com/felixge/httpsnoop v1.0.2/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
-github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
-github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
-github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
-github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
-github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
-github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
-github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
-github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
-github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
-github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
-github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
-github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA=
-github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
-github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg=
-github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
-github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
-github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
-github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
-github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g=
-github.com/go-fonts/latin-modern v0.2.0/go.mod h1:rQVLdDMK+mK1xscDwsqM5J8U2jrRa3T0ecnM9pNujks=
-github.com/go-fonts/liberation v0.1.1/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY=
-github.com/go-fonts/liberation v0.2.0/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY=
-github.com/go-fonts/stix v0.1.0/go.mod h1:w/c1f0ldAUlJmLBvlbkvVXLAD+tAMqobIIQpmnUIzUY=
-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-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
-github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
-github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
-github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
-github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
-github.com/go-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U=
-github.com/go-latex/latex v0.0.0-20210823091927-c0d11ff05a81/go.mod h1:SX0U8uGpxhq9o2S/CELCSUxEWWAuoCUcVCQWv7G2OCk=
-github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
-github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
-github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
-github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
-github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
-github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
-github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
-github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
-github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
-github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
-github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
-github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI=
-github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
-github.com/go-openapi/analysis v0.21.2/go.mod h1:HZwRk4RRisyG8vx2Oe6aqeSQcoxRp47Xkp3+K6q+LdY=
-github.com/go-openapi/errors v0.19.8/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
-github.com/go-openapi/errors v0.19.9/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
-github.com/go-openapi/errors v0.20.2/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
-github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
-github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
-github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
-github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
-github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
-github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
-github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
-github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg=
-github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns=
-github.com/go-openapi/loads v0.21.1/go.mod h1:/DtAMXXneXFjbQMGEtbamCZb+4x7eGwkvZCvBmwUG+g=
-github.com/go-openapi/runtime v0.23.1/go.mod h1:AKurw9fNre+h3ELZfk6ILsfvPN+bvvlaU/M9q/r9hpk=
-github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
-github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
-github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I=
-github.com/go-openapi/strfmt v0.21.0/go.mod h1:ZRQ409bWMj+SOgXofQAGTIo2Ebu72Gs+WaRADcS5iNg=
-github.com/go-openapi/strfmt v0.21.1/go.mod h1:I/XVKeLc5+MM5oPNN7P6urMOpuLXEcNrCX/rPGuWb0k=
-github.com/go-openapi/strfmt v0.21.2/go.mod h1:I/XVKeLc5+MM5oPNN7P6urMOpuLXEcNrCX/rPGuWb0k=
-github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
-github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
-github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
-github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
-github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
-github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
-github.com/go-openapi/validate v0.21.0/go.mod h1:rjnrwK57VJ7A8xqfpAOEKRH8yQSGUriMu5/zuPSQ1hg=
-github.com/go-pdf/fpdf v0.5.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M=
-github.com/go-pdf/fpdf v0.6.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M=
-github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
-github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
-github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
-github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
-github.com/go-resty/resty/v2 v2.1.1-0.20191201195748-d7b97669fe48/go.mod h1:dZGr0i9PLlaaTD4H/hoZIDjQ+r6xq8mgbRzHZf7f2J8=
-github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
-github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4=
-github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
-github.com/go-zookeeper/zk v1.0.2/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw=
-github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0=
-github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY=
-github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg=
-github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
-github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
-github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs=
-github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI=
-github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI=
-github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk=
-github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28=
-github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo=
-github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk=
-github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw=
-github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360=
-github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg=
-github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE=
-github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8=
-github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc=
-github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc=
-github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4=
-github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4=
-github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ=
-github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0=
-github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw=
-github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
-github.com/goccy/go-yaml v1.9.5/go.mod h1:U/jl18uSupI5rdI2jmuCswEA2htH9eXfferR3KfscvA=
-github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
-github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
-github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4=
-github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
-github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
-github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
-github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
-github.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU=
-github.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c=
-github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
-github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
-github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
-github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
-github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
-github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
-github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
-github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
-github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
-github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
-github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
-github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4=
-github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE=
-github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ=
-github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
-github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/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/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
-github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
-github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
-github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
-github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
-github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
-github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
-github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
-github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
-github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
-github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
-github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
-github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
-github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
-github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
-github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
-github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
-github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
-github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
-github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
-github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
-github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
-github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
-github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
-github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
-github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
-github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
-github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
-github.com/gomodule/redigo v1.8.9 h1:Sl3u+2BI/kk+VEatbj0scLdrFhjPmbxOc1myhDP41ws=
-github.com/gomodule/redigo v1.8.9/go.mod h1:7ArFNvsTjH8GMMzB4uy1snslv2BwmginuMs06a1uzZE=
-github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
-github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
-github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
-github.com/google/flatbuffers v2.0.8+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
-github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
-github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
-github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
-github.com/google/go-cmp v0.3.2-0.20191028172631-481baca67f93/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
-github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
-github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
-github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
-github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
-github.com/google/go-containerregistry v0.5.1/go.mod h1:Ct15B4yir3PLOP5jsy0GNeYVaIZs/MK/Jz5any1wFW0=
-github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
-github.com/google/go-pkcs11 v0.2.0/go.mod h1:6eQoGcuNJpa7jnd5pMGdkSaQpNDYvPlXWMcjXXThLlY=
-github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
-github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
-github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
-github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
-github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no=
-github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
-github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
-github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
-github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk=
-github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw=
-github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk=
-github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
-github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
-github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
-github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
-github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
-github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
-github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
-github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/pprof v0.0.0-20220318212150-b2ab0324ddda/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg=
-github.com/google/pprof v0.0.0-20221103000818-d260c55eee4c h1:lvddKcYTQ545ADhBujtIJmqQrZBDsGo7XIMbAQe/sNY=
-github.com/google/pprof v0.0.0-20221103000818-d260c55eee4c/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
-github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
-github.com/google/s2a-go v0.1.0/go.mod h1:OJpEgntRZo8ugHpF9hkoLJbS5dSI20XZeXJ9JVywLlM=
-github.com/google/s2a-go v0.1.3/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A=
-github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc=
-github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A=
-github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
-github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
-github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
-github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg=
-github.com/googleapis/enterprise-certificate-proxy v0.2.1/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k=
-github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k=
-github.com/googleapis/enterprise-certificate-proxy v0.2.4/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k=
-github.com/googleapis/enterprise-certificate-proxy v0.2.5 h1:UR4rDjcgpgEnqpIEvkiqTYKBCKLNmlge2eVjoZfySzM=
-github.com/googleapis/enterprise-certificate-proxy v0.2.5/go.mod h1:RxW0N9901Cko1VOCW3SXCpWP+mlIEkk2tP7jnHy9a3w=
-github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
-github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
-github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
-github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM=
-github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM=
-github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM=
-github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c=
-github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqEF02fYlzkUCyo=
-github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY=
-github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8=
-github.com/googleapis/gax-go/v2 v2.7.1/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI=
-github.com/googleapis/gax-go/v2 v2.8.0/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI=
-github.com/googleapis/gax-go/v2 v2.10.0/go.mod h1:4UOEnMCrxsSqQ940WnTiD6qJ63le2ev3xfyagutxiPw=
-github.com/googleapis/gax-go/v2 v2.11.0/go.mod h1:DxmR61SGKkGLa2xigwuZIQpkCI2S5iydzRfb3peWZJI=
-github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas=
-github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU=
-github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg=
-github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU=
-github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA=
-github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4=
-github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8 h1:tlyzajkF3030q6M8SvmJSemC9DTHL/xaMa18b65+JM4=
-github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
-github.com/gophercloud/gophercloud v0.24.0/go.mod h1:Q8fZtyi5zZxPS/j9aj3sSxtvj41AdQMDwyo1myduD5c=
-github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
-github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
-github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
-github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
-github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
-github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
-github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
-github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
-github.com/grafana/regexp v0.0.0-20220304095617-2e8d9baf4ac2/go.mod h1:M5qHK+eWfAv8VR/265dIuEpL3fNfeC21tXXp9itM24A=
-github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
-github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
-github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
-github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y=
-github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI=
-github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8=
-github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
-github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
-github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
-github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w=
-github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
-github.com/hashicorp/consul/api v1.12.0/go.mod h1:6pVBMo0ebnYdt2S3H87XhekM/HHrUoTD2XXb/VrZVy0=
-github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
-github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms=
-github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
-github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
-github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
-github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
-github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
-github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
-github.com/hashicorp/go-hclog v0.12.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
-github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
-github.com/hashicorp/go-immutable-radix v1.2.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
-github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
-github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I=
-github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
-github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
-github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
-github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
-github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
-github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
-github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
-github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A=
-github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
-github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
-github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
-github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
-github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
-github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
-github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
-github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
-github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
-github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
-github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc=
-github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
-github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE=
-github.com/hashicorp/memberlist v0.3.1/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE=
-github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
-github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4=
-github.com/hetznercloud/hcloud-go v1.33.1/go.mod h1:XX/TQub3ge0yWR2yHWmnDVIrB+MQbda1pHxkUmDlUME=
-github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
-github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
-github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
-github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
-github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
-github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
-github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
-github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
-github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
-github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
-github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
-github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
-github.com/intel/goresctrl v0.2.0/go.mod h1:+CZdzouYFn5EsxgqAQTEzMfwKwuc0fVdMrT9FCCAVRQ=
-github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA=
-github.com/j-keck/arping v1.0.2/go.mod h1:aJbELhR92bSk7tp79AWM/ftfc90EfEi2bQJrbBFOsPw=
-github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
-github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
-github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
-github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
-github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
-github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
-github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
-github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
-github.com/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52CupLJyoVwB10FQ/IQlF1pdL8=
-github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
-github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
-github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
-github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
-github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
-github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
-github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
-github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
-github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
-github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
-github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
-github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
-github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
-github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
-github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
-github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
-github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
-github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
-github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4=
-github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
-github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
-github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
-github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
-github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
-github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
-github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE=
-github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
-github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
-github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
-github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
-github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY=
-github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
-github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
-github.com/kolo/xmlrpc v0.0.0-20201022064351-38db28db192b/go.mod h1:pcaDhQK0/NJZEvtCO0qQPPropqV0sJOJ6YW7X+9kRwM=
-github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
-github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
-github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
-github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
-github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
-github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
-github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
-github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
-github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
-github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
-github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
-github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
-github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
-github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
-github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
-github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
-github.com/linode/linodego v1.4.0/go.mod h1:PVsRxSlOiJyvG4/scTszpmZDTdgS+to3X6eS8pRrWI8=
-github.com/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3/go.mod h1:3r6x7q95whyfWQpmGZTu3gk3v2YkMi05HEzl7Tf7YEo=
-github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA=
-github.com/lyft/protoc-gen-star v0.6.1/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA=
-github.com/lyft/protoc-gen-star/v2 v2.0.1/go.mod h1:RcCdONR2ScXaYnQC5tUzxzlpA3WVYF7/opLeUgcQs/o=
-github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
-github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
-github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
-github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
-github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
-github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
-github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
-github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
-github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE=
-github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
-github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho=
-github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
-github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
-github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
-github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
-github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
-github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
-github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
-github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
-github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
-github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
-github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
-github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
-github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
-github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
-github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
-github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
-github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
-github.com/mattn/go-shellwords v1.0.6/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
-github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
-github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
-github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
-github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
-github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
-github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2/go.mod h1:eD9eIE7cdwcMi9rYluz88Jz2VyhSmden33/aXg4oVIY=
-github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
-github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
-github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
-github.com/miekg/dns v1.1.48/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
-github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
-github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY=
-github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE=
-github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4=
-github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
-github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI=
-github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
-github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
-github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
-github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
-github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
-github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
-github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
-github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
-github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
-github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
-github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
-github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A=
-github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
-github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
-github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A=
-github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A=
-github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU=
-github.com/moby/sys/signal v0.6.0/go.mod h1:GQ6ObYZfqacOwTtlXvcmh9A26dVRul/hbOZn88Kg8Tg=
-github.com/moby/sys/symlink v0.1.0/go.mod h1:GGDODQmbFOjFsXvfLVn3+ZRxkch54RkSiGqsZeMYowQ=
-github.com/moby/sys/symlink v0.2.0/go.mod h1:7uZVF2dqJjG/NsClqul95CqKOBRQyYSNnJ6BMgR/gFs=
-github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo=
-github.com/moby/term v0.0.0-20210610120745-9d4ed1856297/go.mod h1:vgPCkQMyxTZ7IDy8SXRufE172gr8+K/JE/7hHFxHW3A=
-github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw=
-github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
-github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
-github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
-github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
-github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
-github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8=
-github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
-github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
-github.com/mostynb/zstdpool-syncpool v0.0.7 h1:meYfUODlzmtOCrFmbJsUVEIt5rbmNUsz+Bu+Vnr95ls=
-github.com/mostynb/zstdpool-syncpool v0.0.7/go.mod h1:YpzqIpN8xvRZZvemem7CMLPWkjuaKR37MnkQruSj6aw=
-github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ=
-github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
-github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
-github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
-github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
-github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
-github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM=
-github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
-github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
-github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
-github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU=
-github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
-github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
-github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg=
-github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
-github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0=
-github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
-github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
-github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
-github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
-github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
-github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
-github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
-github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
-github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
-github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
-github.com/onsi/gomega v1.15.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0=
-github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
-github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
-github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
-github.com/opencontainers/go-digest v1.0.0-rc1.0.20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
-github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
-github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
-github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
-github.com/opencontainers/image-spec v1.0.2-0.20211117181255-693428a734f5/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
-github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
-github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
-github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
-github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
-github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
-github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0=
-github.com/opencontainers/runc v1.0.2/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0=
-github.com/opencontainers/runc v1.1.0/go.mod h1:Tj1hFw6eFWp/o33uxGf5yF2BX5yz2Z6iptFpuvbbKqc=
-github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
-github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
-github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
-github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
-github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
-github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
-github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs=
-github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE=
-github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo=
-github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8=
-github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI=
-github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
-github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
-github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
-github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
-github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
-github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw=
-github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
-github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
-github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE=
-github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
-github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
-github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
-github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY=
-github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI=
-github.com/phpdave11/gofpdi v1.0.13/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI=
-github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
-github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
-github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
-github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
-github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
-github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
-github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
-github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
-github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
-github.com/pkg/xattr v0.4.4 h1:FSoblPdYobYoKCItkqASqcrKCxRn9Bgurz0sCBwzO5g=
-github.com/pkg/xattr v0.4.4/go.mod h1:sBD3RAqlr8Q+RC3FutZcikpT8nyDrIEEBw2J744gVWs=
-github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
-github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
-github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
-github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
-github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
-github.com/prometheus/alertmanager v0.24.0/go.mod h1:r6fy/D7FRuZh5YbnX6J3MBY0eI4Pb5yPYS7/bPSXXqI=
-github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
-github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
-github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
-github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
-github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
-github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
-github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
-github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
-github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
-github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
-github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
-github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
-github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
-github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
-github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
-github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
-github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
-github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
-github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
-github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
-github.com/prometheus/common v0.29.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
-github.com/prometheus/common v0.30.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
-github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
-github.com/prometheus/common v0.34.0/go.mod h1:gB3sOl7P0TvJabZpLY5uQMpUqRCPPCyRLCZYc7JZTNE=
-github.com/prometheus/common/assets v0.1.0/go.mod h1:D17UVUE12bHbim7HzwUvtqm6gwBEaDQ0F+hIGbFbccI=
-github.com/prometheus/common/sigv4 v0.1.0/go.mod h1:2Jkxxk9yYvCkE5G1sQT7GuEXm57JrvHu9k5YwTjsNtI=
-github.com/prometheus/exporter-toolkit v0.7.1/go.mod h1:ZUBIj498ePooX9t/2xtDjeQYwvRpiPP2lh5u4iblj2g=
-github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
-github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
-github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
-github.com/prometheus/procfs v0.0.0-20190522114515-bc1a522cf7b1/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
-github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
-github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
-github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
-github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
-github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
-github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
-github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
-github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
-github.com/prometheus/prometheus v0.35.0 h1:N93oX6BrJ2iP3UuE2Uz4Lt+5BkUpaFer3L9CbADzesc=
-github.com/prometheus/prometheus v0.35.0/go.mod h1:7HaLx5kEPKJ0GDgbODG0fZgXbQ8K/XjZNJXQmbmgQlY=
-github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
-github.com/rakyll/embedmd v0.0.0-20171029212350-c8060a0752a2/go.mod h1:7jOTMgqac46PZcF54q6l2hkLEG8op93fZu61KmxWDV4=
-github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
-github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
-github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
-github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
-github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
-github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
-github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
-github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
-github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
-github.com/rs/cors v1.8.2/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
-github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
-github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w=
-github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245/go.mod h1:pQAZKsJ8yyVxGRWYNEm9oFB8ieLgKFnamEyDmSA0BRk=
-github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
-github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
-github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4=
-github.com/safchain/ethtool v0.0.0-20210803160452-9aa261dae9b1/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4=
-github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
-github.com/scaleway/scaleway-sdk-go v1.0.0-beta.9/go.mod h1:fCa7OJZ/9DRTnOKmxvT6pn+LPWUptQAmHF/SBJUGEcg=
-github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=
-github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U=
-github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
-github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
-github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg=
-github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
-github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
-github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw=
-github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
-github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
-github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
-github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
-github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
-github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
-github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
-github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
-github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
-github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
-github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
-github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
-github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
-github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0=
-github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
-github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
-github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
-github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4=
-github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
-github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y=
-github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
-github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
-github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
-github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
-github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
-github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo=
-github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
-github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
-github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
-github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
-github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
-github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
-github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
-github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
-github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8=
-github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
-github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
-github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
-github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
-github.com/stretchr/testify v0.0.0-20180303142811-b89eecf5ca5d/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
-github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
-github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
-github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
-github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
-github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
-github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
-github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
-github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
-github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
-github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
-github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
-github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
-github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
-github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
-github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I=
-github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
-github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
-github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
-github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
-github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
-github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c/go.mod h1:hzIxponao9Kjc7aWznkXaL4U4TWaDSs8zcsY4Ka08nM=
-github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
-github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
-github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
-github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
-github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
-github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk=
-github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
-github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=
-github.com/vishvananda/netlink v1.1.1-0.20210330154013-f5de75959ad5/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=
-github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI=
-github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
-github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
-github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
-github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
-github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI=
-github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
-github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs=
-github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM=
-github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
-github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
-github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs=
-github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
-github.com/xlab/treeprint v1.1.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0=
-github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
-github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
-github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
-github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
-github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
-github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs=
-github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA=
-github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg=
-github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
-github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA=
-go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
-go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
-go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
-go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
-go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg=
-go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
-go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
-go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ=
-go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0=
-go.etcd.io/etcd/pkg/v3 v3.5.0/go.mod h1:UzJGatBQ1lXChBkQF0AuAtkRQMYnHubxAEYIrC3MSsE=
-go.etcd.io/etcd/raft/v3 v3.5.0/go.mod h1:UFOHSIvO/nKwd4lhkwabrTD3cqW5yVyYYf/KlD00Szc=
-go.etcd.io/etcd/server/v3 v3.5.0/go.mod h1:3Ah5ruV+M+7RZr0+Y/5mNLwC+eQlni+mQmOVdCRJoS4=
-go.mongodb.org/mongo-driver v1.7.3/go.mod h1:NqaYOwnXWr5Pm7AOpO5QFxKJ503nbMse/R79oO62zWg=
-go.mongodb.org/mongo-driver v1.7.5/go.mod h1:VXEWRZ6URJIkUq2SCAyapmhH0ZLRBP+FT4xhp5Zvxng=
-go.mongodb.org/mongo-driver v1.8.3/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY=
-go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk=
-go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
-go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
-go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
-go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
-go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
-go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
-go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
-go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
-go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
-go.opentelemetry.io/contrib v0.20.0/go.mod h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUzl5H4LY0Kc=
-go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0/go.mod h1:oVGt1LRbBOBq1A5BQLlUg9UaU/54aiHw8cgjV3aWZ/E=
-go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.28.0/go.mod h1:vEhqr0m4eTc+DWxfsXoXue2GBgV2uUwVznkGIHW/e5w=
-go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0/go.mod h1:2AboqHi0CiIZU0qwhtUfCYD1GeUzvvIXWNkhDt7ZMG4=
-go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.31.0/go.mod h1:PFmBsWbldL1kiWZk9+0LBZz2brhByaGsvp6pRICMlPE=
-go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo=
-go.opentelemetry.io/otel v1.3.0/go.mod h1:PWIKzi6JCp7sM0k9yZ43VX+T345uNbAkDKwHVjb2PTs=
-go.opentelemetry.io/otel v1.6.0/go.mod h1:bfJD2DZVw0LBxghOTlgnlI0CV3hLDu9XF/QKOUXMTQQ=
-go.opentelemetry.io/otel v1.6.1/go.mod h1:blzUabWHkX6LJewxvadmzafgh/wnvBSDBdOuwkAtrWQ=
-go.opentelemetry.io/otel/exporters/otlp v0.20.0/go.mod h1:YIieizyaN77rtLJra0buKiNBOm9XQfkPEKBeuhoMwAM=
-go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.3.0/go.mod h1:VpP4/RMn8bv8gNo9uK7/IMY4mtWLELsS+JIP0inH0h4=
-go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.6.1/go.mod h1:NEu79Xo32iVb+0gVNV8PMd7GoWqnyDXRlj04yFjqz40=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.3.0/go.mod h1:hO1KLR7jcKaDDKDkvI9dP/FIhpmna5lkqPUQdEjFAM8=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.6.1/go.mod h1:YJ/JbY5ag/tSQFXzH3mtDmHqzF3aFn3DI/aB1n7pt4w=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.3.0/go.mod h1:keUU7UfnwWTWpJ+FWnyqmogPa82nuU5VUANFq49hlMY=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.6.1/go.mod h1:UJJXJj0rltNIemDMwkOJyggsvyMG9QHfJeFH0HS5JjM=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.3.0/go.mod h1:QNX1aly8ehqqX1LEa6YniTU7VY9I6R3X/oPxhGdTceE=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.6.1/go.mod h1:DAKwdo06hFLc0U88O10x4xnb5sc7dDRDqRuiN+io8JE=
-go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU=
-go.opentelemetry.io/otel/metric v0.28.0/go.mod h1:TrzsfQAmQaB1PDcdhBauLMk7nyyg9hm+GoQq/ekE9Iw=
-go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw=
-go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc=
-go.opentelemetry.io/otel/sdk v1.3.0/go.mod h1:rIo4suHNhQwBIPg9axF8V9CA72Wz2mKF1teNrup8yzs=
-go.opentelemetry.io/otel/sdk v1.6.1/go.mod h1:IVYrddmFZ+eJqu2k38qD3WezFR2pymCzm8tdxyh3R4E=
-go.opentelemetry.io/otel/sdk/export/metric v0.20.0/go.mod h1:h7RBNMsDJ5pmI1zExLi+bJK+Dr8NQCh0qGhm1KDnNlE=
-go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4/0TjTXukfxjzSTpHE=
-go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw=
-go.opentelemetry.io/otel/trace v1.3.0/go.mod h1:c/VDhno8888bvQYmbYLqe41/Ldmr/KKunbvWM4/fEjk=
-go.opentelemetry.io/otel/trace v1.6.0/go.mod h1:qs7BrU5cZ8dXQHBGxHMOxwME/27YH2qEp4/+tZLLwJE=
-go.opentelemetry.io/otel/trace v1.6.1/go.mod h1:RkFRM1m0puWIq10oxImnGEduNBzxiN7TXluRBtE+5j0=
-go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
-go.opentelemetry.io/proto/otlp v0.11.0/go.mod h1:QpEjXPrNQzrFDZgoTo49dgHR9RYRSrg3NAKnUGl9YpQ=
-go.opentelemetry.io/proto/otlp v0.12.1/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
-go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
-go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
-go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
-go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
-go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
-go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
-go.uber.org/automaxprocs v1.5.1/go.mod h1:BF4eumQw0P9GtnuxxovUd06vwm1o18oMzFtK66vU6XU=
-go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
-go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
-go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo=
-go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
-go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
-go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
-go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
-go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
-go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
-go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
-go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
-go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
-go.uber.org/zap v1.25.0 h1:4Hvk6GtkucQ790dqmj7l1eEnRdKm3k3ZUrUMS2d5+5c=
-go.uber.org/zap v1.25.0/go.mod h1:JIAUzQIH94IC4fOJQm7gMmBJP5k7wQfdcnYdPoEXJYk=
-go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
-golang.org/x/build v0.0.0-20191031202223-0706ea4fce0c h1:jjNoDZTS0vmbqBhqD5MPXauZW+kcGyflfDDFBNCPSVI=
-golang.org/x/build v0.0.0-20191031202223-0706ea4fce0c/go.mod h1:Nl5grlQor/lxfX9FfGLe+g2cVSCiURG36KQgsg/ODs4=
-golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
-golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
-golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
-golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
-golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
-golang.org/x/crypto v0.0.0-20190424203555-c05e17bb3b2d/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
-golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
-golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
-golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
-golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
-golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/crypto v0.0.0-20211202192323-5770296d904e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
-golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
-golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
-golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
-golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
-golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
-golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
-golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
-golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
-golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
-golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
-golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
-golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
-golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
-golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
-golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE=
-golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
-golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
-golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
-golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
-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/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
-golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
-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=
-golang.org/x/image v0.0.0-20200119044424-58c23975cae1/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
-golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
-golang.org/x/image v0.0.0-20200618115811-c13761719519/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
-golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
-golang.org/x/image v0.0.0-20210216034530-4410531fe030/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
-golang.org/x/image v0.0.0-20210607152325-775e3b0c77b9/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
-golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
-golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
-golang.org/x/image v0.0.0-20220302094943-723b81ca9867/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
-golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
-golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
-golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
-golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
-golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
-golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
-golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
-golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/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/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/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
-golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
-golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
-golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
-golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
-golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
-golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
-golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
-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=
-golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190502183928-7f726cade0ab/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
-golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
-golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20190628185345-da137c7871d7/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-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/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-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-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/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-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/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
-golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
-golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
-golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
-golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
-golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
-golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
-golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
-golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
-golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
-golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
-golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
-golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20210505214959-0714010a04ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
-golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
-golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
-golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
-golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
-golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
-golang.org/x/net v0.0.0-20221012135044-0b7e1fb9d458/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
-golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
-golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
-golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
-golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
-golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
-golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
-golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
-golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
-golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
-golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
-golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
-golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
-golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
-golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
-golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
-golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
-golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
-golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
-golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE=
-golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE=
-golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
-golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
-golang.org/x/oauth2 v0.0.0-20221006150949-b44042a4b9c1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
-golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
-golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec=
-golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I=
-golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw=
-golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4=
-golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE=
-golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI=
-golang.org/x/oauth2 v0.11.0 h1:vPL4xzxBM4niKCW6g9whtaWVXTJf1U5e4aZxxFx/gbU=
-golang.org/x/oauth2 v0.11.0/go.mod h1:LdF7O/8bLR/qWK9DrpXmbHLTouvRHK0SgJl0GmDBchk=
-golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
-golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
-golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
-golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190522044717-8097e1b27ff5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190812073006-9eafafc0a87e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200120151820-655fe14d7479/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200217220822-9197077df867/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/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200817155316-9781c653f443/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20201202213521-69691e467435/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210304124612-50617c2ba197/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210507014357-30e306a8bba5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210903071746-97244b99971b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
-golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
-golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
-golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
-golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
-golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
-golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
-golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
-golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
-golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
-golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
-golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
-golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
-golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
-golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo=
-golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
-golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
-golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
-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/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
-golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
-golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
-golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
-golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
-golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
-golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
-golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
-golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
-golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
-golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
-golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
-golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
-golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
-golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20220224211638-0e9765cccd65/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
-golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
-golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
-golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
-golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
-golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
-golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
-golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
-golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
-golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
-golang.org/x/tools v0.0.0-20190706070813-72ffa07ba3db/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
-golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/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-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/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/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/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-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/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/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/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
-golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
-golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
-golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20200616133436-c1934b75d054/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
-golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
-golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
-golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
-golang.org/x/tools v0.0.0-20200916195026-c9a70fc28ce3/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
-golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
-golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
-golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
-golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
-golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k=
-golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
-golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
-golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
-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/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
-golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
-golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk=
-golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
-gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=
-gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0=
-gonum.org/v1/gonum v0.9.3/go.mod h1:TZumC3NeyVQskjXqmyWt4S3bINhy7B4eYwW69EbyX+0=
-gonum.org/v1/gonum v0.11.0/go.mod h1:fSG4YDCxxUZQJ7rKsQrj0gMOg00Il0Z96/qMA4bVQhA=
-gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
-gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc=
-gonum.org/v1/plot v0.9.0/go.mod h1:3Pcqqmp6RHvJI72kgb8fThyUnav364FOsdDo2aGW5lY=
-gonum.org/v1/plot v0.10.1/go.mod h1:VZW5OlhkL1mysU9vaqNHnsy86inf6Ot+jB3r+BczCEo=
-google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
-google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
-google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
-google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
-google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
-google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
-google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
-google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
-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/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
-google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
-google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
-google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
-google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
-google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
-google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
-google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
-google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
-google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
-google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
-google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo=
-google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4=
-google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw=
-google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU=
-google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k=
-google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
-google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
-google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI=
-google.golang.org/api v0.58.0/go.mod h1:cAbP2FsxoGVNwtgNAmmn3y5G1TWAiVYRmg4yku3lv+E=
-google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I=
-google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo=
-google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g=
-google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA=
-google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8=
-google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs=
-google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA=
-google.golang.org/api v0.77.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA=
-google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw=
-google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg=
-google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o=
-google.golang.org/api v0.85.0/go.mod h1:AqZf8Ep9uZ2pyTvgL+x0D3Zt0eoT9b5E8fmzfu6FO2g=
-google.golang.org/api v0.90.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw=
-google.golang.org/api v0.93.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw=
-google.golang.org/api v0.95.0/go.mod h1:eADj+UBuxkh5zlrSntJghuNeg8HwQ1w5lTKkuqaETEI=
-google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s=
-google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s=
-google.golang.org/api v0.98.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s=
-google.golang.org/api v0.99.0/go.mod h1:1YOf74vkVndF7pG6hIHuINsM7eWwpVTAfNMNiL91A08=
-google.golang.org/api v0.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70=
-google.golang.org/api v0.102.0/go.mod h1:3VFl6/fzoA+qNuS1N1/VfXY4LjoXN/wzeIp7TweWwGo=
-google.golang.org/api v0.103.0/go.mod h1:hGtW6nK1AC+d9si/UBhw8Xli+QMOf6xyNAyJw4qU9w0=
-google.golang.org/api v0.106.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY=
-google.golang.org/api v0.107.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY=
-google.golang.org/api v0.108.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY=
-google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60ca7iI=
-google.golang.org/api v0.111.0/go.mod h1:qtFHvU9mhgTJegR31csQ+rwxyUTHOKFqCKWp1J0fdw0=
-google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg=
-google.golang.org/api v0.118.0/go.mod h1:76TtD3vkgmZ66zZzp72bUUklpmQmKlhh6sYtIjYK+5E=
-google.golang.org/api v0.122.0/go.mod h1:gcitW0lvnyWjSp9nKxAbdHKIZ6vF4aajGueeslZOyms=
-google.golang.org/api v0.124.0/go.mod h1:xu2HQurE5gi/3t1aFCvhPD781p0a3p11sdunTJ2BlP4=
-google.golang.org/api v0.125.0/go.mod h1:mBwVAtz+87bEN6CbA1GtZPDOqY2R5ONPqJeIlvyo4Aw=
-google.golang.org/api v0.126.0/go.mod h1:mBwVAtz+87bEN6CbA1GtZPDOqY2R5ONPqJeIlvyo4Aw=
-google.golang.org/api v0.128.0/go.mod h1:Y611qgqaE92On/7g65MQgxYul3c0rEB894kniWLY750=
-google.golang.org/api v0.134.0 h1:ktL4Goua+UBgoP1eL1/60LwZJqa1sIzkLmvoR3hR6Gw=
-google.golang.org/api v0.134.0/go.mod h1:sjRL3UnjTx5UqNQS9EWr9N8p7xbHpy1k0XGRLCf3Spk=
-google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
-google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
-google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
-google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
-google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
-google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
-google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
-google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
-google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk=
-google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
-google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190522204451-c2c4e71fbf69/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
-google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
-google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
-google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
-google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
-google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
-google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
-google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
-google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
-google.golang.org/genproto v0.0.0-20200117163144-32f20d992d24/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/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
-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/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
-google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
-google.golang.org/genproto v0.0.0-20200527145253-8367513e4ece/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
-google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
-google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
-google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
-google.golang.org/genproto v0.0.0-20210506142907-4a47615972c2/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
-google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
-google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
-google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
-google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
-google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=
-google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
-google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
-google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
-google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
-google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w=
-google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
-google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
-google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
-google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
-google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
-google.golang.org/genproto v0.0.0-20210917145530-b395a37504d4/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
-google.golang.org/genproto v0.0.0-20210921142501-181ce0d877f6/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20211018162055-cf77aa76bad2/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
-google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
-google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
-google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
-google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E=
-google.golang.org/genproto v0.0.0-20220329172620-7be39ac1afc7/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
-google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
-google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
-google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
-google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
-google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
-google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
-google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
-google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
-google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
-google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
-google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
-google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
-google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
-google.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
-google.golang.org/genproto v0.0.0-20220722212130-b98a9ff5e252/go.mod h1:GkXuJDJ6aQ7lnJcRF+SJVgFdQhypqgl3LB1C9vabdRE=
-google.golang.org/genproto v0.0.0-20220801145646-83ce21fca29f/go.mod h1:iHe1svFLAZg9VWz891+QbRMwUv9O/1Ww+/mngYeThbc=
-google.golang.org/genproto v0.0.0-20220815135757-37a418bb8959/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk=
-google.golang.org/genproto v0.0.0-20220817144833-d7fd3f11b9b1/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk=
-google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk=
-google.golang.org/genproto v0.0.0-20220829144015-23454907ede3/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk=
-google.golang.org/genproto v0.0.0-20220829175752-36a9c930ecbf/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk=
-google.golang.org/genproto v0.0.0-20220913154956-18f8339a66a5/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo=
-google.golang.org/genproto v0.0.0-20220914142337-ca0e39ece12f/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo=
-google.golang.org/genproto v0.0.0-20220915135415-7fd63a7952de/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo=
-google.golang.org/genproto v0.0.0-20220916172020-2692e8806bfa/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo=
-google.golang.org/genproto v0.0.0-20220919141832-68c03719ef51/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo=
-google.golang.org/genproto v0.0.0-20220920201722-2b89144ce006/go.mod h1:ht8XFiar2npT/g4vkk7O0WYS1sHOHbdujxbEp7CJWbw=
-google.golang.org/genproto v0.0.0-20220926165614-551eb538f295/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI=
-google.golang.org/genproto v0.0.0-20220926220553-6981cbe3cfce/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI=
-google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqwhZAwq4wsRUaVG555sVgsNmIjRtO7t/JH29U=
-google.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM=
-google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM=
-google.golang.org/genproto v0.0.0-20221024153911-1573dae28c9c/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s=
-google.golang.org/genproto v0.0.0-20221024183307-1bc688fe9f3e/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s=
-google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c/go.mod h1:CGI5F/G+E5bKwmfYo09AXuVN4dD894kIKUFmVbP2/Fo=
-google.golang.org/genproto v0.0.0-20221109142239-94d6d90a7d66/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg=
-google.golang.org/genproto v0.0.0-20221114212237-e4508ebdbee1/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg=
-google.golang.org/genproto v0.0.0-20221117204609-8f9c96812029/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg=
-google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg=
-google.golang.org/genproto v0.0.0-20221201164419-0e50fba7f41c/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg=
-google.golang.org/genproto v0.0.0-20221201204527-e3fa12d562f3/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg=
-google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd/go.mod h1:cTsE614GARnxrLsqKREzmNYJACSWWpAWdNMwnD7c2BE=
-google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
-google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
-google.golang.org/genproto v0.0.0-20230112194545-e10362b5ecf9/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
-google.golang.org/genproto v0.0.0-20230113154510-dbe35b8444a5/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
-google.golang.org/genproto v0.0.0-20230123190316-2c411cf9d197/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
-google.golang.org/genproto v0.0.0-20230124163310-31e0e69b6fc2/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
-google.golang.org/genproto v0.0.0-20230125152338-dcaf20b6aeaa/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
-google.golang.org/genproto v0.0.0-20230127162408-596548ed4efa/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
-google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
-google.golang.org/genproto v0.0.0-20230216225411-c8e22ba71e44/go.mod h1:8B0gmkoRebU8ukX6HP+4wrVQUY1+6PkQ44BSyIlflHA=
-google.golang.org/genproto v0.0.0-20230222225845-10f96fb3dbec/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw=
-google.golang.org/genproto v0.0.0-20230223222841-637eb2293923/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw=
-google.golang.org/genproto v0.0.0-20230303212802-e74f57abe488/go.mod h1:TvhZT5f700eVlTNwND1xoEZQeWTB2RY/65kplwl/bFA=
-google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s=
-google.golang.org/genproto v0.0.0-20230320184635-7606e756e683/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s=
-google.golang.org/genproto v0.0.0-20230323212658-478b75c54725/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak=
-google.golang.org/genproto v0.0.0-20230330154414-c0448cd141ea/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak=
-google.golang.org/genproto v0.0.0-20230331144136-dcfb400f0633/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak=
-google.golang.org/genproto v0.0.0-20230403163135-c38d8f061ccd/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak=
-google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU=
-google.golang.org/genproto v0.0.0-20230525234025-438c736192d0/go.mod h1:9ExIQyXL5hZrHzQceCwuSYwZZ5QZBazOcprJ5rgs3lY=
-google.golang.org/genproto v0.0.0-20230526161137-0005af68ea54/go.mod h1:zqTuNwFlFRsw5zIts5VnzLQxSRqh+CGOTVMlYbY0Eyk=
-google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:xZnkP7mREFX5MORlOPEzLMr+90PPZQ2QWzrVTWfAq64=
-google.golang.org/genproto v0.0.0-20230629202037-9506855d4529/go.mod h1:xZnkP7mREFX5MORlOPEzLMr+90PPZQ2QWzrVTWfAq64=
-google.golang.org/genproto v0.0.0-20230706204954-ccb25ca9f130 h1:Au6te5hbKUV8pIYWHqOUZ1pva5qK/rwbIhoXEUB9Lu8=
-google.golang.org/genproto v0.0.0-20230706204954-ccb25ca9f130/go.mod h1:O9kGHb51iE/nOGvQaDUuadVYqovW56s5emA88lQnj6Y=
-google.golang.org/genproto/googleapis/api v0.0.0-20230525234020-1aefcd67740a/go.mod h1:ts19tUU+Z0ZShN1y3aPyq2+O3d5FUNNgT6FtOzmrNn8=
-google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig=
-google.golang.org/genproto/googleapis/api v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig=
-google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig=
-google.golang.org/genproto/googleapis/api v0.0.0-20230629202037-9506855d4529/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig=
-google.golang.org/genproto/googleapis/api v0.0.0-20230706204954-ccb25ca9f130 h1:XVeBY8d/FaK4848myy41HBqnDwvxeV3zMZhwN1TvAMU=
-google.golang.org/genproto/googleapis/api v0.0.0-20230706204954-ccb25ca9f130/go.mod h1:mPBs5jNgx2GuQGvFwUvVKqtn6HsUw9nP64BedgvqEsQ=
-google.golang.org/genproto/googleapis/bytestream v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:ylj+BE99M198VPbBh6A8d9n3w8fChvyLK3wwBOjXBFA=
-google.golang.org/genproto/googleapis/bytestream v0.0.0-20230720185612-659f7aaaa771 h1:gm8vsVR64Jx1GxHY8M+p8YA2bxU/H/lymcutB2l7l9s=
-google.golang.org/genproto/googleapis/bytestream v0.0.0-20230720185612-659f7aaaa771/go.mod h1:3QoBVwTHkXbY1oRGzlhwhOykfcATQN43LJ6iT8Wy8kE=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234015-3fc162c6f38a/go.mod h1:xURIpW9ES5+/GZhnV6beoEtxQrnkRGIfP5VQG2tCBLc=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20230629202037-9506855d4529/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20230720185612-659f7aaaa771 h1:Z8qdAF9GFsmcUuWQ5KVYIpP3PCKydn/YKORnghIalu4=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20230720185612-659f7aaaa771/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM=
-google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
-google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
-google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
-google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
-google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
-google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
-google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
-google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA=
-google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
-google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
-google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
-google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
-google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
-google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
-google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
-google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
-google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
-google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
-google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
-google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
-google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
-google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
-google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
-google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
-google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
-google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
-google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
-google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
-google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
-google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
-google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
-google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
-google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
-google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
-google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
-google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
-google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
-google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
-google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
-google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
-google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
-google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww=
-google.golang.org/grpc v1.52.0/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY=
-google.golang.org/grpc v1.52.3/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY=
-google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw=
-google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g=
-google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8=
-google.golang.org/grpc v1.56.1/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s=
-google.golang.org/grpc v1.56.2/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s=
-google.golang.org/grpc v1.57.0 h1:kfzNeI/klCGD2YPMUlaGNT3pxvYfga7smW3Vth8Zsiw=
-google.golang.org/grpc v1.57.0/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo=
-google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
-google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
-google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
-google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
-google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
-google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
-google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
-google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
-google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
-google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
-google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
-google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
-google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
-google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
-google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
-google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
-google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
-google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
-google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
-google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
-gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
-gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
-gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
-gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
-gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
-gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
-gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
-gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
-gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
-gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
-gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
-gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
-gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
-gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
-gopkg.in/telebot.v3 v3.0.0/go.mod h1:7rExV8/0mDDNu9epSrDm/8j22KLaActH1Tbee6YjzWg=
-gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
-gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
-gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
-gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
-gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
-gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
-gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
-gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
-grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
-honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-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/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
-honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
-honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
-honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las=
-k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo=
-k8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ=
-k8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8=
-k8s.io/api v0.22.5/go.mod h1:mEhXyLaSD1qTOf40rRiKXkc+2iCem09rWLlFwhCEiAs=
-k8s.io/api v0.23.5/go.mod h1:Na4XuKng8PXJ2JsploYYrivXrINeTaycCGcYgF91Xm8=
-k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU=
-k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU=
-k8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc=
-k8s.io/apimachinery v0.22.1/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0=
-k8s.io/apimachinery v0.22.5/go.mod h1:xziclGKwuuJ2RM5/rSFQSYAj0zdbci3DH8kj+WvyN0U=
-k8s.io/apimachinery v0.23.5/go.mod h1:BEuFMMBaIbcOqVIJqNZJXGFTP4W6AycEpb5+m/97hrM=
-k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU=
-k8s.io/apiserver v0.20.4/go.mod h1:Mc80thBKOyy7tbvFtB4kJv1kbdD0eIH8k8vianJcbFM=
-k8s.io/apiserver v0.20.6/go.mod h1:QIJXNt6i6JB+0YQRNcS0hdRHJlMhflFmsBDeSgT1r8Q=
-k8s.io/apiserver v0.22.5/go.mod h1:s2WbtgZAkTKt679sYtSudEQrTGWUSQAPe6MupLnlmaQ=
-k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y=
-k8s.io/client-go v0.20.4/go.mod h1:LiMv25ND1gLUdBeYxBIwKpkSC5IsozMMmOOeSJboP+k=
-k8s.io/client-go v0.20.6/go.mod h1:nNQMnOvEUEsOzRRFIIkdmYOjAZrC8bgq0ExboWSU1I0=
-k8s.io/client-go v0.22.5/go.mod h1:cs6yf/61q2T1SdQL5Rdcjg9J1ElXSwbjSrW2vFImM4Y=
-k8s.io/client-go v0.23.5/go.mod h1:flkeinTO1CirYgzMPRWxUCnV0G4Fbu2vLhYCObnt/r4=
-k8s.io/code-generator v0.19.7/go.mod h1:lwEq3YnLYb/7uVXLorOJfxg+cUu2oihFhHZ0n9NIla0=
-k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk=
-k8s.io/component-base v0.20.4/go.mod h1:t4p9EdiagbVCJKrQ1RsA5/V4rFQNDfRlevJajlGwgjI=
-k8s.io/component-base v0.20.6/go.mod h1:6f1MPBAeI+mvuts3sIdtpjljHWBQ2cIy38oBIWMYnrM=
-k8s.io/component-base v0.22.5/go.mod h1:VK3I+TjuF9eaa+Ln67dKxhGar5ynVbwnGrUiNF4MqCI=
-k8s.io/cri-api v0.17.3/go.mod h1:X1sbHmuXhwaHs9xxYffLqJogVsnI+f6cPRcgPel7ywM=
-k8s.io/cri-api v0.20.1/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI=
-k8s.io/cri-api v0.20.4/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI=
-k8s.io/cri-api v0.20.6/go.mod h1:ew44AjNXwyn1s0U4xCKGodU7J1HzBeZ1MpGrpa5r8Yc=
-k8s.io/cri-api v0.23.1/go.mod h1:REJE3PSU0h/LOV1APBrupxrEJqnoxZC8KWzkBUHwrK4=
-k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
-k8s.io/gengo v0.0.0-20200428234225-8167cfdcfc14/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
-k8s.io/gengo v0.0.0-20201113003025-83324d819ded/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
-k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
-k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
-k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
-k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
-k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
-k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec=
-k8s.io/klog/v2 v2.30.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
-k8s.io/klog/v2 v2.40.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
-k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o=
-k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM=
-k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
-k8s.io/kube-openapi v0.0.0-20211109043538-20434351676c/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
-k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65/go.mod h1:sX9MT8g7NVZM5lVL/j8QyCCJe8YSMW30QvGZWaCIDIk=
-k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=
-k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
-k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
-k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
-k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
-k8s.io/utils v0.0.0-20211116205334-6203023598ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
-lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
-lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
-modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI=
-modernc.org/cc/v3 v3.36.2/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI=
-modernc.org/cc/v3 v3.36.3/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI=
-modernc.org/cc/v3 v3.37.0/go.mod h1:vtL+3mdHx/wcj3iEGz84rQa8vEqR6XM84v5Lcvfph20=
-modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0=
-modernc.org/ccgo/v3 v3.0.0-20220428102840-41399a37e894/go.mod h1:eI31LL8EwEBKPpNpA4bU1/i+sKOwOrQy8D87zWUcRZc=
-modernc.org/ccgo/v3 v3.0.0-20220430103911-bc99d88307be/go.mod h1:bwdAnOoaIt8Ax9YdWGjxWsdkPcZyRPHqrOvJxaKAKGw=
-modernc.org/ccgo/v3 v3.0.0-20220904174949-82d86e1b6d56/go.mod h1:YSXjPL62P2AMSxBphRHPn7IkzhVHqkvOnRKAKh+W6ZI=
-modernc.org/ccgo/v3 v3.16.4/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ=
-modernc.org/ccgo/v3 v3.16.6/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ=
-modernc.org/ccgo/v3 v3.16.8/go.mod h1:zNjwkizS+fIFDrDjIAgBSCLkWbJuHF+ar3QRn+Z9aws=
-modernc.org/ccgo/v3 v3.16.9/go.mod h1:zNMzC9A9xeNUepy6KuZBbugn3c0Mc9TeiJO4lgvkJDo=
-modernc.org/ccgo/v3 v3.16.13-0.20221017192402-261537637ce8/go.mod h1:fUB3Vn0nVPReA+7IG7yZDfjv1TMWjhQP8gCxrFAtL5g=
-modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY=
-modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
-modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
-modernc.org/libc v0.0.0-20220428101251-2d5f3daf273b/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA=
-modernc.org/libc v1.16.0/go.mod h1:N4LD6DBE9cf+Dzf9buBlzVJndKr/iJHG97vGLHYnb5A=
-modernc.org/libc v1.16.1/go.mod h1:JjJE0eu4yeK7tab2n4S1w8tlWd9MxXLRzheaRnAKymU=
-modernc.org/libc v1.16.17/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU=
-modernc.org/libc v1.16.19/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA=
-modernc.org/libc v1.17.0/go.mod h1:XsgLldpP4aWlPlsjqKRdHPqCxCjISdHfM/yeWC5GyW0=
-modernc.org/libc v1.17.1/go.mod h1:FZ23b+8LjxZs7XtFMbSzL/EhPxNbfZbErxEHc7cbD9s=
-modernc.org/libc v1.17.4/go.mod h1:WNg2ZH56rDEwdropAJeZPQkXmDwh+JCA1s/htl6r2fA=
-modernc.org/libc v1.18.0/go.mod h1:vj6zehR5bfc98ipowQOM2nIDUZnVew/wNC/2tOGS+q0=
-modernc.org/libc v1.20.3/go.mod h1:ZRfIaEkgrYgZDl6pa4W39HgN5G/yDW+NRmNKZBDFrk0=
-modernc.org/libc v1.21.4/go.mod h1:przBsL5RDOZajTVslkugzLBj1evTue36jEomFQOoYuI=
-modernc.org/libc v1.22.2/go.mod h1:uvQavJ1pZ0hIoC/jfqNoMLURIMhKzINIWypNM17puug=
-modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
-modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
-modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
-modernc.org/memory v1.1.1/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw=
-modernc.org/memory v1.2.0/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw=
-modernc.org/memory v1.2.1/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
-modernc.org/memory v1.3.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
-modernc.org/memory v1.4.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
-modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
-modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
-modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
-modernc.org/sqlite v1.18.1/go.mod h1:6ho+Gow7oX5V+OiOQ6Tr4xeqbx13UZ6t+Fw9IRUG4d4=
-modernc.org/sqlite v1.18.2/go.mod h1:kvrTLEWgxUcHa2GfHBQtanR1H9ht3hTJNtKpzH9k1u0=
-modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw=
-modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw=
-modernc.org/tcl v1.13.1/go.mod h1:XOLfOwzhkljL4itZkK6T72ckMgvj0BDsnKNdZVUOecw=
-modernc.org/tcl v1.13.2/go.mod h1:7CLiGIPo1M8Rv1Mitpv5akc2+8fxUd2y2UzC/MfMzy0=
-modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
-modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
-modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
-modernc.org/z v1.5.1/go.mod h1:eWFB510QWW5Th9YGZT81s+LwvaAs3Q2yr4sP0rmLkv8=
-rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
-rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
-rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
-rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
-sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg=
-sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg=
-sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.22/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg=
-sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6/go.mod h1:p4QtZmO4uMYipTQNzagwnNoseA6OxSUutVw05NhYDRs=
-sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
-sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
-sigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
-sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4=
-sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4=
-sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
-sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
diff --git a/hash/hash.go b/hash/hash.go
deleted file mode 100644
index 4aee98c..0000000
--- a/hash/hash.go
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2017 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.
-
-// Package hash provides a hash function used in goma.
-package hash
-
-import (
-	"crypto/hmac"
-	"crypto/sha256"
-	"encoding/hex"
-	"fmt"
-	"io"
-	"os"
-	"reflect"
-
-	"google.golang.org/protobuf/proto"
-)
-
-// SHA256HMAC returns a hexdecimal representation of the SHA256 hmac of the given two content.
-func SHA256HMAC(key []byte, data []byte) string {
-	m := hmac.New(sha256.New, key)
-	m.Write(data)
-	return hex.EncodeToString(m.Sum(nil))
-}
-
-// SHA256Content returns a hexdecimal representation of the SHA256 hash of the given content.
-func SHA256Content(b []byte) string {
-	h := sha256.Sum256(b)
-	return hex.EncodeToString(h[:])
-}
-
-// SHA256Proto returns a hexdecimal representation of the SHA256 hash of the given protocol buffer.
-func SHA256Proto(m proto.Message) (string, error) {
-	// github.com/golang/protobuf/proto's Marshal returned error for nil
-	// message, but google.golang.org/protobuf/proto returns nil err.
-	// To preserve behavior of SHA256Proto, check m is nil-pointer or not.
-	if !reflect.ValueOf(m).IsValid() {
-		return "", fmt.Errorf("nil %T", m)
-	}
-	b, err := proto.Marshal(m)
-	if err != nil {
-		return "", err
-	}
-	return SHA256Content(b), nil
-}
-
-// SHA256File returns a hexadecimal representation of the SHA256 hash of the file contents.
-func SHA256File(path string) (string, error) {
-	f, err := os.Open(path)
-	if err != nil {
-		return "", err
-	}
-	defer f.Close()
-
-	h := sha256.New()
-	if _, err := io.Copy(h, f); err != nil {
-		return "", err
-	}
-	return hex.EncodeToString(h.Sum(nil)), nil
-}
diff --git a/hash/hash_test.go b/hash/hash_test.go
deleted file mode 100644
index 324ad16..0000000
--- a/hash/hash_test.go
+++ /dev/null
@@ -1,108 +0,0 @@
-// Copyright 2017 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.
-
-package hash
-
-import (
-	"io/ioutil"
-	"os"
-	"testing"
-
-	pb "go.chromium.org/goma/server/proto/api"
-)
-
-func TestSHA256HMAC(t *testing.T) {
-	// https://tools.ietf.org/html/rfc4231
-	key := []byte("Jefe")
-	data := []byte("what do ya want for nothing?")
-	expected := "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843"
-	actual := SHA256HMAC(key, data)
-	if actual != expected {
-		t.Errorf("SHA256HMAC(%s, %s)=%s; want %s", key, data, actual, expected)
-	}
-}
-
-func TestSHA256Content(t *testing.T) {
-	b := []byte{}
-	hk := SHA256Content(b)
-	expectedHk := "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
-	if hk != expectedHk {
-		t.Errorf("SHA256Content(%q) = %q; want %q", b, hk, expectedHk)
-	}
-	b2 := byte('\n')
-	b3 := append([]byte(hk), b2)
-	hk2 := SHA256Content(b3)
-	expectedHk2 := "38acb15d02d5ac0f2a2789602e9df950c380d2799b4bdb59394e4eeabdd3a662"
-	if hk2 != expectedHk2 {
-		t.Errorf("SHA256Content(%q) = %q; want %q", b3, hk2, expectedHk2)
-	}
-}
-
-func TestSHA256Proto(t *testing.T) {
-	fb := &pb.FileBlob{
-		BlobType: pb.FileBlob_FILE.Enum(),
-	}
-	actual, err := SHA256Proto(fb)
-	if err != nil {
-		t.Errorf("SHA256Proto(%q) error returns %v; want nil", fb, err)
-	}
-	expected := "fb8da7eb5b1b399e7321179dac9e9f65773d7331e1e30554e3911e4325e1ef19"
-	if actual != expected {
-		t.Errorf("SHA256Proto(%q) return %v; want %v", fb, actual, expected)
-	}
-}
-
-func TestSHA256ProtoWithMarshalFailure(t *testing.T) {
-	_, err := SHA256Proto(nil)
-	if err == nil {
-		t.Errorf("SHA256Proto(nil) error is nil; want error")
-	}
-}
-
-func TestSHA256File(t *testing.T) {
-	tempDir, err := ioutil.TempDir(os.TempDir(), "sha256dir")
-	if err != nil {
-		t.Fatalf("couldn't make a temporary directory")
-	}
-	defer os.RemoveAll(tempDir)
-
-	tempFile, err := ioutil.TempFile(tempDir, "test")
-	if err != nil {
-		t.Fatalf("couldn't make a temporary file")
-	}
-
-	filename := tempFile.Name()
-
-	if err := tempFile.Close(); err != nil {
-		t.Fatalf("couldn't close a temporary file")
-	}
-
-	actual, err := SHA256File(filename)
-	if err != nil {
-		t.Errorf("SHA256File(%q) error returns %v; want nil", filename, err)
-	}
-
-	expected := "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
-	if actual != expected {
-		t.Errorf("SHA256File(%q) return %v; want %v", filename, actual, expected)
-	}
-}
-
-func TestSHA256FileWhenFileNotExists(t *testing.T) {
-	tempDir, err := ioutil.TempDir(os.TempDir(), "sha256dir")
-	if err != nil {
-		t.Fatalf("couldn't make a temporary directory")
-	}
-	defer os.RemoveAll(tempDir)
-
-	filename := "not-exist-file.txt"
-
-	_, err = SHA256File(filename)
-	if err == nil {
-		t.Errorf("SHA256File(%q) error is nil; want error", filename)
-	}
-	if !os.IsNotExist(err) {
-		t.Errorf("SHA256File(%q) error is %v; want ErrNotExist", filename, err)
-	}
-}
diff --git a/httprpc/admission.go b/httprpc/admission.go
deleted file mode 100644
index 5c89a4b..0000000
--- a/httprpc/admission.go
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2018 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.
-
-package httprpc
-
-import (
-	"net/http"
-
-	"go.chromium.org/goma/server/log"
-)
-
-// AdmissionController checks incoming request.
-type AdmissionController interface {
-	Admit(*http.Request) error
-}
-
-// AdmissionControl adds admission controller to h.
-func AdmissionControl(ac AdmissionController, h http.Handler) http.Handler {
-	if ac == nil {
-		return h
-	}
-	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
-		ctx := req.Context()
-		err := ac.Admit(req)
-		if err != nil {
-			code, msg := httpStatus(err)
-			http.Error(w, msg, code)
-			logger := log.FromContext(ctx)
-			logger.Errorf("deny %s: %d %s: %v", req.URL.Path, code, msg, err)
-			return
-		}
-		h.ServeHTTP(w, req)
-	})
-}
diff --git a/httprpc/auth.go b/httprpc/auth.go
deleted file mode 100644
index d527c5b..0000000
--- a/httprpc/auth.go
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2017 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.
-
-package httprpc
-
-import (
-	"context"
-	"fmt"
-	"net/http"
-
-	"go.chromium.org/goma/server/auth/enduser"
-	"go.chromium.org/goma/server/log"
-)
-
-// AuthChecker represents an interface to checks HTTP access.
-type AuthChecker interface {
-	// Check represents the function to check HTTP access.
-	// If the access is granted, it returns non-nil enduser.EndUser instance.
-	Check(context.Context, *http.Request) (*enduser.EndUser, error)
-}
-
-// AuthHandler converts given http.Handler to access controlled HTTP handler using AuthChecker.
-// Alternatives: WithAuth handler option if it requires retry with Unauthenticated error.
-func AuthHandler(a AuthChecker, h http.Handler) http.Handler {
-	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
-		ctx := req.Context()
-		u, err := a.Check(ctx, req)
-		if err != nil {
-			code := http.StatusUnauthorized
-			http.Error(w, fmt.Sprintf("auth failed %s: %v", RemoteAddr(req), err), code)
-			logger := log.FromContext(ctx)
-			logger.Errorf("auth error %s: %d %s: %v", req.URL.Path, code, http.StatusText(code), err)
-			return
-		}
-		req = req.WithContext(enduser.NewContext(ctx, u))
-		h.ServeHTTP(w, req)
-	})
-}
diff --git a/httprpc/auth_test.go b/httprpc/auth_test.go
deleted file mode 100644
index d75e501..0000000
--- a/httprpc/auth_test.go
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright 2017 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.
-
-package httprpc
-
-import (
-	"context"
-	"fmt"
-	"net/http"
-	"net/http/httptest"
-	"testing"
-
-	"golang.org/x/oauth2"
-	"google.golang.org/grpc"
-	"google.golang.org/grpc/codes"
-
-	"go.chromium.org/goma/server/auth/enduser"
-)
-
-type fakeAuth struct {
-	check func(ctx context.Context, req *http.Request) (*enduser.EndUser, error)
-}
-
-func (s fakeAuth) Check(ctx context.Context, req *http.Request) (*enduser.EndUser, error) {
-	return s.check(ctx, req)
-}
-
-func TestAuthHandler(t *testing.T) {
-	for _, tc := range []struct {
-		desc string
-		f    fakeAuth
-		want int
-	}{
-		{
-			desc: "AuthHandler should allow access on auth success",
-			f: fakeAuth{
-				check: func(ctx context.Context, req *http.Request) (*enduser.EndUser, error) {
-					return enduser.New("dummy@example.com", "dummy", &oauth2.Token{}), nil
-				},
-			},
-			want: http.StatusOK,
-		},
-		{
-			desc: "AuthHandler must deny access on auth fail",
-			f: fakeAuth{
-				check: func(ctx context.Context, req *http.Request) (*enduser.EndUser, error) {
-					return nil, grpc.Errorf(codes.PermissionDenied, "permission denied")
-				},
-			},
-			want: http.StatusUnauthorized,
-		},
-	} {
-		t.Run(tc.desc, func(t *testing.T) {
-			ts := httptest.NewServer(AuthHandler(tc.f, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-				fmt.Fprintln(w, "Hello, client")
-			})))
-			defer ts.Close()
-			res, err := http.Get(ts.URL)
-			if err != nil {
-				t.Fatal(err)
-			}
-			defer res.Body.Close()
-			if res.StatusCode != tc.want {
-				t.Errorf("res.StatusCode=%d; want %d", res.StatusCode, http.StatusOK)
-			}
-		})
-	}
-}
diff --git a/httprpc/authdb/authdb.go b/httprpc/authdb/authdb.go
deleted file mode 100644
index 05eea6e..0000000
--- a/httprpc/authdb/authdb.go
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2018 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.
-
-// Package authdb implements authdb service for goma httprpc.
-package authdb
-
-import (
-	"context"
-	"net/http"
-
-	"google.golang.org/protobuf/proto"
-
-	"go.chromium.org/goma/server/httprpc"
-	pb "go.chromium.org/goma/server/proto/auth"
-)
-
-func Handler(s pb.AuthDBServiceServer, opts ...httprpc.HandlerOption) http.Handler {
-	return httprpc.Handler(
-		"AuthDBSerrvice.CheckMembership",
-		&pb.CheckMembershipReq{}, &pb.CheckMembershipResp{},
-		func(ctx context.Context, req proto.Message) (proto.Message, error) {
-			resp, err := s.CheckMembership(ctx, req.(*pb.CheckMembershipReq))
-			return resp, err
-		}, opts...)
-}
-
-func Register(mux *http.ServeMux, s pb.AuthDBServiceServer, opts ...httprpc.HandlerOption) {
-	mux.Handle("/authdb/checkMembership", Handler(s, opts...))
-}
diff --git a/httprpc/bytestream/bytestream.go b/httprpc/bytestream/bytestream.go
deleted file mode 100644
index 202b81f..0000000
--- a/httprpc/bytestream/bytestream.go
+++ /dev/null
@@ -1,144 +0,0 @@
-// Copyright 2018 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.
-
-// Package bytestream implements bytestream for goma http.
-package bytestream
-
-import (
-	"compress/gzip"
-	"compress/zlib"
-	"context"
-	"io"
-	"net/http"
-	"path"
-	"strings"
-
-	pb "google.golang.org/genproto/googleapis/bytestream"
-	"google.golang.org/grpc/codes"
-	"google.golang.org/grpc/status"
-
-	"go.chromium.org/goma/server/bytestreamio"
-	"go.chromium.org/goma/server/httprpc"
-	"go.chromium.org/goma/server/log"
-)
-
-const bufsize = 2 * 1024 * 1024
-
-// Handler returns http.Handler to serve bytestream API.
-func Handler(c pb.ByteStreamClient, opts ...httprpc.HandlerOption) http.Handler {
-	return httprpc.StreamHandler(
-		"Bytestream",
-		func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
-			logger := log.FromContext(ctx)
-			switch r.Method {
-			case http.MethodGet:
-				return byteStreamGet(ctx, c, w, r)
-
-			case http.MethodPost:
-				return byteStreamPost(ctx, c, w, r)
-
-			case http.MethodHead:
-				return byteStreamHead(ctx, c, w, r)
-
-			}
-			http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
-			logger.Errorf("server error %s %s: %d %s", r.Method, r.URL.Path, http.StatusMethodNotAllowed, "method not allowed")
-			return nil
-		}, opts...)
-}
-
-func byteStreamResourceName(r *http.Request) string {
-	return strings.TrimLeft(path.Clean(r.URL.Path), "/")
-}
-
-func byteStreamGet(ctx context.Context, c pb.ByteStreamClient, w http.ResponseWriter, r *http.Request) error {
-	resname := byteStreamResourceName(r)
-
-	rd, err := bytestreamio.Open(ctx, c, resname)
-	if err != nil {
-		return err
-	}
-	buf := make([]byte, bufsize)
-	var wr io.Writer = w
-	switch {
-	case strings.Contains(r.Header.Get("Accept-Encoding"), "gzip"):
-		wr, err = gzip.NewWriterLevel(w, gzip.BestSpeed)
-		if err != nil {
-			return err
-		}
-		w.Header().Set("Content-Encoding", "gzip")
-
-	case strings.Contains(r.Header.Get("Accept-Encoding"), "deflate"):
-		// RFC7230 says deflate coding is "zlib" (RFC1950) containing
-		// "deflate" compressed data (RFC1951).
-		// but goma client just used "deflate" compressed data
-		// for "Content-Encoding: deflate" wrongly.
-		wr, err = zlib.NewWriterLevel(w, zlib.BestSpeed)
-		if err != nil {
-			return err
-		}
-		w.Header().Set("Content-Encoding", "deflate")
-	}
-	_, err = io.CopyBuffer(wr, rd, buf)
-	if err != nil {
-		return err
-	}
-	if c, ok := wr.(io.Closer); ok {
-		err = c.Close()
-	}
-	return err
-}
-
-func byteStreamPost(ctx context.Context, c pb.ByteStreamClient, w http.ResponseWriter, r *http.Request) error {
-	resname := byteStreamResourceName(r)
-	wr, err := bytestreamio.Create(ctx, c, resname)
-	if err != nil {
-		return err
-	}
-	buf := make([]byte, bufsize)
-	var rd io.Reader = r.Body
-	switch r.Header.Get("Content-Encoding") {
-	case "gzip":
-		gr, err := gzip.NewReader(r.Body)
-		if err != nil {
-			return status.Errorf(codes.InvalidArgument, "gzip error: %v", err)
-		}
-		defer gr.Close()
-		rd = gr
-
-	case "deflate":
-		// RFC7230 says deflate coding is "zlib" (RFC1950) containing
-		// "deflate" compressed data (RFC1951).
-		// but goma client just used "deflate" compressed data
-		// for "Content-Encoding: deflate" wrongly.
-		rd, err = zlib.NewReader(r.Body)
-		if err != nil {
-			return status.Errorf(codes.InvalidArgument, "zlib error: %v", err)
-		}
-	}
-	_, err = io.CopyBuffer(wr, rd, buf)
-	if err != nil {
-		wr.Close()
-		return err
-	}
-	err = wr.Close()
-	if err != nil {
-		return err
-	}
-	w.WriteHeader(http.StatusOK)
-	return nil
-}
-
-func byteStreamHead(ctx context.Context, c pb.ByteStreamClient, w http.ResponseWriter, r *http.Request) error {
-	resname := byteStreamResourceName(r)
-
-	err := bytestreamio.Exists(ctx, c, resname)
-	if err != nil {
-		return err
-	}
-	// client doesn't handle StatusOK well.
-	// got "connection closed before receiving all chunks".
-	w.WriteHeader(http.StatusNoContent)
-	return nil
-}
diff --git a/httprpc/bytestream/bytestream_test.go b/httprpc/bytestream/bytestream_test.go
deleted file mode 100644
index 07082c5..0000000
--- a/httprpc/bytestream/bytestream_test.go
+++ /dev/null
@@ -1,509 +0,0 @@
-// Copyright 2018 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.
-
-package bytestream
-
-import (
-	"bytes"
-	"compress/gzip"
-	"compress/zlib"
-	"context"
-	"io"
-	"io/ioutil"
-	"net/http"
-	"net/http/httptest"
-	"strings"
-	"testing"
-
-	pb "google.golang.org/genproto/googleapis/bytestream"
-	"google.golang.org/grpc"
-	"google.golang.org/grpc/codes"
-	"google.golang.org/grpc/status"
-)
-
-type fakeByteStreamClient struct {
-	pb.ByteStreamClient
-	m map[string]string
-}
-
-func (c fakeByteStreamClient) Read(ctx context.Context, req *pb.ReadRequest, opts ...grpc.CallOption) (pb.ByteStream_ReadClient, error) {
-	v, ok := c.m[req.ResourceName]
-	if !ok {
-		return nil, status.Errorf(codes.NotFound, "%s is not found", req.ResourceName)
-	}
-	if req.ReadOffset >= int64(len(v)) {
-		return nil, status.Errorf(codes.OutOfRange, "out of range %d > %d", req.ReadOffset, len(v))
-	}
-	v = v[req.ReadOffset:]
-	if req.ReadLimit < 0 {
-		return nil, status.Errorf(codes.InvalidArgument, "negative limit %d", req.ReadLimit)
-	}
-	if req.ReadLimit > 0 && int64(len(v)) > req.ReadLimit {
-		v = v[:req.ReadLimit]
-	}
-	return &fakeByteStreamReadClient{
-		c:    c,
-		data: v,
-	}, nil
-}
-
-func (c fakeByteStreamClient) Write(ctx context.Context, opts ...grpc.CallOption) (pb.ByteStream_WriteClient, error) {
-	return &fakeByteStreamWriteClient{
-		c: c,
-	}, nil
-}
-
-type fakeByteStreamReadClient struct {
-	pb.ByteStream_ReadClient
-	c    fakeByteStreamClient
-	data string
-}
-
-func (c *fakeByteStreamReadClient) Recv() (*pb.ReadResponse, error) {
-	if len(c.data) == 0 {
-		return nil, io.EOF
-	}
-	const chunksize = 16
-	v := c.data
-	if len(v) > chunksize {
-		v = v[:chunksize]
-	}
-	c.data = c.data[len(v):]
-	return &pb.ReadResponse{
-		Data: []byte(v),
-	}, nil
-}
-
-type fakeByteStreamWriteClient struct {
-	pb.ByteStream_WriteClient
-	c fakeByteStreamClient
-
-	resname string
-	buf     *bytes.Buffer
-	finish  bool
-}
-
-func (c *fakeByteStreamWriteClient) Send(req *pb.WriteRequest) error {
-	if c.buf == nil {
-		c.buf = bytes.NewBuffer(nil)
-		c.resname = req.ResourceName
-		if c.resname == "" {
-			return status.Errorf(codes.InvalidArgument, "empty resname")
-		}
-	} else if req.ResourceName != "" && req.ResourceName != c.resname {
-		return status.Errorf(codes.InvalidArgument, "resname mismatch %s; want=%s", req.ResourceName, c.resname)
-	}
-	_, ok := c.c.m[c.resname]
-	if ok {
-		return io.EOF
-	}
-	if req.WriteOffset != int64(c.buf.Len()) {
-		return status.Errorf(codes.InvalidArgument, "incorrect offset %d; want=%d", req.WriteOffset, c.buf.Len())
-	}
-	if c.finish {
-		return status.Errorf(codes.FailedPrecondition, "write to already finished resource")
-	}
-	c.finish = req.FinishWrite
-	c.buf.Write(req.Data)
-	return nil
-}
-
-func (c *fakeByteStreamWriteClient) CloseAndRecv() (*pb.WriteResponse, error) {
-	if !c.finish {
-		return nil, status.Errorf(codes.FailedPrecondition, "write not finished")
-	}
-	c.c.m[c.resname] = c.buf.String()
-	return &pb.WriteResponse{
-		CommittedSize: int64(c.buf.Len()),
-	}, nil
-}
-
-func TestGet(t *testing.T) {
-	const resname = `blobs/hash/size`
-	const data = `blob data`
-	c := fakeByteStreamClient{
-		m: map[string]string{
-			resname: data,
-		},
-	}
-	handler := Handler(c)
-	s := httptest.NewServer(handler)
-	defer s.Close()
-
-	req, err := http.NewRequest("GET", s.URL+"/"+resname, nil)
-	if err != nil {
-		t.Fatal(err)
-	}
-	resp, err := http.DefaultClient.Do(req)
-	if err != nil {
-		t.Fatal(err)
-	}
-	defer resp.Body.Close()
-	if resp.StatusCode != http.StatusOK {
-		t.Errorf("GET %s=%d %s; want=%d", resname, resp.StatusCode, resp.Status, http.StatusOK)
-	}
-	buf, err := ioutil.ReadAll(resp.Body)
-	if err != nil {
-		t.Fatal(err)
-	}
-	if got, want := string(buf), data; got != want {
-		t.Errorf("GET %s=%q; want=%q", resname, got, want)
-	}
-}
-
-func TestGetDeflate(t *testing.T) {
-	const resname = `blobs/hash/size`
-	const data = `blob data`
-	c := fakeByteStreamClient{
-		m: map[string]string{
-			resname: data,
-		},
-	}
-	handler := Handler(c)
-	s := httptest.NewServer(handler)
-	defer s.Close()
-
-	req, err := http.NewRequest("GET", s.URL+"/"+resname, nil)
-	if err != nil {
-		t.Fatal(err)
-	}
-	req.Header.Set("Accept-Encoding", "deflate")
-	resp, err := http.DefaultClient.Do(req)
-	if err != nil {
-		t.Fatal(err)
-	}
-	defer resp.Body.Close()
-	if resp.StatusCode != http.StatusOK {
-		t.Errorf("GET %s=%d %s; want=%d", resname, resp.StatusCode, resp.Status, http.StatusOK)
-	}
-	if got, want := resp.Header.Get("Content-Encoding"), "deflate"; got != want {
-		t.Errorf("GET %s: content-encoding=%s; want=%s", resname, got, want)
-	}
-	rawbuf, err := ioutil.ReadAll(resp.Body)
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	if got, want := string(rawbuf), data; got == want {
-		t.Errorf("GET %s=%q; should be compressed with deflate of %q", resname, got, want)
-	}
-	r, err := zlib.NewReader(bytes.NewReader(rawbuf))
-	if err != nil {
-		t.Errorf("GET %s; zlib %v", resname, err)
-	}
-	buf, err := ioutil.ReadAll(r)
-	if err != nil {
-		t.Fatalf("deflate %q=%q: %v", rawbuf, string(buf), err)
-	}
-	if got, want := string(buf), data; got != want {
-		t.Errorf("GET %s=%q; want=%q", resname, got, want)
-	}
-}
-
-func TestGetGzip(t *testing.T) {
-	const resname = `blobs/hash/size`
-	const data = `blob data`
-	c := fakeByteStreamClient{
-		m: map[string]string{
-			resname: data,
-		},
-	}
-	handler := Handler(c)
-	s := httptest.NewServer(handler)
-	defer s.Close()
-
-	req, err := http.NewRequest("GET", s.URL+"/"+resname, nil)
-	if err != nil {
-		t.Fatal(err)
-	}
-	req.Header.Set("Accept-Encoding", "gzip")
-	resp, err := http.DefaultClient.Do(req)
-	if err != nil {
-		t.Fatal(err)
-	}
-	defer resp.Body.Close()
-	if resp.StatusCode != http.StatusOK {
-		t.Errorf("GET %s=%d %s; want=%d", resname, resp.StatusCode, resp.Status, http.StatusOK)
-	}
-	if got, want := resp.Header.Get("Content-Encoding"), "gzip"; got != want {
-		t.Errorf("GET %s: content-encoding=%s; want=%s", resname, got, want)
-	}
-	rawbuf, err := ioutil.ReadAll(resp.Body)
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	if got, want := string(rawbuf), data; got == want {
-		t.Errorf("GET %s=%q; should be compressed with gzip of %q", resname, got, want)
-	}
-	r, err := gzip.NewReader(bytes.NewReader(rawbuf))
-	if err != nil {
-		t.Errorf("GET %s; gzip %v", resname, err)
-	}
-	buf, err := ioutil.ReadAll(r)
-	if err != nil {
-		t.Fatalf("gzip %q=%q: %v", rawbuf, string(buf), err)
-	}
-	if got, want := string(buf), data; got != want {
-		t.Errorf("GET %s=%q; want=%q", resname, got, want)
-	}
-}
-
-func TestGetNotFound(t *testing.T) {
-	const resname = `blobs/hash/size`
-	c := fakeByteStreamClient{
-		m: map[string]string{},
-	}
-	handler := Handler(c)
-	s := httptest.NewServer(handler)
-	defer s.Close()
-
-	req, err := http.NewRequest("GET", s.URL+"/"+resname, nil)
-	if err != nil {
-		t.Fatal(err)
-	}
-	resp, err := http.DefaultClient.Do(req)
-	if err != nil {
-		t.Fatal(err)
-	}
-	defer resp.Body.Close()
-	if resp.StatusCode != http.StatusNotFound {
-		t.Errorf("GET %s=%d %s; want=%d", resname, resp.StatusCode, resp.Status, http.StatusNotFound)
-	}
-}
-
-func TestHead(t *testing.T) {
-	const resname = `blobs/hash/size`
-	const data = `blob data`
-	c := fakeByteStreamClient{
-		m: map[string]string{
-			resname: data,
-		},
-	}
-	handler := Handler(c)
-	s := httptest.NewServer(handler)
-	defer s.Close()
-
-	req, err := http.NewRequest("HEAD", s.URL+"/"+resname, nil)
-	if err != nil {
-		t.Fatal(err)
-	}
-	resp, err := http.DefaultClient.Do(req)
-	if err != nil {
-		t.Fatal(err)
-	}
-	defer resp.Body.Close()
-	if resp.StatusCode != http.StatusNoContent {
-		t.Errorf("HEAD %s=%d %s; want=%d", resname, resp.StatusCode, resp.Status, http.StatusNoContent)
-	}
-	buf, err := ioutil.ReadAll(resp.Body)
-	if err != nil {
-		t.Fatal(err)
-	}
-	if got, want := string(buf), ""; got != want {
-		t.Errorf("HEAD %s=%q; want=%q", resname, got, want)
-	}
-}
-
-func TestHeadNotFound(t *testing.T) {
-	const resname = `blobs/hash/size`
-	c := fakeByteStreamClient{
-		m: map[string]string{},
-	}
-	handler := Handler(c)
-	s := httptest.NewServer(handler)
-	defer s.Close()
-
-	req, err := http.NewRequest("HEAD", s.URL+"/"+resname, nil)
-	if err != nil {
-		t.Fatal(err)
-	}
-	resp, err := http.DefaultClient.Do(req)
-	if err != nil {
-		t.Fatal(err)
-	}
-	defer resp.Body.Close()
-	if resp.StatusCode != http.StatusNotFound {
-		t.Errorf("HEAD %s=%d %s; want=%d", resname, resp.StatusCode, resp.Status, http.StatusNoContent)
-	}
-}
-
-func TestPost(t *testing.T) {
-	const resname = `blobs/hash/size`
-	const data = `blob data`
-	c := fakeByteStreamClient{
-		m: map[string]string{},
-	}
-	handler := Handler(c)
-	s := httptest.NewServer(handler)
-	defer s.Close()
-
-	req, err := http.NewRequest("POST", s.URL+"/"+resname, strings.NewReader(data))
-	if err != nil {
-		t.Fatal(err)
-	}
-	resp, err := http.DefaultClient.Do(req)
-	if err != nil {
-		t.Fatal(err)
-	}
-	defer resp.Body.Close()
-	if resp.StatusCode != http.StatusOK {
-		t.Errorf("POST %s=%d %s; want=%d", resname, resp.StatusCode, resp.Status, http.StatusOK)
-	}
-	got, ok := c.m[resname]
-	if !ok {
-		t.Errorf("POST %s didn't create data", resname)
-	}
-	if got != data {
-		t.Errorf("POST %s=%q; want=%q", resname, got, data)
-	}
-}
-
-func TestPostDeflate(t *testing.T) {
-	const resname = `blobs/hash/size`
-	const data = `blob data`
-	c := fakeByteStreamClient{
-		m: map[string]string{},
-	}
-	handler := Handler(c)
-	s := httptest.NewServer(handler)
-	defer s.Close()
-
-	var buf bytes.Buffer
-	w, err := zlib.NewWriterLevel(&buf, zlib.BestSpeed)
-	if err != nil {
-		t.Fatal(err)
-	}
-	_, err = w.Write([]byte(data))
-	if err != nil {
-		t.Fatal(err)
-	}
-	err = w.Close()
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	req, err := http.NewRequest("POST", s.URL+"/"+resname, &buf)
-	if err != nil {
-		t.Fatal(err)
-	}
-	req.Header.Set("Content-Encoding", "deflate")
-	resp, err := http.DefaultClient.Do(req)
-	if err != nil {
-		t.Fatal(err)
-	}
-	defer resp.Body.Close()
-	if resp.StatusCode != http.StatusOK {
-		t.Errorf("POST %s=%d %s; want=%d", resname, resp.StatusCode, resp.Status, http.StatusOK)
-	}
-	got, ok := c.m[resname]
-	if !ok {
-		t.Errorf("POST %s didn't create data", resname)
-	}
-	if got != data {
-		t.Errorf("POST %s=%q; want=%q", resname, got, data)
-	}
-}
-
-func TestPostGzip(t *testing.T) {
-	const resname = `blobs/hash/size`
-	const data = `blob data`
-	c := fakeByteStreamClient{
-		m: map[string]string{},
-	}
-	handler := Handler(c)
-	s := httptest.NewServer(handler)
-	defer s.Close()
-
-	var buf bytes.Buffer
-	w, err := gzip.NewWriterLevel(&buf, gzip.BestSpeed)
-	if err != nil {
-		t.Fatal(err)
-	}
-	_, err = w.Write([]byte(data))
-	if err != nil {
-		t.Fatal(err)
-	}
-	err = w.Close()
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	req, err := http.NewRequest("POST", s.URL+"/"+resname, &buf)
-	if err != nil {
-		t.Fatal(err)
-	}
-	req.Header.Set("Content-Encoding", "gzip")
-	resp, err := http.DefaultClient.Do(req)
-	if err != nil {
-		t.Fatal(err)
-	}
-	defer resp.Body.Close()
-	if resp.StatusCode != http.StatusOK {
-		t.Errorf("POST %s=%d %s; want=%d", resname, resp.StatusCode, resp.Status, http.StatusOK)
-	}
-	got, ok := c.m[resname]
-	if !ok {
-		t.Errorf("POST %s didn't create data", resname)
-	}
-	if got != data {
-		t.Errorf("POST %s=%q; want=%q", resname, got, data)
-	}
-}
-
-func TestPostAlreadyExists(t *testing.T) {
-	const resname = `blobs/hash/size`
-	const data = `blob data`
-	c := fakeByteStreamClient{
-		m: map[string]string{
-			resname: data,
-		},
-	}
-	handler := Handler(c)
-	s := httptest.NewServer(handler)
-	defer s.Close()
-
-	req, err := http.NewRequest("POST", s.URL+"/"+resname, strings.NewReader(data))
-	if err != nil {
-		t.Fatal(err)
-	}
-	resp, err := http.DefaultClient.Do(req)
-	if err != nil {
-		t.Fatal(err)
-	}
-	defer resp.Body.Close()
-	if resp.StatusCode != http.StatusOK {
-		t.Errorf("POST %s=%d %s; want=%d", resname, resp.StatusCode, resp.Status, http.StatusOK)
-	}
-	got, ok := c.m[resname]
-	if !ok {
-		t.Errorf("POST %s didn't create data", resname)
-	}
-	if got != data {
-		t.Errorf("POST %s=%q; want=%q", resname, got, data)
-	}
-}
-
-func TestBadMethod(t *testing.T) {
-	const resname = `blobs/hash/size`
-	c := fakeByteStreamClient{
-		m: map[string]string{},
-	}
-	handler := Handler(c)
-	s := httptest.NewServer(handler)
-	defer s.Close()
-
-	req, err := http.NewRequest("PUT", s.URL+"/"+resname, nil)
-	if err != nil {
-		t.Fatal(err)
-	}
-	resp, err := http.DefaultClient.Do(req)
-	if err != nil {
-		t.Fatal(err)
-	}
-	defer resp.Body.Close()
-	if resp.StatusCode != http.StatusMethodNotAllowed {
-		t.Errorf("PUT %s=%d %s; want=%d", resname, resp.StatusCode, resp.Status, http.StatusMethodNotAllowed)
-	}
-}
diff --git a/httprpc/client.go b/httprpc/client.go
deleted file mode 100644
index fb18ed9..0000000
--- a/httprpc/client.go
+++ /dev/null
@@ -1,214 +0,0 @@
-// Copyright 2017 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.
-
-package httprpc
-
-import (
-	"bytes"
-	"compress/flate"
-	"compress/gzip"
-	"context"
-	"io"
-	"io/ioutil"
-	"net/http"
-
-	"go.opencensus.io/trace"
-	"google.golang.org/grpc/codes"
-	"google.golang.org/grpc/status"
-	"google.golang.org/protobuf/proto"
-)
-
-// Client is httprpc client.
-type Client struct {
-	*http.Client
-
-	// endpoint URL.
-	URL string
-
-	// ContentEncoding of the request. "identity", "gzip" or "deflate".
-	// "deflate" uses "deflate" compressed data (RFC1951) without
-	// zlib header, different from RFC7230 says, for histrical reason.
-	// default is "deflate" for backward compatibility.
-	// TODO: change default to gzip?
-	ContentEncoding string
-}
-
-func serializeToHTTPRequest(ctx context.Context, url string, req proto.Message, contentEncoding string) (*http.Request, error) {
-	ctx, span := trace.StartSpan(ctx, "go.chromium.org/goma/server/httprpc.serializedToHTTPRequest")
-	defer span.End()
-	reqMsg, err := proto.Marshal(req)
-	if err != nil {
-		return nil, err
-	}
-	buf := bytes.NewBuffer(nil)
-	switch contentEncoding {
-	case "identity":
-		buf.Write(reqMsg)
-
-	case "deflate":
-		// RFC7230 says deflate coding is "zlib" (RFC1950) containing
-		// "deflate" compressed data (RFC1951).
-		// but goma client just used "deflate" compressed data
-		// for "Content-Encoding: deflate" wrongly.
-		// TODO: use gzip
-		w, err := flate.NewWriter(buf, flate.BestSpeed)
-		if err != nil {
-			return nil, err
-		}
-		_, err = w.Write(reqMsg)
-		if err != nil {
-			return nil, err
-		}
-		err = w.Close()
-		if err != nil {
-			return nil, err
-		}
-
-	case "gzip":
-		w, err := gzip.NewWriterLevel(buf, gzip.BestSpeed)
-		if err != nil {
-			return nil, err
-		}
-		_, err = w.Write(reqMsg)
-		if err != nil {
-			return nil, err
-		}
-		err = w.Close()
-		if err != nil {
-			return nil, err
-		}
-	default:
-		return nil, status.Errorf(codes.InvalidArgument, "unsupported content-encoding: %s", contentEncoding)
-	}
-
-	len := int64(len(buf.Bytes()))
-	post, err := http.NewRequest("POST", url, bytes.NewReader(buf.Bytes()))
-	if err != nil {
-		return nil, err
-	}
-	post = post.WithContext(ctx)
-	post.Header.Set("Content-Type", "binary/x-protocol-buffer")
-	post.ContentLength = len
-	post.Header.Set("Accept-Encoding", "gzip, deflate")
-	post.Header.Set("Content-Encoding", contentEncoding)
-	return post, nil
-}
-
-func fromHTTPStatus(code int) codes.Code {
-	// go/http-canonical-mapping
-	if code >= http.StatusOK && code < http.StatusMultipleChoices {
-		return codes.OK
-	}
-	if code >= http.StatusMultipleChoices && code < http.StatusBadRequest {
-		return codes.Unknown
-	}
-	switch code {
-	case http.StatusBadRequest:
-		return codes.InvalidArgument
-	case http.StatusUnauthorized:
-		return codes.Unauthenticated
-	case http.StatusForbidden:
-		return codes.PermissionDenied
-	case http.StatusNotFound:
-		return codes.NotFound
-	case http.StatusConflict:
-		return codes.Aborted
-	case http.StatusRequestedRangeNotSatisfiable:
-		return codes.OutOfRange
-	case http.StatusTooManyRequests:
-		return codes.ResourceExhausted
-	case 499: // Client closed request
-		return codes.Canceled
-	case http.StatusNotImplemented:
-		return codes.Unimplemented
-	case http.StatusServiceUnavailable:
-		return codes.Unavailable
-	case http.StatusGatewayTimeout:
-		return codes.DeadlineExceeded
-	}
-	if code >= http.StatusBadRequest && code < http.StatusInternalServerError {
-		return codes.FailedPrecondition
-	}
-	if code >= http.StatusInternalServerError {
-		return codes.Internal
-	}
-	return codes.Unknown
-}
-
-func parseFromHTTPResponse(ctx context.Context, response *http.Response, resp proto.Message) error {
-	defer response.Body.Close()
-	ctx, span := trace.StartSpan(ctx, "go.chromium.org/goma/server/httprpc.parseFromHTTPResponse")
-	defer span.End()
-	span.AddAttributes(trace.Int64Attribute("status_code", int64(response.StatusCode)))
-	if response.StatusCode != 200 {
-		span.AddAttributes(trace.StringAttribute("goma_error", response.Header.Get("X-Goma-Error")))
-		b, err := ioutil.ReadAll(response.Body)
-
-		return status.Errorf(fromHTTPStatus(response.StatusCode), "%d: %s: %s: %v", response.StatusCode, response.Header.Get("X-Goma-Error"), string(b), err)
-	}
-	var r io.Reader = response.Body
-	switch response.Header.Get("Content-Encoding") {
-	case "identity", "":
-
-	case "gzip":
-		var err error
-		r, err = gzip.NewReader(response.Body)
-		if err != nil {
-			return err
-		}
-	case "deflate":
-		// RFC7230 says deflate coding is "zlib" (RFC1950) containing
-		// "deflate" compressed data (RFC1951).
-		// but goma client just used "deflate" compressed data
-		// for "Content-Encoding: deflate" wrongly.
-		r = flate.NewReader(response.Body)
-	default:
-		return status.Errorf(codes.InvalidArgument, "unknown content-encoding: %s", response.Header.Get("Content-Encoding"))
-	}
-	respMsg, err := ioutil.ReadAll(r)
-	if err != nil {
-		return err
-	}
-	err = proto.Unmarshal(respMsg, resp)
-	if err != nil {
-		return err
-	}
-	return nil
-}
-
-// Call calls remote services over http.
-func (c *Client) Call(ctx context.Context, req proto.Message, resp proto.Message) error {
-	client := c.Client
-	if client == nil {
-		client = http.DefaultClient
-	}
-	ctx, span := trace.StartSpan(ctx, "go.chromium.org/goma/server/httprpc.Client.Call")
-	defer span.End()
-	contentEncoding := c.ContentEncoding
-	if contentEncoding == "" {
-		contentEncoding = "deflate"
-	}
-	post, err := serializeToHTTPRequest(ctx, c.URL, req, contentEncoding)
-	if err != nil {
-		return err
-	}
-	response, err := client.Do(post)
-	if err != nil {
-		return err
-	}
-	err = parseFromHTTPResponse(ctx, response, resp)
-	if err != nil {
-		return err
-	}
-	return nil
-}
-
-// Call calls remote services over http.
-func Call(ctx context.Context, client *http.Client, url string, req proto.Message, resp proto.Message) error {
-	c := Client{
-		Client: client,
-		URL:    url,
-	}
-	return c.Call(ctx, req, resp)
-}
diff --git a/httprpc/client_test.go b/httprpc/client_test.go
deleted file mode 100644
index 584bf2c..0000000
--- a/httprpc/client_test.go
+++ /dev/null
@@ -1,88 +0,0 @@
-// Copyright 2018 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.
-
-package httprpc
-
-import (
-	"net/http"
-	"testing"
-
-	"google.golang.org/grpc/codes"
-	"google.golang.org/grpc/status"
-)
-
-func TestCanonicalCode(t *testing.T) {
-	// go/http-canonical-mapping
-
-	// make sure grpc status code round back.
-	// commented out codes don't have 1:1 mapping.
-	for _, code := range []codes.Code{
-		codes.OK,
-		codes.Canceled,
-		// codes.Unknown,
-		codes.InvalidArgument,
-		codes.DeadlineExceeded,
-		codes.NotFound,
-		// codes.AlreadyExists,
-		codes.PermissionDenied,
-		codes.ResourceExhausted,
-		// codes.FailedPrecondition,
-		codes.Aborted,
-		// codes.OutOfRange,
-		codes.Unimplemented,
-		codes.Internal,
-		codes.Unavailable,
-		// codes.DataLoss,
-		// codes.Unauthenticated,
-	} {
-		hc, _ := httpStatus(status.Errorf(code, "%v", code))
-		got := fromHTTPStatus(hc)
-		if got != code {
-			t.Errorf("%d %s != %d %s (via %d %s)", got, got, code, code, hc, http.StatusText(hc))
-		}
-	}
-
-	for _, tc := range []struct {
-		from, to codes.Code
-		hc       int
-	}{
-		{
-			from: codes.Unknown,
-			to:   codes.Internal,
-			hc:   http.StatusInternalServerError,
-		},
-		{
-			from: codes.AlreadyExists,
-			to:   codes.Aborted,
-			hc:   http.StatusConflict,
-		},
-		{
-			from: codes.FailedPrecondition,
-			to:   codes.InvalidArgument,
-			hc:   http.StatusBadRequest,
-		},
-		{
-			from: codes.OutOfRange,
-			to:   codes.InvalidArgument,
-			hc:   http.StatusBadRequest,
-		},
-		{
-			from: codes.DataLoss,
-			to:   codes.Internal,
-			hc:   http.StatusInternalServerError,
-		},
-		{
-			// goma specific.
-			from: codes.Unauthenticated,
-			to:   codes.Internal,
-			hc:   http.StatusInternalServerError,
-		},
-	} {
-		gothc, _ := httpStatus(status.Errorf(tc.from, "%v", tc.from))
-		got := fromHTTPStatus(gothc)
-		if gothc != tc.hc || got != tc.to {
-			t.Errorf("%d %s => (%d %s) => %d %s; want (%d %s) => %d %s", tc.from, tc.from, gothc, http.StatusText(gothc), got, got, tc.hc, http.StatusText(tc.hc), tc.to, tc.to)
-		}
-	}
-}
diff --git a/httprpc/doc.go b/httprpc/doc.go
deleted file mode 100644
index ecb1840..0000000
--- a/httprpc/doc.go
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2017 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.
-
-/*
-Package httprpc provides goma httprpc implementation.
-*/
-package httprpc
-
-/*
-TODO:
- panic handling in server?
-
-*/
diff --git a/httprpc/e2e_test.go b/httprpc/e2e_test.go
deleted file mode 100644
index af739d4..0000000
--- a/httprpc/e2e_test.go
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright 2017 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.
-
-package httprpc_test
-
-import (
-	"context"
-	"crypto/tls"
-	"net/http"
-	"net/http/httptest"
-	"testing"
-
-	"google.golang.org/protobuf/proto"
-
-	"go.chromium.org/goma/server/httprpc"
-
-	pb "go.chromium.org/goma/server/proto/settings"
-)
-
-func TestHandlerAndClient(t *testing.T) {
-	for _, contentEncoding := range []string{"identity", "deflate", "gzip"} {
-		t.Run(contentEncoding, func(t *testing.T) {
-			resp := &pb.SettingsResp{
-				Settings: &pb.Settings{
-					Name: "test",
-				},
-			}
-			req := &pb.SettingsReq{
-				UseCase: "test",
-			}
-
-			h := httprpc.Handler("test", &pb.SettingsReq{}, &pb.SettingsResp{},
-				func(ctx context.Context, r proto.Message) (proto.Message, error) {
-					if !proto.Equal(r, req) {
-						t.Errorf("handler req=%#v; want=%#v", r, req)
-					}
-					return resp, nil
-				})
-
-			mux := http.NewServeMux()
-			mux.Handle("/settings", h)
-			s := httptest.NewTLSServer(mux)
-			defer s.Close()
-
-			ctx := context.Background()
-			gotResp := &pb.SettingsResp{}
-
-			client := &httprpc.Client{
-				Client: &http.Client{
-					Transport: &http.Transport{
-						TLSClientConfig: &tls.Config{
-							InsecureSkipVerify: true,
-						},
-					},
-				},
-				URL:             s.URL + "/settings",
-				ContentEncoding: contentEncoding,
-			}
-
-			err := client.Call(ctx, req, gotResp)
-			if err != nil {
-				t.Errorf("httprpc.Call=%v; want nil err", err)
-			}
-			if !proto.Equal(gotResp, resp) {
-				t.Errorf("httprpc.Call=%#v; want=%#v", gotResp, resp)
-			}
-			if client.ContentEncoding != contentEncoding {
-				t.Errorf("client.ContentEncoding=%q; want=%q", client.ContentEncoding, contentEncoding)
-			}
-		})
-	}
-}
diff --git a/httprpc/exec/exec.go b/httprpc/exec/exec.go
deleted file mode 100644
index 38a529c..0000000
--- a/httprpc/exec/exec.go
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2017 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.
-
-// Package exec implements exec service for goma httprpc.
-package exec
-
-// TODO: go generate from proto?
-
-import (
-	"context"
-	"net/http"
-
-	"google.golang.org/protobuf/proto"
-
-	"go.chromium.org/goma/server/httprpc"
-	pb "go.chromium.org/goma/server/proto/api"
-	execpb "go.chromium.org/goma/server/proto/exec"
-)
-
-// Handler returns exec service handler.
-func Handler(s execpb.ExecServiceServer, opts ...httprpc.HandlerOption) http.Handler {
-	return httprpc.Handler(
-		"ExecService.Exec",
-		&pb.ExecReq{}, &pb.ExecResp{},
-		func(ctx context.Context, req proto.Message) (proto.Message, error) {
-			resp, err := s.Exec(ctx, req.(*pb.ExecReq))
-			return resp, err
-		}, opts...)
-}
diff --git a/httprpc/execlog/execlog.go b/httprpc/execlog/execlog.go
deleted file mode 100644
index c380fc8..0000000
--- a/httprpc/execlog/execlog.go
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2017 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.
-
-// Package execlog implements log service for goma httprpc.
-package execlog
-
-// TODO: go generate from proto?
-
-import (
-	"context"
-	"net/http"
-
-	"google.golang.org/protobuf/proto"
-
-	"go.chromium.org/goma/server/httprpc"
-	pb "go.chromium.org/goma/server/proto/api"
-	execlogpb "go.chromium.org/goma/server/proto/execlog"
-)
-
-// Handler returns execlog service handler.
-func Handler(s execlogpb.LogServiceServer, opts ...httprpc.HandlerOption) http.Handler {
-	return httprpc.Handler(
-		"LogService.SaveLog",
-		&pb.SaveLogReq{}, &pb.SaveLogResp{},
-		func(ctx context.Context, req proto.Message) (proto.Message, error) {
-			resp, err := s.SaveLog(ctx, req.(*pb.SaveLogReq))
-			return resp, err
-		}, opts...)
-}
diff --git a/httprpc/file/file.go b/httprpc/file/file.go
deleted file mode 100644
index 76c845f..0000000
--- a/httprpc/file/file.go
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2017 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.
-
-// Package file implements file service for goma httprpc.
-package file
-
-// TODO: go generate from proto?
-
-import (
-	"context"
-	"net/http"
-
-	"google.golang.org/protobuf/proto"
-
-	"go.chromium.org/goma/server/httprpc"
-	pb "go.chromium.org/goma/server/proto/api"
-	filepb "go.chromium.org/goma/server/proto/file"
-)
-
-// StoreHandler returns file service StoreFile handler.
-func StoreHandler(s filepb.FileServiceServer, opts ...httprpc.HandlerOption) http.Handler {
-	return httprpc.Handler(
-		"FileService.StoreFile",
-		&pb.StoreFileReq{}, &pb.StoreFileResp{},
-		func(ctx context.Context, req proto.Message) (proto.Message, error) {
-			resp, err := s.StoreFile(ctx, req.(*pb.StoreFileReq))
-			return resp, err
-		}, opts...)
-}
-
-// LookupHandler returns file service LookupFile handler.
-func LookupHandler(s filepb.FileServiceServer, opts ...httprpc.HandlerOption) http.Handler {
-	return httprpc.Handler(
-		"FileService.LookupFile",
-		&pb.LookupFileReq{}, &pb.LookupFileResp{},
-		func(ctx context.Context, req proto.Message) (proto.Message, error) {
-			resp, err := s.LookupFile(ctx, req.(*pb.LookupFileReq))
-			return resp, err
-		}, opts...)
-}
diff --git a/httprpc/server.go b/httprpc/server.go
deleted file mode 100644
index c8a4a6b..0000000
--- a/httprpc/server.go
+++ /dev/null
@@ -1,457 +0,0 @@
-// Copyright 2017 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.
-
-package httprpc
-
-import (
-	"compress/flate"
-	"compress/gzip"
-	"context"
-	"fmt"
-	"io"
-	"io/ioutil"
-	"net/http"
-	"strings"
-	"time"
-
-	"go.opencensus.io/trace"
-	"google.golang.org/grpc"
-	"google.golang.org/grpc/codes"
-	"google.golang.org/grpc/metadata"
-	"google.golang.org/grpc/status"
-	"google.golang.org/protobuf/proto"
-
-	"go.chromium.org/goma/server/log"
-	"go.chromium.org/goma/server/rpc"
-)
-
-var (
-	deflateCompressionLevel = flate.BestSpeed
-	gzipCompressionLevel    = gzip.BestSpeed
-)
-
-const (
-	httpHeader = `X-Cloud-Trace-Context`
-)
-
-type encodingType int
-
-const (
-	noEncoding encodingType = iota
-	encodingDeflate
-	encodingGzip
-	unknownEncoding
-)
-
-func (e encodingType) String() string {
-	switch e {
-	case noEncoding:
-		return "identity"
-	case encodingDeflate:
-		return "deflate"
-	case encodingGzip:
-		return "gzip"
-	default:
-		return fmt.Sprintf("unknownEncoding[%d]", e)
-	}
-}
-
-func encodingFromHeader(header string) encodingType {
-	switch {
-	case strings.Contains(header, "gzip"):
-		return encodingGzip
-	case strings.Contains(header, "deflate"):
-		return encodingDeflate
-	case header == "", strings.Contains(header, "identity"):
-		return noEncoding
-	default:
-		return unknownEncoding
-	}
-}
-
-func parseFromHTTPServerRequest(ctx context.Context, req *http.Request, msg proto.Message) (int, error) {
-	ctx, span := trace.StartSpan(ctx, "go.chromium.org/goma/server/httprpc.parseFromHTTPServerRequest")
-	defer span.End()
-	contentEncoding := encodingFromHeader(req.Header.Get("Content-Encoding"))
-	var r io.Reader
-	switch contentEncoding {
-	case noEncoding:
-		r = req.Body
-	case encodingDeflate:
-		// RFC7230 says deflate coding is "zlib" (RFC1950) containing
-		// "deflate" compressed data (RFC1951).
-		// but goma client just used "deflate" compressed data
-		// for "Content-Encoding: deflate" wrongly.
-		r = flate.NewReader(req.Body)
-	case encodingGzip:
-		var err error
-		r, err = gzip.NewReader(req.Body)
-		if err != nil {
-			return 0, status.Errorf(codes.InvalidArgument, "gzip %v", err)
-		}
-	case unknownEncoding:
-		return 0, status.Errorf(codes.InvalidArgument, "unknown encoding: %s", req.Header.Get("Content-Encoding"))
-	}
-	data, err := ioutil.ReadAll(r)
-	if err != nil {
-		return 0, err
-	}
-	return len(data), proto.Unmarshal(data, msg)
-}
-
-// serializeToResponseWriter serialize msg to w.
-// it returns raw message size, so might differ to actual size if compressed.
-func serializeToResponseWriter(ctx context.Context, w http.ResponseWriter, msg proto.Message, acceptEncoding encodingType) (n int, err error) {
-	ctx, span := trace.StartSpan(ctx, "go.chromium.org/goma/server/httprpc.serializeToResponseWriter")
-	defer span.End()
-	w.Header().Set("Content-Type", "binary/x-protocol-buffer")
-	// Accept-Encoding: deflate only if client didn't say gzip,
-	// since old goma client only recognizes "Accept-Encoding: deflate".
-	// TODO: always accept gzip, deflate once new goma client released.
-	if acceptEncoding == encodingGzip {
-		w.Header().Set("Accept-Encoding", "gzip, deflate")
-	} else {
-		w.Header().Set("Accept-Encoding", "deflate")
-	}
-
-	resp, err := proto.Marshal(msg)
-	if err != nil {
-		return 0, err
-	}
-	var wr io.Writer
-	wr = w
-	if len(resp) > 0 {
-		switch acceptEncoding {
-		case noEncoding, unknownEncoding:
-			wr = w
-			w.Header().Set("Content-Encoding", "identity")
-		case encodingDeflate:
-			wr, err = flate.NewWriter(w, deflateCompressionLevel)
-			if err != nil {
-				return 0, err
-			}
-			defer func() {
-				ferr := wr.(*flate.Writer).Close()
-				if err == nil {
-					err = ferr
-				}
-			}()
-			w.Header().Set("Content-Encoding", "deflate")
-		case encodingGzip:
-			wr, err = gzip.NewWriterLevel(w, gzipCompressionLevel)
-			if err != nil {
-				return 0, err
-			}
-			defer func() {
-				ferr := wr.(*gzip.Writer).Close()
-				if err == nil {
-					err = ferr
-				}
-			}()
-			w.Header().Set("Content-Encoding", "gzip")
-		}
-	}
-	return wr.Write(resp)
-}
-
-// RemoteAddr returns http's remote (client) addr.
-// https://cloud.google.com/compute/docs/load-balancing/http/#components
-func RemoteAddr(req *http.Request) string {
-	forwards := req.Header.Get("X-Forwarded-For")
-	// initial IP in the comma-separated list.
-	s := strings.Split(forwards, ",")
-	ip := strings.TrimSpace(s[0])
-	if ip == "" {
-		// "<ip>:<port>"
-		return req.RemoteAddr
-	}
-	return ip
-}
-
-type option struct {
-	timeout   time.Duration
-	retry     rpc.Retry
-	apiKey    string
-	cluster   string
-	namespace string
-	Auth      Auth
-}
-
-// HandlerOption sets option for handler.
-type HandlerOption func(*option)
-
-// Timeout sets timeout to the handler. Default is 1 second.
-func Timeout(d time.Duration) HandlerOption {
-	return func(o *option) {
-		o.timeout = d
-	}
-}
-
-// WithRetry sets retry config to the handler.
-func WithRetry(retry rpc.Retry) HandlerOption {
-	return func(o *option) {
-		o.retry = retry
-	}
-}
-
-// WithAPIKey sets api key in outgoing context.
-func WithAPIKey(apiKey string) HandlerOption {
-	return func(o *option) {
-		o.apiKey = apiKey
-	}
-}
-
-// WithCluster sets cluster name to the handler for logging/monitoring etc.
-func WithCluster(c string) HandlerOption {
-	return func(o *option) {
-		o.cluster = c
-	}
-}
-
-// WithNamespace sets cluster namespace to the handler for logging/monitoring etc.
-func WithNamespace(ns string) HandlerOption {
-	return func(o *option) {
-		o.namespace = ns
-	}
-}
-
-// Auth authenticates the request.
-type Auth interface {
-	Auth(context.Context, *http.Request) (context.Context, error)
-}
-
-// WithAuth sets auth to the handler.
-func WithAuth(a Auth) HandlerOption {
-	return func(o *option) {
-		o.Auth = a
-	}
-}
-
-func httpStatus(err error) (int, string) {
-	// go/http-canonical-mapping
-	hc := http.StatusInternalServerError
-	var msg string
-	switch grpc.Code(err) {
-	case codes.OK:
-		hc = http.StatusOK
-
-	case codes.Canceled:
-		hc = 499
-		msg = "Client closed request"
-
-	case codes.InvalidArgument:
-		hc = http.StatusBadRequest
-
-	case codes.DeadlineExceeded:
-		hc = http.StatusGatewayTimeout
-
-	case codes.NotFound:
-		hc = http.StatusNotFound
-
-	case codes.AlreadyExists:
-		hc = http.StatusConflict
-
-	case codes.PermissionDenied:
-		hc = http.StatusForbidden
-
-	case codes.Unauthenticated:
-		// canonical mapping is StatusUnauthrozied (401).
-		// however, goma client considers StatusUnauthorized (401)
-		// as fatal errors and hard to recover http from
-		// the error.
-		// we already checked incoming end user credential
-		// in Auth, so the request was properly authorized.
-		// Unauthenticated would be reported by backend when
-		// access token becomes invalid (expired etc).
-		// it would happen if access token returned by
-		// auth_server, or passed through, has short expiration
-		// than request duration. i.e. during handling Exec request,
-		// access token is expired.
-		// we retries this case, this error should not be returned
-		// to user.
-		// http://b/78610039 http://b/78662481 http://b/119593170
-		hc = http.StatusInternalServerError
-
-	case codes.ResourceExhausted:
-		hc = http.StatusTooManyRequests
-
-	case codes.FailedPrecondition:
-		hc = http.StatusBadRequest
-
-	case codes.Aborted:
-		hc = http.StatusConflict
-
-	case codes.OutOfRange:
-		hc = http.StatusBadRequest
-
-	case codes.Unimplemented:
-		hc = http.StatusNotImplemented
-
-	case codes.Unavailable:
-		hc = http.StatusServiceUnavailable
-	}
-	if msg == "" {
-		msg = http.StatusText(hc)
-	}
-	return hc, msg
-}
-
-// Handler returns http.Handler to serve http rpc handler.
-func Handler(name string, req, resp proto.Message, h func(context.Context, proto.Message) (proto.Message, error), opts ...HandlerOption) http.Handler {
-	opt := &option{
-		timeout: 1 * time.Minute,
-	}
-	for _, o := range opts {
-		o(opt)
-	}
-
-	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		ctx, cancel := context.WithTimeout(r.Context(), opt.timeout)
-		defer cancel()
-
-		if opt.apiKey != "" {
-			// https://cloud.google.com/endpoints/docs/grpc/restricting-api-access-with-api-keys-grpc#grpc_clients
-			ctx = metadata.AppendToOutgoingContext(ctx, "x-api-key", opt.apiKey)
-		}
-
-		ctx, span := trace.StartSpan(ctx, "go.chromium.org/goma/server/httprpc.Handler:"+r.URL.Path)
-		defer span.End()
-		span.AddAttributes(
-			trace.StringAttribute("clueter", opt.cluster),
-			trace.StringAttribute("namespace", opt.namespace),
-		)
-		logger := log.FromContext(ctx)
-
-		req := proto.Clone(req)
-
-		// appengine/rp sets Accept-Encoding: gzip?
-		acceptEncoding := encodingFromHeader(r.Header.Get("Accept-Encoding"))
-		_, err := parseFromHTTPServerRequest(ctx, r, req)
-		if err != nil {
-			code := http.StatusBadRequest
-			http.Error(w, "bad request", code)
-			logger.Errorf("incoming parse error %s: %d %s: %v", r.URL.Path, code, http.StatusText(code), err)
-			return
-		}
-
-		// use short timeout at first, then longer timeout when
-		// deadline exceeded, to mitigate grpc lost response case.
-		// http://b/129647209
-		// assume most call needs less than 1 minute (both exec,
-		// file access),
-		// according to API metrics, 99%ile of Execute API latency
-		// was ~45 seconds (as of Nov 19, 2020).
-		// only a few exec call may need longer timeout.
-		// see below if status.Code(err) == codes.DeadlineExceeded case.
-		timeouts := []time.Duration{50 * time.Second, 90 * time.Second, 3 * time.Minute, 5 * time.Minute}
-		var resp proto.Message
-		authOK := false
-		err = opt.retry.Do(ctx, func() error {
-			pctx := ctx
-			ctx, cancel := context.WithTimeout(ctx, timeouts[0])
-			defer cancel()
-			// TODO: hard fail if opt.Auth == nil?
-			if opt.Auth != nil {
-				ctx, err = opt.Auth.Auth(ctx, r)
-				if err != nil {
-					if authOK {
-						// if once auth ok, then it failed because client access token was expired.
-						code := http.StatusGatewayTimeout
-						http.Error(w, fmt.Sprintf("auth token expired %s", RemoteAddr(r)), code)
-						logger.Errorf("auth token expired %s: %d %s", r.URL.Path, code, http.StatusText(code))
-						return err
-					}
-					code := http.StatusUnauthorized
-					http.Error(w, fmt.Sprintf("auth failed %s: %v", RemoteAddr(r), err), code)
-					logger.Errorf("auth error %s: %d %s: %v", r.URL.Path, code, http.StatusText(code), err)
-					return err
-				}
-				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 ((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.
-				// b) api has been finished, but grpc lost response. http://b/129647209
-				// Retry with longer timeout.
-				// if a, expect to succeed for long run with longer timeout.
-				// if b, expect response soon (cache hit).
-				if len(timeouts) > 1 {
-					timeouts = timeouts[1:]
-				}
-				logger.Warnf("retry with longer timeout %s", timeouts[0])
-				return rpc.RetriableError{
-					Err: err,
-				}
-			}
-			return err
-		})
-		if err != nil {
-			span.SetStatus(trace.Status{
-				Code:    int32(grpc.Code(err)),
-				Message: err.Error(),
-			})
-			code, msg := httpStatus(err)
-			http.Error(w, msg, code)
-			switch code {
-			case 499: // client closed request
-				logger.Warnf("server error %s: %d %s: %v", r.URL.Path, code, msg, err)
-			default:
-				logger.Errorf("server error %s: %d %s: %v", r.URL.Path, code, msg, err)
-			}
-			return
-		}
-
-		_, err = serializeToResponseWriter(ctx, w, resp, acceptEncoding)
-
-		if err != nil {
-			logger.Errorf("outgoing serialize error %s: %v", r.URL.Path, err)
-			return
-		}
-	})
-
-	return handler
-}
-
-// Handler returns http.Handler to serve http stream.
-func StreamHandler(name string, h func(ctx context.Context, w http.ResponseWriter, req *http.Request) error, opts ...HandlerOption) http.Handler {
-	opt := &option{
-		timeout: 1 * time.Minute,
-	}
-	for _, o := range opts {
-		o(opt)
-	}
-
-	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		ctx, cancel := context.WithTimeout(r.Context(), opt.timeout)
-		defer cancel()
-
-		ctx, span := trace.StartSpan(ctx, "go.chromium.org/goma/server/httprpc.StreamHandler:"+name)
-		defer span.End()
-
-		err := h(ctx, w, r)
-		if err != nil {
-			span.SetStatus(trace.Status{
-				Code:    int32(grpc.Code(err)),
-				Message: err.Error(),
-			})
-			code, msg := httpStatus(err)
-			http.Error(w, msg, code)
-			logger := log.FromContext(ctx)
-			logger.Errorf("server error %s: %d %s: %v", r.URL.Path, code, msg, err)
-			return
-		}
-	})
-	return handler
-}
diff --git a/httprpc/server_test.go b/httprpc/server_test.go
deleted file mode 100644
index 6989ad3..0000000
--- a/httprpc/server_test.go
+++ /dev/null
@@ -1,89 +0,0 @@
-// Copyright 2017 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.
-
-package httprpc
-
-import (
-	"compress/flate"
-	"compress/gzip"
-	"context"
-	"io/ioutil"
-	"net/http"
-	"net/http/httptest"
-	"testing"
-
-	healthpb "google.golang.org/grpc/health/grpc_health_v1"
-	"google.golang.org/protobuf/proto"
-
-	pb "go.chromium.org/goma/server/proto/auth"
-)
-
-func TestSeralizeToResponseWriterDeflate(t *testing.T) {
-	want := &pb.AuthResp{
-		Email: "goma-dev@google.com",
-	}
-	rw := httptest.NewRecorder()
-	_, err := serializeToResponseWriter(context.Background(), rw, want, encodingDeflate)
-	if err != nil {
-		t.Errorf("serializeToResponseWriter()=_, %v; want=_, nil", err)
-	}
-	gotBytes, err := ioutil.ReadAll(flate.NewReader(rw.Result().Body))
-	if err != nil {
-		t.Errorf("serialize response read: %v", err)
-	}
-	got := &pb.AuthResp{}
-	err = proto.Unmarshal(gotBytes, got)
-	if err != nil {
-		t.Errorf("unmarshal: %v", err)
-	}
-	if !proto.Equal(got, want) {
-		t.Errorf("got %#v; want %#v", got, want)
-	}
-}
-
-func TestSeralizeToResponseWriterGzip(t *testing.T) {
-	want := &pb.AuthResp{
-		Email: "goma-dev@google.com",
-	}
-	rw := httptest.NewRecorder()
-	_, err := serializeToResponseWriter(context.Background(), rw, want, encodingGzip)
-	if err != nil {
-		t.Errorf("serializeToResponseWriter()=_, %v; want=_, nil", err)
-	}
-	r, err := gzip.NewReader(rw.Result().Body)
-	if err != nil {
-		t.Errorf("gzip %v", err)
-	}
-	gotBytes, err := ioutil.ReadAll(r)
-	if err != nil {
-		t.Errorf("serialize response read: %v", err)
-	}
-	got := &pb.AuthResp{}
-	err = proto.Unmarshal(gotBytes, got)
-	if err != nil {
-		t.Errorf("unmarshal: %v", err)
-	}
-	if !proto.Equal(got, want) {
-		t.Errorf("got %#v; want %#v", got, want)
-	}
-}
-
-func TestHandler(t *testing.T) {
-	var opts []HandlerOption
-
-	handler := Handler(
-		"Health",
-		&healthpb.HealthCheckRequest{}, &healthpb.HealthCheckResponse{},
-		func(ctx context.Context, req proto.Message) (proto.Message, error) {
-			return &healthpb.HealthCheckResponse{}, nil
-		}, opts...)
-
-	s := httptest.NewServer(handler)
-	defer s.Close()
-
-	_, err := http.Get(s.URL)
-	if err != nil {
-		t.Errorf("http.Get err: %v", err)
-	}
-}
diff --git a/httprpc/settings/settings.go b/httprpc/settings/settings.go
deleted file mode 100644
index b79457b..0000000
--- a/httprpc/settings/settings.go
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2017 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.
-
-// Package settings implements settings service for goma httprpc.
-package settings
-
-import (
-	"context"
-	"net/http"
-
-	"google.golang.org/protobuf/proto"
-
-	"go.chromium.org/goma/server/httprpc"
-	pb "go.chromium.org/goma/server/proto/settings"
-)
-
-func Handler(s pb.SettingsServiceServer, opts ...httprpc.HandlerOption) http.Handler {
-	return httprpc.Handler(
-		"SettingsService.Get",
-		&pb.SettingsReq{}, &pb.SettingsResp{},
-		func(ctx context.Context, req proto.Message) (proto.Message, error) {
-			resp, err := s.Get(ctx, req.(*pb.SettingsReq))
-			return resp, err
-		}, opts...)
-}
-
-func Register(mux *http.ServeMux, s pb.SettingsServiceServer, opts ...httprpc.HandlerOption) {
-	mux.Handle("/settings", Handler(s, opts...))
-}
diff --git a/httprpc/trace.go b/httprpc/trace.go
deleted file mode 100644
index b179948..0000000
--- a/httprpc/trace.go
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2018 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.
-
-package httprpc
-
-import (
-	"net/http"
-
-	"go.opencensus.io/trace"
-)
-
-// Trace adds labels to trace span for requested path.
-// It would be used as top handler for incoming request under ochttp.Handler.
-func Trace(handler http.Handler, labels map[string]string) http.Handler {
-	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
-		span := trace.FromContext(req.Context())
-		var attrs []trace.Attribute
-		for k, v := range labels {
-			if v == "" {
-				continue
-			}
-			attrs = append(attrs, trace.StringAttribute(k, v))
-		}
-		span.AddAttributes(attrs...)
-		handler.ServeHTTP(w, req)
-	})
-}
diff --git a/infra/README.md b/infra/README.md
deleted file mode 100644
index 1a6dc7e..0000000
--- a/infra/README.md
+++ /dev/null
@@ -1 +0,0 @@
-This directory contains chrome-infra-specific files.
diff --git a/infra/config/generated/commit-queue.cfg b/infra/config/generated/commit-queue.cfg
deleted file mode 100644
index e411c0f..0000000
--- a/infra/config/generated/commit-queue.cfg
+++ /dev/null
@@ -1,35 +0,0 @@
-# Auto-generated by lucicfg.
-# Do not modify manually.
-#
-# For the schema of this file, see Config message:
-#   https://luci-config.appspot.com/schemas/projects:commit-queue.cfg
-
-cq_status_host: "chromium-cq-status.appspot.com"
-config_groups {
-  name: "Main_CQ"
-  gerrit {
-    url: "https://chromium-review.googlesource.com"
-    projects {
-      name: "infra/goma/server"
-      ref_regexp: "refs/heads/.+"
-    }
-  }
-  verifiers {
-    gerrit_cq_ability {
-      committer_list: "project-goma-server-tryjob-access"
-      dry_run_access_list: "project-chromium-tryjob-access"
-    }
-    tryjob {
-      builders {
-        name: "goma-server/try/linux_rel"
-      }
-      retry_config {
-        single_quota: 1
-        global_quota: 2
-        failure_weight: 100
-        transient_failure_weight: 1
-        timeout_weight: 100
-      }
-    }
-  }
-}
diff --git a/infra/config/generated/cr-buildbucket.cfg b/infra/config/generated/cr-buildbucket.cfg
deleted file mode 100644
index a07c85e..0000000
--- a/infra/config/generated/cr-buildbucket.cfg
+++ /dev/null
@@ -1,39 +0,0 @@
-# Auto-generated by lucicfg.
-# Do not modify manually.
-#
-# For the schema of this file, see BuildbucketCfg message:
-#   https://luci-config.appspot.com/schemas/projects:buildbucket.cfg
-
-buckets {
-  name: "try"
-  acls {
-    group: "all"
-  }
-  acls {
-    role: SCHEDULER
-    group: "project-goma-server-tryjob-access"
-  }
-  swarming {
-    builders {
-      name: "linux_rel"
-      swarming_host: "chromium-swarm.appspot.com"
-      dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-18.04"
-      dimensions: "pool:luci.flex.try"
-      exe {
-        cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
-        cipd_version: "refs/heads/main"
-        cmd: "luciexe"
-      }
-      properties:
-        '{'
-        '  "recipe": "goma_server"'
-        '}'
-      service_account: "goma-server-ext-try-builder@chops-service-accounts.iam.gserviceaccount.com"
-      experiments {
-        key: "luci.recipes.use_python3"
-        value: 100
-      }
-    }
-  }
-}
diff --git a/infra/config/generated/luci-logdog.cfg b/infra/config/generated/luci-logdog.cfg
deleted file mode 100644
index 45d1c90..0000000
--- a/infra/config/generated/luci-logdog.cfg
+++ /dev/null
@@ -1,8 +0,0 @@
-# Auto-generated by lucicfg.
-# Do not modify manually.
-#
-# For the schema of this file, see ProjectConfig message:
-#   https://luci-config.appspot.com/schemas/projects:luci-logdog.cfg
-
-reader_auth_groups: "all"
-writer_auth_groups: "luci-logdog-chromium-writers"
diff --git a/infra/config/generated/luci-milo.cfg b/infra/config/generated/luci-milo.cfg
deleted file mode 100644
index 55e5e00..0000000
--- a/infra/config/generated/luci-milo.cfg
+++ /dev/null
@@ -1,15 +0,0 @@
-# Auto-generated by lucicfg.
-# Do not modify manually.
-#
-# For the schema of this file, see Project message:
-#   https://luci-config.appspot.com/schemas/projects:luci-milo.cfg
-
-consoles {
-  id: "Try Builders"
-  name: "Try Builders"
-  builders {
-    name: "buildbucket/luci.goma-server.try/linux_rel"
-  }
-  builder_view_only: true
-}
-logo_url: "https://storage.googleapis.com/chrome-infra-public/logo/goma-server-logo.png"
diff --git a/infra/config/generated/project.cfg b/infra/config/generated/project.cfg
deleted file mode 100644
index 20b7f59..0000000
--- a/infra/config/generated/project.cfg
+++ /dev/null
@@ -1,14 +0,0 @@
-# Auto-generated by lucicfg.
-# Do not modify manually.
-#
-# For the schema of this file, see ProjectCfg message:
-#   https://luci-config.appspot.com/schemas/projects:project.cfg
-
-name: "goma-server"
-access: "group:all"
-lucicfg {
-  version: "1.30.9"
-  package_dir: ".."
-  config_dir: "generated"
-  entry_point: "main.star"
-}
diff --git a/infra/config/generated/realms.cfg b/infra/config/generated/realms.cfg
deleted file mode 100644
index a081e94..0000000
--- a/infra/config/generated/realms.cfg
+++ /dev/null
@@ -1,56 +0,0 @@
-# Auto-generated by lucicfg.
-# Do not modify manually.
-#
-# For the schema of this file, see RealmsCfg message:
-#   https://luci-config.appspot.com/schemas/projects:realms.cfg
-
-realms {
-  name: "@root"
-  bindings {
-    role: "role/buildbucket.reader"
-    principals: "group:all"
-  }
-  bindings {
-    role: "role/configs.reader"
-    principals: "group:all"
-  }
-  bindings {
-    role: "role/cq.committer"
-    principals: "group:project-goma-server-tryjob-access"
-  }
-  bindings {
-    role: "role/cq.dryRunner"
-    principals: "group:project-chromium-tryjob-access"
-  }
-  bindings {
-    role: "role/logdog.reader"
-    principals: "group:all"
-  }
-  bindings {
-    role: "role/logdog.writer"
-    principals: "group:luci-logdog-chromium-writers"
-  }
-  bindings {
-    role: "role/scheduler.owner"
-    principals: "group:project-goma-server-tryjob-access"
-  }
-  bindings {
-    role: "role/scheduler.reader"
-    principals: "group:all"
-  }
-}
-realms {
-  name: "try"
-  bindings {
-    role: "role/buildbucket.builderServiceAccount"
-    principals: "user:goma-server-ext-try-builder@chops-service-accounts.iam.gserviceaccount.com"
-  }
-  bindings {
-    role: "role/buildbucket.triggerer"
-    principals: "group:project-goma-server-tryjob-access"
-  }
-  bindings {
-    role: "role/swarming.taskTriggerer"
-    principals: "group:flex-try-led-users"
-  }
-}
diff --git a/infra/config/main.star b/infra/config/main.star
deleted file mode 100755
index f6b3308..0000000
--- a/infra/config/main.star
+++ /dev/null
@@ -1,108 +0,0 @@
-#!/usr/bin/env lucicfg
-
-lucicfg.check_version("1.30.9", "Please update depot_tools")
-
-luci.project(
-    name = "goma-server",
-    buildbucket = "cr-buildbucket.appspot.com",
-    logdog = "luci-logdog.appspot.com",
-    milo = "luci-milo.appspot.com",
-    swarming = "chromium-swarm.appspot.com",
-    acls = [
-        # This project is publicly readable.
-        acl.entry(
-            roles = [
-                acl.BUILDBUCKET_READER,
-                acl.LOGDOG_READER,
-                acl.PROJECT_CONFIGS_READER,
-                acl.SCHEDULER_READER,
-            ],
-            groups = "all",
-        ),
-        # Allow committers to use CQ and to force-trigger and stop CI builds.
-        acl.entry(
-            roles = [
-                acl.SCHEDULER_OWNER,
-                acl.CQ_COMMITTER,
-            ],
-            groups = "project-goma-server-tryjob-access",
-        ),
-        # Ability to launch CQ dry runs.
-        acl.entry(
-            roles = acl.CQ_DRY_RUNNER,
-            groups = "project-chromium-tryjob-access",
-        ),
-        acl.entry(
-            roles = acl.LOGDOG_WRITER,
-            groups = "luci-logdog-chromium-writers",
-        ),
-    ],
-)
-
-luci.milo(logo = "https://storage.googleapis.com/chrome-infra-public/logo/goma-server-logo.png")
-
-# Without this, luci-logdog.cfg won't be made.
-luci.logdog()
-
-luci.cq(status_host = "chromium-cq-status.appspot.com")
-
-luci.bucket(
-    name = "try",
-    acls = [
-        # Allow launching tryjobs directly (in addition to doing it through CQ).
-        acl.entry(
-            roles = acl.BUILDBUCKET_TRIGGERER,
-            groups = "project-goma-server-tryjob-access",
-        ),
-    ],
-    bindings = [
-        luci.binding(
-            roles = "role/swarming.taskTriggerer",
-            groups = "flex-try-led-users",
-        ),
-    ],
-)
-
-# The Milo builder list with all pre-submit builders, referenced below.
-luci.list_view(
-    name = "Try Builders",
-)
-
-# The CQ group with all pre-submit builders, referenced below.
-luci.cq_group(
-    name = "Main_CQ",
-    watch = cq.refset(
-        repo = "https://chromium-review.googlesource.com/infra/goma/server",
-        refs = ["refs/heads/.+"],
-    ),
-)
-luci.builder(
-    name = "linux_rel",
-    bucket = "try",
-    service_account = "goma-server-ext-try-builder@chops-service-accounts.iam.gserviceaccount.com",
-    executable = luci.recipe(
-        name = "goma_server",
-        cipd_package = "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build",
-        use_bbagent = True,
-    ),
-    dimensions = {
-        "pool": "luci.flex.try",
-        "os": "Ubuntu-18.04",
-        "cpu": "x86-64",
-    },
-    experiments = {
-        "luci.recipes.use_python3": 100,
-    },
-)
-
-# Add to the CQ.
-luci.cq_tryjob_verifier(
-    builder = "try/linux_rel",
-    cq_group = "Main_CQ",
-)
-
-# And also to the pre-submit builders list.
-luci.list_view_entry(
-    builder = "try/linux_rel",
-    list_view = "Try Builders",
-)
diff --git a/log/errorreporter/errorreporting.go b/log/errorreporter/errorreporting.go
deleted file mode 100644
index 6d637d5..0000000
--- a/log/errorreporter/errorreporting.go
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright 2018 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.
-
-// Package errorreporter provides error reporting functionality.
-package errorreporter
-
-import (
-	"context"
-	"fmt"
-	"net/http"
-	"reflect"
-
-	"cloud.google.com/go/errorreporting"
-	"google.golang.org/grpc"
-	"google.golang.org/grpc/codes"
-
-	"go.chromium.org/goma/server/log"
-)
-
-// ErrorReporter is an interface to report crash to stackdriver error reporting.
-type ErrorReporter interface {
-	Close() error
-	Flush()
-	Report(e errorreporting.Entry)
-	ReportSync(ctx context.Context, e errorreporting.Entry) error
-}
-
-var DefaultErrorReporter ErrorReporter = nopErrorReporter{}
-
-// Enabled reports DefaultErrorReporter is configured to report crash to stackdriver.
-func Enabled() bool {
-	return !reflect.DeepEqual(DefaultErrorReporter, nopErrorReporter{})
-}
-
-// New creates error reporter.
-func New(ctx context.Context, projectID, serviceName string) ErrorReporter {
-	logger := log.FromContext(ctx)
-	cfg := errorreporting.Config{
-		ServiceName: serviceName,
-	}
-	ec, err := errorreporting.NewClient(ctx, projectID, cfg)
-	if err != nil {
-		logger.Warnf("failed to create errorreporting client: %v", err)
-		return nopErrorReporter{}
-	}
-	return ec
-}
-
-// Do will be used as defer func to recover panic and report error if
-// panic detected. Also update err if err != nil.
-func Do(req *http.Request, err *error) {
-	if r := recover(); r != nil {
-		Report(errorreporting.Entry{
-			Error: fmt.Errorf("panic detected: %v", r),
-			Req:   req,
-		})
-		Flush()
-		if err != nil {
-			*err = grpc.Errorf(codes.Internal, "panic detected: %v", r)
-		}
-	}
-}
-
-// Flush flushes DefaultErrorReporter.
-func Flush() {
-	DefaultErrorReporter.Flush()
-}
-
-// Report reports entry with DefaultErrorReporter.
-func Report(e errorreporting.Entry) {
-	DefaultErrorReporter.Report(e)
-}
-
-// ReportSync reports entry with DefaultErrorReporter.
-func ReportSync(ctx context.Context, e errorreporting.Entry) error {
-	return DefaultErrorReporter.ReportSync(ctx, e)
-}
-
-type nopErrorReporter struct{}
-
-func (e nopErrorReporter) Close() error { return nil }
-func (e nopErrorReporter) Flush()       {}
-
-func (e nopErrorReporter) Report(errorreporting.Entry) {}
-
-func (e nopErrorReporter) ReportSync(context.Context, errorreporting.Entry) error { return nil }
diff --git a/log/example_test.go b/log/example_test.go
deleted file mode 100644
index c43d8df..0000000
--- a/log/example_test.go
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2018 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.
-
-package log_test
-
-import (
-	"context"
-
-	"go.opencensus.io/tag"
-	"go.uber.org/zap"
-
-	"go.chromium.org/goma/server/log"
-)
-
-func Example() {
-	ctx := context.Background()
-	log.SetZapLogger(zap.NewExample())
-	logger := log.FromContext(ctx)
-	defer logger.Sync()
-	k, err := tag.NewKey("go.chromium.org/goma/server/log/example")
-	if err != nil {
-		logger.Fatal(err)
-	}
-	log.RegisterTagKey(k)
-
-	ctx, err = tag.New(ctx, tag.Insert(k, "trace_1"))
-	if err != nil {
-		logger.Fatal(err)
-	}
-	logger = log.FromContext(ctx)
-
-	logger.Info("info")
-	// Output:
-	// {"level":"info","msg":"info","go.chromium.org/goma/server/log/example":"trace_1"}
-}
diff --git a/log/log.go b/log/log.go
deleted file mode 100644
index 5ac4934..0000000
--- a/log/log.go
+++ /dev/null
@@ -1,131 +0,0 @@
-// Copyright 2018 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.
-
-/*
-Package log provides logging mechanism for goma servers.
-*/
-package log
-
-import (
-	"context"
-	"fmt"
-	"sync"
-
-	"cloud.google.com/go/compute/metadata"
-	grpczap "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap"
-	"go.opencensus.io/tag"
-	"go.opencensus.io/trace"
-	"go.uber.org/zap"
-	"go.uber.org/zap/zapcore"
-)
-
-type ctxKeyType int
-
-var (
-	ctxKey ctxKeyType
-
-	logger     = mustZapLogger()
-	grpcLogger = mustGRPCLogger()
-
-	tagKeys []tag.Key
-
-	projectIDonce sync.Once
-	projectID     string
-)
-
-// SetZapLogger sets zap logger as default logger.
-// Useful for test
-//
-//	log.SetZapLogger(zap.NewExample())
-func SetZapLogger(zapLogger *zap.Logger) {
-	logger = zapLogger
-	grpcLogger = zapLogger.WithOptions(zap.AddCallerSkip(2))
-	grpczap.ReplaceGrpcLoggerV2WithVerbosity(grpcLogger, gRPCVerboseLevel)
-}
-
-// RegsiterTagKey registers tag key that would be used for log context.
-func RegisterTagKey(key tag.Key) {
-	tagKeys = append(tagKeys, key)
-}
-
-// NewContext returns a new Context that carries logger.
-func NewContext(ctx context.Context, logger Logger) context.Context {
-	return context.WithValue(ctx, ctxKey, logger)
-}
-
-// FromContext returns logger with context.
-// opencensus's tag registered by RegisterTagKey and
-// trace's span-id and trace-id will be added
-// as context information of the log.
-func FromContext(ctx context.Context) Logger {
-	if logger, ok := ctx.Value(ctxKey).(Logger); ok {
-		return logger
-	}
-	tm := tag.FromContext(ctx)
-
-	var fields []zapcore.Field
-
-	for _, tk := range tagKeys {
-		v, ok := tm.Value(tk)
-		if ok {
-			fields = append(fields, zap.String(tk.Name(), v))
-		}
-	}
-	span := trace.FromContext(ctx)
-	var projErr error
-	if span.IsRecordingEvents() {
-		sc := span.SpanContext()
-		traceID := sc.TraceID.String()
-		projectIDonce.Do(func() {
-			projectID, projErr = metadata.ProjectID()
-		})
-		if projectID != "" {
-			traceID = fmt.Sprintf("projects/%s/traces/%s", projectID, sc.TraceID.String())
-		}
-
-		fields = append(fields,
-			zap.String("logging.googleapis.com/trace", traceID),
-			zap.String("logging.googleapis.com/spanId", sc.SpanID.String()))
-	}
-	l := logger.With(fields...).Sugar()
-	if projErr != nil {
-		l.Errorf("metadata.ProjectID: %v", projErr)
-	}
-	return l
-}
-
-// Logger is logging interface.
-type Logger interface {
-	// Debug logs to DEBUG log. Arguments are handled in the manner of fmt.Print.
-	Debug(args ...interface{})
-
-	// Debugf logs to DEBUG log. Arguments are handled in the manner of fmt.Printf.
-	Debugf(format string, arg ...interface{})
-
-	// Info logs to INFO log. Arguments are handled in the manner of fmt.Print.
-	Info(args ...interface{})
-	// Infof logs to INFO log. Arguments are handled in the manner of fmt.Printf.
-	Infof(format string, arg ...interface{})
-
-	// Warn logs to WARNING log. Arguments are handled in the manner of fmt.Print.
-	Warn(args ...interface{})
-
-	// Warnf logs to WARNING log. Arguments are handled in the manner of fmt.Printf.
-	Warnf(format string, arg ...interface{})
-
-	// Error logs to ERROR log. Arguments are handled in the manner of fmt.Print.
-	Error(args ...interface{})
-
-	// Errorf logs to ERROR log. Arguments are handled in the manner of fmt.Printf.
-	Errorf(format string, arg ...interface{})
-
-	// Fatal logs to CRITICAL log. Arguments are handled in te manner of fmt.Print.
-	Fatal(args ...interface{})
-
-	// Fatalf logs to CRITICAL log. Arguments are handled in the manner of fmt.Printf.
-	Fatalf(format string, arg ...interface{})
-
-	// Sync flushes any buffered log entries.
-	Sync() error
-}
diff --git a/log/zap.go b/log/zap.go
deleted file mode 100644
index 7633508..0000000
--- a/log/zap.go
+++ /dev/null
@@ -1,105 +0,0 @@
-// Copyright 2018 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.
-
-package log
-
-import (
-	"context"
-	"log"
-	"os"
-	"strconv"
-
-	gce "cloud.google.com/go/compute/metadata"
-	grpczap "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap"
-	"go.uber.org/zap"
-	"go.uber.org/zap/zapcore"
-	"google.golang.org/grpc"
-	"google.golang.org/grpc/codes"
-)
-
-var (
-	gRPCLevel        = zapcore.ErrorLevel
-	gRPCVerboseLevel int
-)
-
-func mustGRPCLogger() *zap.Logger {
-	// emulate grpclog/loggerv2.go
-	switch os.Getenv("GRPC_GO_LOG_SEVERITY_LEVEL") {
-	case "WARNING", "warning":
-		gRPCLevel = zapcore.WarnLevel
-	case "INFO", "info":
-		gRPCLevel = zapcore.InfoLevel
-	default:
-		gRPCLevel = zapcore.ErrorLevel
-	}
-
-	vLevel := os.Getenv("GRPC_GO_LOG_VERBOSITY_LEVEL")
-	if vl, err := strconv.Atoi(vLevel); err == nil {
-		gRPCVerboseLevel = vl
-	}
-
-	zapCfg := zapConfig()
-	zapCfg.Level = zap.NewAtomicLevelAt(gRPCLevel)
-	gl, err := zapCfg.Build()
-	if err != nil {
-		FromContext(context.Background()).Fatalf("failed to build grpc zap logger: %v", err)
-	}
-	// skip 2
-	//   zapGRPCLogger method
-	//   grpclog func
-	// ReplaceGrpcLoggerV2WithVerbosity sets
-	// "system=grpc" and "grpc_log=true".
-	return gl.WithOptions(zap.AddCallerSkip(2))
-}
-
-func init() {
-	grpczap.ReplaceGrpcLoggerV2WithVerbosity(grpcLogger, gRPCVerboseLevel)
-}
-
-// GRPCUnaryServerInterceptor returns server interceptor to log grpc calls.
-func GRPCUnaryServerInterceptor(opts ...grpczap.Option) grpc.UnaryServerInterceptor {
-	opts = append([]grpczap.Option{
-		grpczap.WithLevels(func(code codes.Code) zapcore.Level {
-			// "finished unary call with code OK" is too chatty
-			// (for health check etc).
-			switch code {
-			case codes.OK:
-				return zap.DebugLevel
-			}
-			return grpczap.DefaultCodeToLevel(code)
-		}),
-	}, opts...)
-	return grpczap.UnaryServerInterceptor(grpcLogger, opts...)
-}
-
-func zapConfig() zap.Config {
-	if !gce.OnGCE() {
-		zapCfg := zap.NewDevelopmentConfig()
-		zapCfg.DisableStacktrace = true
-		return zapCfg
-	}
-
-	zapCfg := zap.NewProductionConfig()
-	zapCfg.Level = zap.NewAtomicLevelAt(zap.DebugLevel)
-	// To show text content only in cloud logging top level viewer.
-	// https://cloud.google.com/logging/docs/view/logs_viewer_v2#expanding
-	zapCfg.EncoderConfig.MessageKey = "message"
-	// https://github.com/GoogleCloudPlatform/fluent-plugin-google-cloud/blob/cea0afd43287ce35d6aa3e1b29e791943d379df4/lib/fluent/plugin/out_google_cloud.rb#L950
-	zapCfg.EncoderConfig.TimeKey = "time"
-	zapCfg.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
-	// https://github.com/GoogleCloudPlatform/fluent-plugin-google-cloud/blob/cea0afd43287ce35d6aa3e1b29e791943d379df4/lib/fluent/plugin/out_google_cloud.rb#L980
-	zapCfg.EncoderConfig.LevelKey = "severity"
-	return zapCfg
-}
-
-// mustZapLoggerConfig returns
-// * zap logger configured for GKE container if running on compute engine
-// * otherwise, use zap's default logger for development outputting non-json text format log.
-func mustZapLogger(options ...zap.Option) *zap.Logger {
-	logger, err := zapConfig().Build(options...)
-	if err != nil {
-		log.Fatalf("failed to build zap logger: %v", err)
-	}
-	return logger
-}
diff --git a/profiler/profiler.go b/profiler/profiler.go
deleted file mode 100644
index e528993..0000000
--- a/profiler/profiler.go
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2017 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.
-
-// Package profiler provides convenient function to enable cloud profiler.
-package profiler
-
-import (
-	"context"
-	"os"
-	"path/filepath"
-
-	gce "cloud.google.com/go/compute/metadata"
-	"cloud.google.com/go/profiler"
-	"google.golang.org/api/option"
-
-	"go.chromium.org/goma/server/log"
-)
-
-// TODO: profiler API over quota? http://b/73749051
-
-// Setup starts cloud profiler if executable is running on GCE.
-func Setup(ctx context.Context) {
-	if gce.OnGCE() {
-		logger := log.FromContext(ctx)
-		cluster, err := gce.InstanceAttributeValue("cluster-name")
-		if err != nil {
-			logger.Errorf("failed to get cluster name: %v", err)
-			cluster = "unknown"
-		}
-		target := cluster + "." + filepath.Base(os.Args[0])
-		logger.Infof("profiler target name: %s", target)
-		err = profiler.Start(profiler.Config{Service: target},
-			// Disallow grpc in google-api-go-client to send stats/trace of profiler grpc's api call.
-			option.WithTelemetryDisabled())
-		if err != nil {
-			logger.Errorf("failed to start cloud profiler: %v", err)
-		}
-	}
-}
diff --git a/proto/api/goma_data.proto b/proto/api/goma_data.proto
deleted file mode 100644
index 69921b2..0000000
--- a/proto/api/goma_data.proto
+++ /dev/null
@@ -1,432 +0,0 @@
-// Copyright 2010 Google LLC. All Rights Reserved.
-
-//
-// new proto definitions for goma v2
-// LINT: ALLOW_GROUPS
-
-syntax = "proto2";
-
-package devtools_goma;
-
-option go_package = "go.chromium.org/goma/server/proto/api";
-
-import "google/protobuf/timestamp.proto";
-
-
-// persistent data
-
-// hash_key = sha256(serialized FileBlob)
-//
-// 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;
-    FILE = 1;
-    FILE_META = 2;
-    FILE_CHUNK = 3;
-    // ARCHIVE = 4;
-    FILE_REF = 5;
-  }
-  required BlobType blob_type = 1;
-
-  // for blob_type=FILE_CHUNK
-  optional int64 offset = 10;
-
-  // for blob_type=FILE or FILE_CHUNK
-  optional bytes content = 11;
-
-  // for all blob_types
-  optional int64 file_size = 20;
-
-  // for blob_type=FILE_META.  hash_key is FileBlob hash of FILE_CHUNK.
-  repeated string hash_key = 21;
-}
-
-// Goma backend selects compiler with CommandSpec.
-// NEXT ID TO USE: 14
-message CommandSpec {
-  optional string name = 1;  // "gcc", "g++", "cl.exe", etc, without
-                             // path, version number and architecture.
-  optional string version = 2;  // "4.4.3[Ubuntu 4.4.3-4ubuntu5]"
-  optional string target = 3;  // "x86_64-linux-gnu
-
-  // Binary size of the command.
-  // Necessary if toolchain is sent to the server.
-  // TODO: Now that we have ToolchainSpec, this is not necessary.
-  optional int64 size = 13;
-
-  // The followings will be used to request more specific version of
-  // command, populated by compiler_proxy.
-  optional bytes binary_hash = 4;  // to require exact the same binary.
-  // A list of alternative hashes. This field will be used when
-  // multiple binaries can be considered the same. For example, linux
-  // android gcc will be used even if a user is using goma from
-  // mac. For such cases, we should fill this field with binary hash
-  // of mac gcc.
-  repeated bytes equivalent_hash = 11;
-  optional string detailed_info = 5;  // output of "gcc -v"
-  // Tell compiler_proxy what the path to local 'gcc' (not goma gcc)
-  // is, populated by gomacc.cc and consumed by compiler_proxy
-  optional string local_compiler_path = 6;
-
-  // Compiler system include paths (sent from compiler_proxy).
-  // Note that system include paths can be a relative path from cwd.
-  // For Windows NaCl, it could also be a relative path from toolchain root.
-  repeated string system_include_path = 7;
-  repeated string cxx_system_include_path = 8;
-  repeated string system_framework_path = 9;
-
-  // Compiler system library paths
-  repeated string system_library_path = 12;
-
-  // An optional label of this command for logging/monitoring purpose.
-  optional string comment = 10;
-}
-
-message SubprogramSpec {
-  // full path (in client filesystem)
-  // or basename (in result when default subprogram is used).
-  optional string path = 1;
-  optional string binary_hash = 2;
-  // Binary size of the subprogram.
-  optional int64 size = 3;
-}
-
-// Experimental.
-// Specifies toolchain specification.
-// This spec is necessary only if toolchain is also uploaded.
-message ToolchainSpec {
-  // path to toolchain. Relative path from ExecReq::cwd or Absolute path.
-  optional string path = 1;
-  // SHA256 hash of the toolchain.
-  optional string hash = 2;
-  // file size of toolchain (in bytes).
-  optional int64 size = 3;
-  // true if the toolchain has 'x' bit.
-  optional bool is_executable = 4;
-  // If this spec is a symlink, set this path.
-  // hash, size, and is_executable should be empty in this case.
-  // If this spec is not a symlink, this should be empty.
-  optional string symlink_path = 5;
-}
-
-message ExecResult {
-  required int32 exit_status = 1 [default=-1];
-  optional bytes stdout_buffer = 2;
-  optional bytes stderr_buffer = 3;
-  optional CommandSpec command_spec = 4;
-
-  // subprograms that were used in compilation.
-  repeated SubprogramSpec subprogram = 5;
-
-  repeated group Output = 10 {
-    // TODO: We might want to normalize this path to relative path?
-    optional string filename = 11;  // relative to request cwd or full path
-    // if blob.blob_type=FILE_META, client need to request blob.hash_key() later
-    optional FileBlob blob = 12;
-    optional bool is_executable = 13 [default=false];
-  };
-}
-
-// Common RPC message
-
-message PlatformProperty {
-  optional string name = 1;
-  optional string value = 2;
-}
-
-// NEXT ID TO USE: 14
-message RequesterInfo {
-  optional string addr = 1;  // requester's ip addr.
-  optional string username = 2;  // requester's user name
-  // Identifier for each compiler_proxy request.
-  optional string compiler_proxy_id = 3;
-  // The version of goma protocol. This is intended to be used for two purpose:
-  //
-  // - When we changes the meanings of compiler_proxy's request, we may
-  //   want to support the previous type of requests in our backend for a while.
-  //   Backend can change its behavior by checking the value of this field.
-  // - Once a server stops supporting the old behavior, the backend can
-  //   send an error messages to the client. Also, we can track the clients'
-  //   versions with this field so we can easily decide if it's safe to
-  //   stop supporting the old behavior.
-  //
-  // 0 => 1: Changed the meaning of command_spec.system_include_path.
-  //         Now -isysroot in a command line is considered to obtain this field.
-  // 1 => 2: command_spec.version contains vendor versions.
-  //         E.g., 4.4.3[Ubuntu 4.4.3-4ubuntu5]
-  enum GomaApiVersion {
-    CURRENT_VERSION = 2;
-  }
-  // Can't use [default=CURRENT_VERSION] since GomaApiVersion is not int32.
-  optional int32 api_version = 4 [default=2];
-
-  optional int32 pid = 5;
-
-  // deprecated: indicates client use case.
-  reserved 6;
-
-  // # of retry. 0 is first call.
-  optional int32 retry = 7;
-
-  optional string goma_revision = 8;
-
-  // Unique ID per build.
-  // Client should set unique identifier of the build in this field.
-  //
-  // Note to LUCI users: since there could be multiple compile steps for
-  // recipes, you cannot use buildbucket build_id here.
-  optional string build_id = 9;
-
-  // Requester's dimensions. Each dimension is a "<key>:<value>" pair.
-  //
-  // `dimensions` can define the constraint of the remote machine.
-  // e.g. "os":"linux".
-  //
-  // Inspired from swarming bot dimensions.
-  // 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
-  // prefix or suffix.
-  enum PathStyle {
-    UNKNOWN_STYLE = 0;
-    POSIX_STYLE = 1;    // unix-style, slash separated.
-    WINDOWS_STYLE = 2;  // windows-style. backslash separated.
-  }
-  // Requester's path style.
-  optional PathStyle path_style = 11;
-
-  // re-client compat flags
-  // The exec root of the command.
-  optional string exec_root = 12;
-  // This is used to identify remote platform settings like the docker image
-  // to use to run the command.
-  repeated PlatformProperty platform_properties = 13;
-
-}
-
-message RequesterEnv {
-  optional string gomacc_path = 41;  // full pathname of gomacc.
-  optional string local_path = 42;  // user's PATH.
-  optional int32 umask = 43;  // user's umask.
-  optional bool verify_output = 50;  // GOMA_VERIFY_OUTPUT
-  optional bool use_local = 51;  // GOMA_USE_LOCAL
-  optional bool fallback = 52; // GOMA_FALLBACK
-  optional string verify_command = 53; // GOMA_VERIFY_COMMAND
-  repeated string fallback_input_file = 60;  // GOMA_FALLBACK_INPUT_FILES
-}
-
-// ExecService Interface
-
-message ExecReq {
-  required CommandSpec command_spec = 1;
-  repeated string arg = 2;
-  repeated string env = 3;
-  optional string cwd = 4;
-
-  repeated group Input = 10 {
-    optional string filename = 11;  // relative to cwd or full path
-    required string hash_key = 12;
-    optional FileBlob content = 13;
-  };
-
-  // The @ notations in arg should be expanded and the result should
-  // be stored in this field for javac and VC++.
-  repeated string expanded_arg = 14;
-
-  // Subprograms that would be used in client. By setting this,
-  // client could request backend to use the same subprograms.
-  //
-  repeated SubprogramSpec subprogram = 15;
-
-  optional RequesterInfo requester_info = 30;
-  enum CachePolicy {
-    // IGNORE = 0;
-    LOOKUP_AND_STORE = 1;
-    LOOKUP_ONLY = 2;
-    STORE_ONLY = 3;
-    LOOKUP_AND_STORE_SUCCESS = 4;
-  }
-  optional CachePolicy cache_policy = 31 [default = LOOKUP_AND_STORE];
-
-  // This is passed from gomacc to compiler proxy, and compiler proxy
-  // clears it before sending ExecReq to goma service.
-  optional RequesterEnv requester_env = 32;
-
-  // When hermetic_mode is true, restrict backend use the same compiler
-  // as local version. Backend should use a compiler package
-  // that has the same version string and the same binary_hash only.
-  // If there is no such compilers in backend, it should not run any other
-  // compiler but returns error: ExecResp contains error messages,
-  // no command spec and empty missing_input.  It isn't rpc error.
-  optional bool hermetic_mode = 33;
-
-  // Requests that the call is traced.
-  optional bool trace = 34;
-
-  // Expected output files and dirs, which are calculated in CompilerFlags.
-  // Goma server will return these files and dirs as output.
-  //
-  // If files which are not specified here are generated in the server,
-  // those files will be ignored (so, we can ignore temporary cache file or
-  // telemetry information).
-  //
-  // If files which are specified here are not generated in the server,
-  // the server just ignores such files. If the specified files do not look
-  // generated by a compiler, Goma server can return BAD_REQUEST.
-  //
-  // If output files and dirs are not specified, the server can calculate
-  // expected output files and dirs by itself (if possible).
-  //
-  // These path must be in client-format.
-  // Use '/' on Linux and Mac, and use '\\' for Windows.
-  repeated string expected_output_files = 35;
-  repeated string expected_output_dirs = 36;
-
-  // EXPERIMENTAL.
-  // True if ExecReq contains toolchains as input.
-  optional bool toolchain_included = 37;
-  // If ExecReq contains toolchains as input, set toolchain spec.
-  // toolchain spec should contain all compiler related files except system
-  // shared objects which are expected to exist in the server side. For
-  // example, in `clang` case, recent `clang` binary usually contains
-  // everything, so it is OK to have just `clang`. In `pnacl-clang` case, it
-  // needs libLLVM.so, and since it is a compiler wrapper, it needs related
-  // python scripts etc.
-  // Currently, if `dimensions` is set correctly, we don't need to add
-  // shared objects in /lib or /usr/lib.
-  //
-  // toolchain_specs should contain compiler-driver (which is set in
-  // command_spec) and subprograms, too.
-  repeated ToolchainSpec toolchain_specs = 38;
-
-  reserved 99;
-}
-
-// 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;
-
-  // 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;
-    BAD_REQUEST = -1;  // Non retryable error.
-  };
-  // Specifies the reason of ExecError::BAD_REQUEST.
-  enum BadRequestReasonCode {
-    // The reason is unknown (or not BAD_REQUEST)
-    UNKNOWN = 0;
-    // The request contains unsupported compiler flags.
-    UNSUPPORTED_COMPILER_FLAGS = 1;
-  };
-  enum CacheSource {
-    NO_CACHE = 0;
-    MEM_CACHE = 1;
-    STORAGE_CACHE = 2;
-    LOCAL_OUTPUT_CACHE = 3;
-  };
-  optional ExecResult result = 1;
-  optional ExecError error = 2 [default=OK];
-  optional BadRequestReasonCode bad_request_reason_code = 3 [default=UNKNOWN];
-
-  repeated string missing_input = 11;  // filename
-  repeated string missing_reason = 15;  // reasons of missing_input.
-  repeated string error_message = 12;
-
-  optional bool force_store_output_file_for_unmatched_hash = 13
-      [default=false];
-  optional bool force_store_output_file_for_unmatched_version = 14
-      [default=false];
-  optional bool force_store_output_file_for_unmatched_subprograms = 16
-      [default=false];
-
-  // for trace
-  optional string cache_key = 21;  // result cache_key
-  optional CacheSource cache_hit = 27;
-  reserved 22, 23;
-
-  // requester's compiler_proxy_id.
-  // for cached resp, it is the original requester, not current requester.
-  optional string requester_compiler_proxy_id = 26;
-
-
-  // Time at compiler_proxy
-  optional double compiler_proxy_time = 50;
-  optional double compiler_proxy_include_preproc_time = 51;
-  optional double compiler_proxy_include_fileload_time = 52;
-  optional double compiler_proxy_rpc_call_time = 53;
-  optional double compiler_proxy_file_response_time = 54;
-  optional double compiler_proxy_rpc_build_time = 55;
-  optional double compiler_proxy_rpc_send_time = 56;
-  optional double compiler_proxy_rpc_wait_time = 57;
-  optional double compiler_proxy_rpc_recv_time = 58;
-  optional double compiler_proxy_rpc_parse_time = 59;
-
-  optional double compiler_proxy_local_pending_time = 60;
-  optional double compiler_proxy_local_run_time = 61;
-
-  optional bool compiler_proxy_goma_finished = 70;
-  optional bool compiler_proxy_goma_cache_hit = 71;
-  optional bool compiler_proxy_goma_aborted = 72;
-  optional bool compiler_proxy_goma_error = 73;
-  optional bool compiler_proxy_local_finished = 74;
-  optional bool compiler_proxy_local_run = 75;
-  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;
-}
-
-// FileService Interface
-
-message StoreFileReq {
-  repeated FileBlob blob = 1;
-
-  optional RequesterInfo requester_info = 10;
-}
-
-message StoreFileResp {
-  repeated string hash_key = 1;  // sha256(blob) for success or "" for error
-}
-
-message LookupFileReq {
-  repeated string hash_key = 1;
-
-  optional RequesterInfo requester_info = 10;
-}
-
-message LookupFileResp {
-  repeated FileBlob blob = 2;
-}
-
-message EmptyMessage {
-}
-
-message HttpPortResponse {
-  required int32 port = 1;
-}
diff --git a/proto/api/goma_log.proto b/proto/api/goma_log.proto
deleted file mode 100644
index 97aee6c..0000000
--- a/proto/api/goma_log.proto
+++ /dev/null
@@ -1,256 +0,0 @@
-// Copyright 2011 Google LLC. All Rights Reserved.
-
-//
-// proto definitions for goma log.
-
-syntax = "proto2";
-
-package devtools_goma;
-
-option go_package = "go.chromium.org/goma/server/proto/api";
-
-// NEXT ID TO USE: 96
-message ExecLog {
-  enum AuthenticationType {
-    NONE = 0;
-    UNKNOWN = 1;
-    // reserved 2,3
-    OAUTH2_UNSPEC = 4;
-    OAUTH2_APPLICATION = 5;
-    OAUTH2_SERVICE_ACCOUNT = 6;
-    OAUTH2_GCE_SERVICE_ACCOUNT = 7;
-    OAUTH2_LUCI_LOCAL_AUTH = 8;
-  };
-  enum NetworkFailureType {
-    NO_NETWORK_ERROR = 0;  // succeeded or not used.
-    DISABLED = 1;  // failed because http is disabled (failnow() is true).
-    UNKNOWN_NETWORK_ERROR = 2;  // unknown error status.
-    CONNECT_FAILED = 3;  // failed at connect.
-    SEND_FAILED = 4;  // failed at send.
-    TIMEDOUT_AFTER_SEND = 5;  // timed out after request has sent.
-    RECEIVE_FAILED = 6;  // failed at receiving response.
-    BAD_HTTP_STATUS_CODE = 7;  // received but response was not 2xx.
-  };
-  enum CacheSource {
-    UNKNOWN_CACHE = 0;
-    MEM_CACHE = 1;
-    STORAGE_CACHE = 2;
-    LOCAL_OUTPUT_CACHE = 3;
-  };
-  // optional string compiler_proxy_id = 1;
-  optional string username = 46;
-  optional string nodename = 47;
-  optional int32 port = 48;
-  optional int32 compiler_proxy_start_time = 49;
-  optional int32 task_id = 50;
-  reserved 81;  // deprecated: use_case
-  optional string build_id = 91;
-  optional string service_account_id = 94;
-  optional string oauth2_email = 95;
-
-  optional string compiler_proxy_user_agent = 51;
-
-  // request info.
-  optional int32 start_time = 2;
-  repeated string arg = 3;
-  repeated string env = 4;
-  optional string cwd = 5;
-  repeated string expanded_arg = 62;
-
-  // command spec
-  optional string command_version = 71;
-  optional string command_target = 72;
-
-  optional string latest_input_filename = 59;
-  optional int32 latest_input_mtime = 60;
-
-  optional bool use_ssl = 64;
-  reserved 65;
-  optional AuthenticationType auth_type = 86 [default = NONE];
-  optional CpuFeatures cpu_features = 87;
-  optional NetworkFailureType network_failure_type = 88
-      [default = NO_NETWORK_ERROR];
-  optional OSInfo os_info = 89;
-
-  // in INIT.
-  optional int32 pending_time = 63;
-  // in SETUP.
-  optional int32 compiler_info_process_time = 82;
-  // include_preprocess_time is sum of
-  // include_processor_wait_time and include_processor_run_time.
-  optional int32 include_preprocess_time = 6;
-  optional int32 include_processor_wait_time = 84;
-  optional int32 include_processor_run_time = 85;
-  optional bool depscache_used = 78;
-  optional int32 include_preprocess_total_files = 79;
-  optional int32 include_preprocess_skipped_files = 80;
-
-  // in FILE_REQ.
-  optional int32 include_fileload_time = 7;
-  repeated int32 include_fileload_pending_time = 69;
-  repeated int32 include_fileload_run_time = 70;
-  optional int32 num_total_input_file = 8;
-  optional int64 total_input_file_size = 93;
-
-  // repeated by retry.
-  repeated int32 num_uploading_input_file = 9;
-  repeated int32 num_missing_input_file = 10;
-  repeated int32 num_dropped_input_file = 92;
-  repeated int32 num_file_uploaded_during_exec_failure = 66;
-  // repeated by each input file.
-  repeated int32 input_file_time = 11;
-  repeated int32 input_file_size = 12;
-
-  // in CALL_EXEC.  repeated by retry.
-  repeated int32 rpc_call_time = 13;
-  repeated int32 rpc_req_size = 14;
-  repeated int32 rpc_resp_size = 15;
-  repeated int32 rpc_raw_req_size = 16;
-  repeated int32 rpc_raw_resp_size = 17;
-  repeated string rpc_master_trace_id = 58;
-  repeated int32 rpc_throttle_time = 67;
-  repeated int32 rpc_pending_time = 57;
-  repeated int32 rpc_req_build_time = 18;
-  repeated int32 rpc_req_send_time = 19;
-  repeated int32 rpc_wait_time = 20;
-  repeated int32 rpc_resp_recv_time = 21;
-  repeated int32 rpc_resp_parse_time = 22;
-
-  // stats from backends. repeated by exec retry.
-
-  // in FILE_RESP.
-  optional int32 file_response_time = 32;
-  optional int32 num_output_file = 33;
-  // repeated by each output file
-  repeated int32 output_file_time = 34;
-  repeated int32 output_file_size = 35;
-  repeated int32 chunk_resp_size = 36;
-
-  // Total time elapsed for handling the request in compiler_proxy.
-  optional int32 handler_time = 37;
-
-  // result info
-  optional string exec_command_not_found = 76;
-  optional string exec_command_name_mismatch = 73;
-  optional string exec_command_target_mismatch = 74;
-  optional string exec_command_version_mismatch = 38;
-  optional string exec_command_binary_hash_mismatch = 39;
-  optional string exec_command_subprograms_mismatch = 75;
-  optional int32 exec_exit_status = 40;
-  optional int32 exec_request_retry = 41;
-  repeated string exec_request_retry_reason = 56;
-
-  // local run
-  optional string local_run_reason = 42;
-  optional int32 local_pending_time = 43;
-  optional int32 local_run_time = 44;
-  // TODO: use int32?
-  optional int64 local_mem_kb = 52;
-  repeated int32 local_output_file_time = 54;
-  // TODO: use int64?
-  repeated int32 local_output_file_size = 55;
-  optional int32 local_delay_time = 61;
-
-  optional bool cache_hit = 45;
-  optional CacheSource cache_source = 90;
-
-  // goma_error indicates result mismatch (exit status, stdout, stderr) between
-  // local and remote.
-  optional bool goma_error = 53;
-
-  // compiler_proxy_error indicates it replied failure exit status to gomacc
-  // while remote/local compilation have succeeded.
-  // so not genuie compilation failure.
-  optional bool compiler_proxy_error = 77;
-}
-
-// NEXT ID TO USE: 8
-message MemoryUsageLog {
-  // compiler_proxy identification
-  optional int32 compiler_proxy_start_time = 1;
-  optional string compiler_proxy_user_agent = 2;
-
-  // user information
-  optional string username = 3;
-  optional string nodename = 4;
-
-  // memory information
-  optional int64 memory = 5;
-  optional int64 virtual_memory = 7;
-  optional int64 time = 6;
-}
-
-message SaveLogReq {
-  repeated ExecLog exec_log = 1;
-  repeated MemoryUsageLog memory_usage_log = 2;
-}
-
-message SaveLogResp {
-}
-
-message NumberSummary {
-  optional int64 samples = 1;
-  optional int32 average = 2;
-
-  optional int32 minimum = 10;
-  optional int32 percentile_2 = 11;
-  optional int32 percentile_9 = 12;
-  optional int32 lower_quantile = 13;
-  optional int32 median = 14;
-  optional int32 upper_quantile = 15;
-  optional int32 percentile_91 = 16;
-  optional int32 percentile_98 = 17;
-  optional int32 maximum = 18;
-}
-
-// NEXT ID TO USE: 13
-message ExecLogStat {
-  optional NumberSummary handler_time = 1;
-
-  optional NumberSummary compiler_info_process_time = 12;
-  optional NumberSummary include_preprocess_time = 2;
-  optional NumberSummary include_fileload_time = 3;
-  optional NumberSummary rpc_call_time = 4;
-  optional NumberSummary file_response_time = 7;
-  optional NumberSummary local_pending_time = 8;
-  optional NumberSummary local_run_time = 9;
-
-  optional int64 cache_hit = 10;
-  optional int64 goma_error = 11;
-}
-
-// NEXT ID TO USE: 12
-message CpuFeatures {
-  optional bool mmx = 1;
-  optional bool sse = 2;
-  optional bool sse2 = 3;
-  optional bool sse3 = 4;
-  optional bool sse41 = 5;
-  optional bool sse42 = 6;
-  optional bool popcnt = 7;
-  optional bool avx = 8;
-  optional bool avx2 = 9;
-  optional bool aesni = 10;
-  optional bool non_stop_time_stamp_counter = 11;
-}
-
-// NEXT ID TO USE: 4
-message OSInfo {
-  message LinuxInfo {
-    optional string gnu_libc_version = 1;
-  };
-
-  message WinInfo {
-  };
-
-  message MacInfo {
-    // TODO: Have max_osx_major_version
-    optional int32 mac_osx_minor_version = 1;
-  };
-
-  oneof os_info_oneof {
-    LinuxInfo linux_info = 1;
-    WinInfo win_info = 2;
-    MacInfo mac_info = 3;
-  };
-}
diff --git a/proto/auth/acl.proto b/proto/auth/acl.proto
deleted file mode 100644
index d9b22fa..0000000
--- a/proto/auth/acl.proto
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2018 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.
-
-syntax = "proto3";
-
-package auth;
-
-option go_package = "go.chromium.org/goma/server/proto/auth";
-
-// Simple ACL.
-// https://docs.google.com/document/d/1xv8M0WqQyAanj-fM5Rv3wOMEis06HRwVEFLXjINOwC0/edit
-
-// Group defines a group of users that shares the same service account.
-// Different groups may share a service account.
-message Group {
-  string id = 1;
-  string description = 2;
-
-  // if audience is not empty, group member should have this audience
-  // in the token.
-  string audience = 3;
-
-  // matches mail in the token.
-  repeated string emails = 4;
-  // matches domain part of email in the token.
-  repeated string domains = 5;
-  // if both emails and domains are not specified, use id as external group id
-  // and check membership with external authentication database if given.
-
-  // use this service account for this group.
-  // it is used to read json in service-accounts volume.
-  // If service_account is "default", use server's default service account.
-  // If service_account is empty, pass EUC as is.
-  string service_account = 6;
-
-  // If reject is true, deny access from this group.
-  bool reject = 7;
-}
-
-message ACL {
-  // First matched group will be used.
-  repeated Group groups = 1;
-}
diff --git a/proto/auth/auth.proto b/proto/auth/auth.proto
deleted file mode 100644
index d15340e..0000000
--- a/proto/auth/auth.proto
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2018 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.
-
-syntax = "proto3";
-
-package auth;
-
-option go_package = "go.chromium.org/goma/server/proto/auth";
-
-import "google/protobuf/timestamp.proto";
-
-message AuthReq {
-  string authorization = 1;
-
-  // TODO: have method, request path?
-}
-
-message Token {
-  string access_token = 1;
-  string token_type = 2;
-}
-
-message AuthResp {
-  string email = 1;
-  google.protobuf.Timestamp expires_at = 3;
-
-  // number of requests allowed until expire.
-  // -1 means unlimited. 0 means disallow.
-  int32 quota = 4;
-  // TODO: group quota?
-
-  // error description for user.
-  string error_description = 5;
-
-  reserved 6;
-  Token token = 7;
-  // group that email belongs to.
-  string group_id = 8;
-}
diff --git a/proto/auth/auth_service.proto b/proto/auth/auth_service.proto
deleted file mode 100644
index 6792372..0000000
--- a/proto/auth/auth_service.proto
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright 2018 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.
-
-syntax = "proto3";
-
-package auth;
-
-option go_package = "go.chromium.org/goma/server/proto/auth";
-
-import "auth/auth.proto";
-
-service AuthService {
-  rpc Auth(AuthReq) returns (AuthResp) {}
-}
diff --git a/proto/auth/authdb.proto b/proto/auth/authdb.proto
deleted file mode 100644
index efc7dfd..0000000
--- a/proto/auth/authdb.proto
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2018 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.
-
-syntax = "proto3";
-
-package auth;
-
-option go_package = "go.chromium.org/goma/server/proto/auth";
-
-message CheckMembershipReq {
-  string email = 1;
-  string group = 2;
-}
-
-message CheckMembershipResp {
-  bool is_member = 1;
-}
diff --git a/proto/auth/authdb_service.proto b/proto/auth/authdb_service.proto
deleted file mode 100644
index b8795db..0000000
--- a/proto/auth/authdb_service.proto
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright 2018 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.
-
-syntax = "proto3";
-
-package auth;
-
-option go_package = "go.chromium.org/goma/server/proto/auth";
-
-import "auth/authdb.proto";
-
-service AuthDBService {
-  rpc CheckMembership(CheckMembershipReq) returns (CheckMembershipResp) {}
-}
diff --git a/proto/backend/backend.proto b/proto/backend/backend.proto
deleted file mode 100644
index 2b58c75..0000000
--- a/proto/backend/backend.proto
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright 2018 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.
-
-syntax = "proto3";
-
-package backend;
-
-option go_package = "go.chromium.org/goma/server/proto/backend";
-
-message LocalBackend {
-  // address of exec server. default "exec-server:5050"
-  string exec_addr = 1;
-  // address of file server. default "file-server:5050"
-  string file_addr = 2;
-  // address of execlog server. default "execlog-server:5050"
-  string execlog_addr = 3;
-  bool enable_bytestream = 4;
-
-  // attributes for cloud tracing when handling this backend request.
-  message TraceOption {
-    string namespace = 1;
-    string cluster = 2;
-  };
-  TraceOption trace_option = 5;
-};
-
-message HttpRpcBackend {
-  // target URL (scheme + host).
-  // request query will be preserved.
-  // e.g. "https://clients5.google.com/cxx-compiler-service"
-  string target = 1;
-};
-
-message RemoteBackend {
-  // target address.
-  // e.g. "goma.endpoints.goma-dev.cloud.goog:443"
-  string address = 1;
-
-  // api_key to access the backend.
-  // it is used to read api_key value in api-keys volume.
-  string api_key_name = 2;
-};
-
-message BackendMapping {
-  // id of group that uses the backend.
-  // group id matches with group id in ACL if not empty.
-  // empty group id will be used as default backend.
-  string group_id = 1;
-
-  // backend selection by query parameters, encoded form sorted by key
-  // as same as https://golang.org/pkg/net/url/#Values.Encode
-  // if specified, this backend will be used if all query parameters
-  // matches with query_params.
-  // if query_params is empty, any requests will match.
-  //
-  // it is not used for default backend (empty group id). i.e.
-  // if group_id is empty, query_params must be empty.
-  string query_params = 4;
-
-  // backend for the group.
-  oneof backend {
-    HttpRpcBackend http_rpc = 2;
-    RemoteBackend remote = 3;
-  }
-}
-
-message BackendRule {
-  repeated BackendMapping backends = 1;
-}
-
-message BackendConfig {
-  oneof backend {
-    // for frontend in backend cluster.
-    LocalBackend local = 1;
-
-    // debug only.
-    HttpRpcBackend http_rpc = 2;
-    RemoteBackend remote = 3;
-
-    // for frontend-mixer
-    BackendRule rule = 4;
-  }
-};
diff --git a/proto/cache/cache.proto b/proto/cache/cache.proto
deleted file mode 100644
index ecdb5ec..0000000
--- a/proto/cache/cache.proto
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2018 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.
-
-syntax = "proto3";
-
-package cache;
-
-option go_package = "go.chromium.org/goma/server/proto/cache";
-
-message KV {
-  string key = 1;
-  bytes  value = 2;
-}
-
-message GetReq {
-  string key = 1;
-  bool fast = 2;
-}
-
-message GetResp {
-  KV kv = 1;
-  bool in_memory = 2;
-}
-
-message PutReq {
-  KV kv = 1;
-  bool write_back = 2;
-}
-
-message PutResp {
-}
diff --git a/proto/cache/cache_service.proto b/proto/cache/cache_service.proto
deleted file mode 100644
index 00c7e83..0000000
--- a/proto/cache/cache_service.proto
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright 2018 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.
-
-syntax = "proto3";
-
-package cache;
-
-option go_package = "go.chromium.org/goma/server/proto/cache";
-
-import "cache/cache.proto";
-
-service CacheService {
-  rpc Get(GetReq) returns (GetResp) {}
-  rpc Put(PutReq) returns (PutResp) {}
-}
diff --git a/proto/command/command.proto b/proto/command/command.proto
deleted file mode 100644
index c1e1c33..0000000
--- a/proto/command/command.proto
+++ /dev/null
@@ -1,295 +0,0 @@
-// Copyright 2018 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.
-
-syntax = "proto3";
-
-package command;
-
-option go_package = "go.chromium.org/goma/server/proto/command";
-
-import "api/goma_data.proto";
-import "google/protobuf/timestamp.proto";
-
-// Selector is a command selector.
-// it is used to select a compiler or a subprogram/plugin to run on
-// cmd_server by matching it with CommandSpec or SubprogramSpec in a request
-// from goma client.
-// a subprogram/plugin use name and binary_hash for matching.
-// basename of path in SubprogramSpec should match with name in Selector.
-// TODO: consider also using target.
-message Selector {
-  // command name.
-  // name is command base name without version number and architecture.
-  // i.e. it should be equivalent CompilerFlags::GetCompilerName.
-  // e.g. "gcc", "g++", "clang", "libFindBadConstructs.so",
-  // "libFindBadConstructs.dylib".
-  string name = 1;
-  // version. e.g. "4.4.3[Ubuntu 4.4.3-4ubuntu5]"
-  string version = 2;
-  // compiler's target machine. e.g. gcc -dumpmachine.
-  string target = 3;
-  // binary hash of the command (sha256, hexencoded)
-  string binary_hash = 4;
-}
-
-// FileSpec is a file metadata. content is identified by hash.
-// NEXT_ID_TO_USE: 9
-message FileSpec {
-  // path in client file system.
-  // may be relative.
-  // for cmd descriptor, it is relative to command binary directory.
-  // (e.g. /usr/bin for /usr/bin/gcc).
-  // for CmdReq, it is relative to working directory specified in cmd.Dir.
-  string path = 1;
-  int64 size = 2;
-  string hash = 3;  // hash is empty, if file is symlink, or dir.
-  bool is_executable = 4;
-  string symlink = 5;  // hash is empty.
-
-  // used for goma api.
-  string hash_key = 6;
-  devtools_goma.FileBlob blob = 7;
-
-  reserved 8;
-  // TODO: retention period?
-}
-
-// Target is a target address.
-message Target {
-  string addr = 1;
-  // TBD: dial option?
-}
-
-// BuildInfo is image build info.
-message BuildInfo {
-  // deprecated. use cmd_server instead.
-  reserved 1;
-  reserved "commit";
-  // who build this toolchain.
-  string creator = 2;
-  // hostname where the toolchain was built.
-  string hostname = 3;
-  // current working directory of toolchain build.
-  string directory = 4;
-  // when this toolchain was built.
-  google.protobuf.Timestamp timestamp = 5;
-
-  // deprecated. use toolchain instead.
-  reserved 6;
-  reserved "upstreams";
-
-  // full image name of cmd_server container.
-  string cmd_server = 7;
-  // full qualified toolchain name (separated by space)
-  string toolchain = 8;
-
-  // notify?
-}
-
-// CmdDescriptor is a command descriptor.
-// NEXT ID TO USE: 7
-message CmdDescriptor {
-  enum PathType {
-    UNKNOWN_PATH_TYPE = 0;
-    POSIX = 1;  // unix-style, slash separated.
-    WINDOWS = 2;  // windows-style. backslash separated.
-  }
-
-  Selector selector = 1;
-  // command binaries to run.
-  // it includes driver program (e.g. gcc), and subprograms
-  // (e.g. cc1, cc1plus, as, objcopy etc).
-  // note: shared objects in standard dirs (/lib, /usr/lib, etc)
-  // will be included in image, so no need to install in each run.
-  // shared objects specified by RPATH (i.e. $ORIGIN/../lib, e.g.
-  // libstdc++.so.6 in chromium-clang) should be specified in files below.
-  //
-  // path is represented in path type.
-  message Setup {
-    // If cmd_file.Path is abs path, then this command binary is installed
-    // in image at the path, and no need to setup. when run command, use
-    // this path instead of local_compiler_path.  files would be empty.
-    //
-    // If cmd_file.Path is relative path from cmd_dir, then this command
-    // binary is installed at local_compiler_path as cmd run setup.  files
-    // would be also installed.
-    FileSpec cmd_file = 1;
-    string cmd_dir = 2;
-    repeated FileSpec files = 3;  // relative to cmd path or absolute.
-
-    PathType path_type = 4;
-  }
-  Setup setup = 2;
-
-  // cross options (when user's platform and cmd_server's platform differs)
-  message Cross {
-    // If clang_need_target is true, exec_service adds -target <arch>
-    // in args if args does not have -target option.
-    // note: it is clang/clang++ specific.
-    bool clang_need_target = 1;
-
-    // If windows_cross is true, it accepts compile request from windows,
-    // but run it on linux workers.
-    bool windows_cross = 2;
-
-    // TODO: implement followings.
-    // - needs path conversion
-    //   - nacl/win->linux
-    //   - pnacl/win->linux
-  }
-  Cross cross = 3;
-
-  // EmulationOpts is used when goma backend cannot fully emulate client environment.
-  message EmulationOpts {
-    // respect_client_include_paths is true if we need to append
-    // include directories sent from goma client to command line.
-    // For example, when we choose a binary is not relocatable, we need to
-    // specify -isystem, -imsvc (or equivalent)
-    bool respect_client_include_paths = 1;
-  }
-  EmulationOpts emulation_opts = 6;
-
-  reserved 4, 5;
-  reserved "cmd_opts", "package_opt";
-
-  // other options?
-  // TODO: more data to describe command (used in exec.Service).
-}
-
-// RemoteexecPlatform is a set of requirements, such as hardware,
-// operating system, for an remoteexec API.
-message RemoteexecPlatform {
-  message Property {
-    // The property name.
-    string name = 1;
-    // The property value.
-    string value = 2;
-  }
-
-  repeated Property properties = 1;
-
-  // Basename of RBE instance to use. e.g. "default_instance" or "windows".
-  string rbe_instance_basename = 2;
-
-  // Set true if nsjail is available in the platform image.
-  // TODO: deprecated. always requires najail on linux platform.
-  bool has_nsjail = 3;
-}
-
-// Config is a command config; mapping from selector.
-message Config {
-  Target   target = 1;
-  reserved 2;
-  reserved "image";
-  BuildInfo build_info = 3;
-  CmdDescriptor cmd_descriptor = 4;
-  RemoteexecPlatform remoteexec_platform = 5;
-
-  // If this config is configured for arbitrary toolchain support,
-  // set dimensions of the config. Otherwise, this should be nil.
-  repeated string dimensions = 6;
-
-  ACL acl = 7;
-}
-
-// ACL is access control list for requester.
-message ACL {
-  // Groups that is allowed to use.
-  // If allowed_groups specified, only groups in allowed_groups are
-  // allowed, and other groups are disallowed.
-  // If no allowed_groups specified, any groups are allowed
-  // if it is not disallowed by disallowed_groups.
-  repeated string allowed_groups = 1;
-
-  // Groups that is not allowed to use.
-  // If no disallowed_groups specified, only allowed_groups is allowed to use.
-  // If both are not specified, any groups are alllowed.
-  repeated string disallowed_groups = 2;
-}
-
-// Platform is a set of requirements, such as haredware, operting system
-// for RBE backend.
-// matched with build.bazel.remote.execution.v2.Platform.
-message Platform {
-  message Property {
-    string name = 1;
-    string value = 2;
-  }
-  repeated Property properties = 1;
-}
-
-// RuntimeConfig is config for runtime.
-// NEXT ID TO USE: 10
-message RuntimeConfig {
-  // name of runtime.
-  //
-  // if this runtime config found in a bucket,
-  // the following files will be detected in this name directory in the bucket:
-  //   seq
-  //   <prebuilt-item>/descriptors/<descriptorHash>
-  string name = 1;
-
-  // service address for the runtime. i.e. RBE address.
-  string service_addr = 2;
-
-  // If not nil, this runtime config will be also configured
-  // for arbitrary toolchain support.
-  // This is selector to use this runtime. i.e. if client request contains
-  // the dimentions, this runtime config will be selected.
-  PlatformRuntimeConfig platform_runtime_config = 6;
-
-  reserved 7;
-  reserved "rbe_instance_basename";
-
-  // Platform is a set of requirements, such as hardware, operating system
-  // for RBE backend.
-  Platform platform = 8;
-
-  // go/goma-toolchain-filter-per-cluster
-  // prefix of prebuilts in the runtime.
-  // disallowed takes precedence over allowed.
-
-  // prebuilts prefix to allow.
-  // if allowed_prebuilts specified, only prebuilts that are matched
-  // by allowed_prebuilts is allowed. other prebuilts are disallowed.
-  // if no allowed_prebuilts specified, any prebuilts are allowed
-  // if it is not disallowed by disallowed_prebuilts.
-  repeated string allowed_prebuilts = 3;
-
-  // prebuilts prefix to disallow.
-  // if no disallowed_prebuilts specified, only allowed_prebuilts is used.
-  // if both are not specified, all prebuilts are allowed.
-  repeated string disallowed_prebuilts = 4;
-
-  // commands that is disallowed in the runtime.
-  // selector field is used for exact match, if it is specified.
-  // selector field that is not specified in disallowed_commands will
-  // match any selector.
-  repeated Selector disallowed_commands = 5;
-
-  ACL acl = 9;
-}
-
-// PlatformRuntimeConfig is a config to use the runtime.
-// NEXT ID TO USE: 3
-message PlatformRuntimeConfig {
-  repeated string dimensions = 1;
-
-  // Set true if nsjail is available in the platform image.
-  bool has_nsjail = 2;
-}
-
-// ConfigMap is a config map; data source of Config.
-// admin creates/updates the file in <bucket>/<config>.config
-// and ConfigMapBucket will read the info.
-message ConfigMap {
-  repeated RuntimeConfig runtimes = 1;
-}
-
-
-// TODO: remove ConfigResp.
-message ConfigResp {
-  string version_id = 2;
-  repeated Config configs = 1;
-}
diff --git a/proto/command/command_service.proto b/proto/command/command_service.proto
deleted file mode 100644
index d34a550..0000000
--- a/proto/command/command_service.proto
+++ /dev/null
@@ -1,10 +0,0 @@
-// Copyright 2018 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.
-
-syntax = "proto3";
-
-package command;
-
-option go_package = "go.chromium.org/goma/server/proto/command";
-
diff --git a/proto/command/package_opts.proto b/proto/command/package_opts.proto
deleted file mode 100644
index 40acf9b..0000000
--- a/proto/command/package_opts.proto
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2018 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.
-
-syntax = "proto3";
-
-package command;
-
-option go_package = "go.chromium.org/goma/server/proto/command";
-
-// PackageOpts is a package option.
-// NEXT_ID_TO_USE: 8
-message PackageOpts {
-  // emulation_command specifies which emulation layer is necessary for this package.
-  // If empty, it means no emulation layer is necessary.
-  string emulation_command = 7;
-
-  // output_file_filters is a set of regexp to filter output files.
-  repeated string output_file_filters = 5;
-
-  reserved 1, 2, 3, 4, 6;
-}
diff --git a/proto/command/setup.proto b/proto/command/setup.proto
deleted file mode 100644
index 23d904b..0000000
--- a/proto/command/setup.proto
+++ /dev/null
@@ -1,115 +0,0 @@
-// Copyright 2018 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.
-
-syntax = "proto3";
-
-package command;
-
-option go_package = "go.chromium.org/goma/server/proto/command";
-
-// Install describes which command to be installed.
-// path is represented as server path (posix style),
-// and converted to client path in CmdDescriptor.Setup.
-//
-// NEXT ID TO USE: 9
-message Install {
-  // PrefixStyle represents a style of prefix.
-  // if it is ASIS, prefix parameter is read as-is.
-  // if it is REGEXP, prefix parameter is read as a regular expression.
-  // the install is automatically extended to all matching directories.
-  // e.g. if there is directory named clang-r123 and clang-r234, and
-  // clang-r[0-9]+ is set in prefix, install for both clang-r123 and clang-r234
-  // are made.
-  // in this case, you can use grouping i.e. parentheses, and you can use
-  // the variables in binary_hash_from field in ForSelector.
-  // if it is REGEXP_MAY_OMIT_BINARY_HASH_MISSING, then apply REGEXP rule
-  // but skip if a file specified with binary_hash_from is missing.
-  enum PrefixStyle {
-    ASIS = 0;
-    REGEXP = 1;
-    REGEXP_MAY_SKIP_BINARY_HASH_MISSING = 2;
-  }
-
-  // command key (e.g. "gcc", "clang++")
-  // note that this value will be Selector name in command.proto.
-  // in other words, this value must be the same with matching CommandSpec name
-  // in api/goma_data.proto if the install is a compiler.
-  // i.e. equivalent CompilerFlags::GetCompilerName.
-  //
-  // followings are known compiler keys (as of Jun 2017):
-  // gcc, g++, clang, clang++, cl.exe, clang-cl, javac, ps3ppusnc.exe
-  //
-  // for subprogram etc, it should be basename
-  // e.g. "libFindBadConstructs.so".
-  string key = 1;
-
-  // prefix dir (or toolchain root).
-  string prefix = 2;
-
-  // prefix_style represents how prefix need to be handled.
-  PrefixStyle prefix_style = 7;
-
-  // path in prefix dir.
-  // actual binary should be found in "prefix/path".
-  string path = 3;
-
-  // glob for additional files (i.e. wrapper scripts, libs etc).
-  // glob syntax is filepath.Match's.
-  // relative to prefix dir.
-  // if it ends with '/', all files under this dir will be added.
-  repeated string files = 4;
-
-  // ForSelector is used for setting selector for cross compile.
-  // selector's name and version is set by compiler specified by
-  // prefix and path above.
-  // note that setup_cmd in cmd_server is responsible for setting version and
-  // equivalent hash, and setting cross option in CmdDescriptor.
-  // for compilers, the result of --version option must be the same between
-  // ForSelector and Install.
-  // for subprograms, the behavior must be the same between ForSelector and
-  // Install.
-  // ForSelector and Install are expected to be made from the same source code.
-  message ForSelector {
-    // target of this cross compile.
-    // this is used for dispatching a compiler as a build target.
-    // note that target must be normalized.
-    // e.g. x86_64-darwin
-    string target = 1;
-    // binary_hash_from represents a filename to calculate hash when selector is
-    // made.
-    // e.g. darwin/third_party/llvm-build/Release+Asserts/bin/clang
-    //
-    // if prefix_style above is either of REGEXP or
-    // REGEXP_MAY_SKIP_BINARY_HASH_MISSING, you can use regexp grouping variable
-    // here.
-    // e.g. if prefix_style is REGEXP, prefix is linux/clang-([0-9]+) and
-    // found clang-12345, binary_hash_from darwin/clang-$1/bin/clang is
-    // automatically converted to darwin/clang-12345/bin/clang when this
-    // field is used.
-    // if prefix_style is REGEXP_MAY_SKIP_BINARY_HASH_MISSING and
-    // darwin/clang-12345/bin/clang does not exist, setup_cmd just skip
-    // to proceed setup of the command without error.
-    string binary_hash_from = 2;
-  }
-
-  ForSelector for_selector = 5;
-
-  // clang_need_target is true if --target option is always needed
-  // to run command.
-  // https://clang.llvm.org/docs/CrossCompilation.html#target-triple
-  // this can be also true if exec_server want to use client's raw target.
-  // e.g. set --target x86_64-apple-darwin-10.8.0 sent by client.
-  // note: this is clang/clang++ only.
-  bool clang_need_target = 6;
-
-  // windows_cross is true for windows cross compile on linux.
-  bool windows_cross = 8;
-}
-
-message Setup {
-  repeated Install installs = 1;
-
-  reserved 2;
-  reserved "package_opt";
-}
diff --git a/proto/copy_google_protobuf.sh b/proto/copy_google_protobuf.sh
deleted file mode 100755
index 00a6853..0000000
--- a/proto/copy_google_protobuf.sh
+++ /dev/null
@@ -1,38 +0,0 @@
-#!/bin/sh -e
-# Copyright 2019 Google LLC. All Rights Reserved.
-# well-known protobufs are shipped with protoc.
-#
-# /usr/local/bin/protoc -> /usr/local/include/google/protobuf/*
-# cipd protoc -> .cipd_bin/include/google/protobuf/*
-# libprotobuf-dev -> /usr/include/google/protobuf/*
-
-protocpath="$(which protoc)"
-case "${protocpath}" in
-/usr/local/bin/protoc)
-  # we install protoc in /usr/local in our docker image.
-  incdir=/usr/local/include/google/protobuf
-  ;;
-
-/usr/bin/protoc)
-  # protobuf-compiler + libprotobuf-dev
-  if [ ! -f /usr/include/google/protobuf/timestamp.proto ]; then
-    echo 'need to install libprotobuf-dev' >&2
-    exit 1
-  fi
-  incdir=/usr/include/google/protobuf
-  ;;
-
-*)
-  # cipd package (used in presubmit builder)
-  cipd_bin_path="$(dirname ${protocpath})"
-  while [ "$(basename $cipd_bin_path)" != ".cipd_bin" ]; do
-    if [ "$cipd_bin_path" = "/" ]; then
-      echo "protoc is not in cipd $protocpath"
-      exit 1
-    fi
-    cipd_bin_path="$(dirname $cipd_bin_path)"
-  done
-  incdir="${cipd_bin_path}/include/google/protobuf"
-  ;;
-esac
-cp "${incdir}/timestamp.proto" ./google/protobuf/timestamp.proto
diff --git a/proto/doc.go b/proto/doc.go
deleted file mode 100644
index ab1747f..0000000
--- a/proto/doc.go
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2017 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.
-
-// Package proto is top directory for proto packages.
-//
-// Standard goma APIs
-//
-//	api: package api defines data used on goma APIs.
-//	exec: package exec defines exec_service (/cxx-compiler-service/{e,me})
-//	file: package file defines file_service (/cxx-compiler-service/{l,s})
-//	execlog: package execlog defines log_service (/cxx-compiler-service/sl)
-//
-// New (internal) APIs
-//
-//	cache: package cache defines cache_service, backend of exec_service,
-//	file_service etc.
-//
-//	command: package command defines data and service to run command in
-//	isolated environment.
-package proto
-
-//go:generate ./gen_protoc-gen-go
-//go:generate ./copy_google_protobuf.sh
-//go:generate protoc -I. --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative api/goma_data.proto api/goma_log.proto
-//go:generate protoc -I. --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative exec/exec_service.proto
-//go:generate protoc -I. --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative file/file_service.proto
-//go:generate protoc -I. --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative execlog/log_service.proto
-
-//go:generate protoc -I. --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative cache/cache.proto cache/cache_service.proto
-
-//go:generate protoc -I. --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative command/command.proto command/command_service.proto command/setup.proto command/package_opts.proto
-
-//go:generate protoc -I. --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative auth/auth.proto auth/acl.proto auth/auth_service.proto auth/authdb.proto auth/authdb_service.proto
-
-//go:generate protoc -I. --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative backend/backend.proto
-
-//go:generate protoc -I. --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative settings/settings.proto settings/settings_service.proto
-
-//go:generate protoc -I. --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative nsjail/config.proto
diff --git a/proto/exec/exec_service.proto b/proto/exec/exec_service.proto
deleted file mode 100644
index 48f1704..0000000
--- a/proto/exec/exec_service.proto
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2018 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.
-
-syntax = "proto2";
-
-package devtools_goma;
-
-option go_package = "go.chromium.org/goma/server/proto/exec";
-option cc_generic_services = false;
-option java_generic_services = false;
-option py_generic_services = false;
-
-import "api/goma_data.proto";
-
-// TODO: reconsider good error codes.
-enum ExecServiceApplicationError {
-  BAD_REQUEST = -1;
-  EXEC_OK = 0;
-  EXECUTABLE_NOT_READY = 1;
-  DISK_EXCEEDED = 2;
-  EXEC_INTERNAL_ERROR = 3;
-  EXECUTOR_IS_LOADING = 4;
-  EXECUTOR_MEMORY_NOT_ENOUGH = 5;
-}
-
-service ExecService {
-  rpc Exec(ExecReq) returns (ExecResp) {
-  }
-}
diff --git a/proto/execlog/log_service.proto b/proto/execlog/log_service.proto
deleted file mode 100644
index ae364fb..0000000
--- a/proto/execlog/log_service.proto
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2011 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.
-
-
-syntax = "proto2";
-
-package devtools_goma;
-
-option go_package = "go.chromium.org/goma/server/proto/execlog";
-option cc_generic_services = false;
-option java_generic_services = false;
-option py_generic_services = false;
-
-import "api/goma_log.proto";
-
-service LogService {
-  rpc SaveLog(SaveLogReq) returns (SaveLogResp) {
-  }
-}
diff --git a/proto/file/file_service.proto b/proto/file/file_service.proto
deleted file mode 100644
index 9bd381e..0000000
--- a/proto/file/file_service.proto
+++ /dev/null
@@ -1,22 +0,0 @@
-// 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.
-
-//
-// goma file service API
-
-syntax = "proto2";
-
-package devtools_goma;
-
-option go_package = "go.chromium.org/goma/server/proto/file";
-option cc_generic_services = false;
-option java_generic_services = false;
-option py_generic_services = false;
-
-import "api/goma_data.proto";
-
-service FileService {
-  rpc StoreFile(StoreFileReq) returns (StoreFileResp) {}
-  rpc LookupFile(LookupFileReq) returns (LookupFileResp) {}
-}
diff --git a/proto/gen_protoc-gen-go b/proto/gen_protoc-gen-go
deleted file mode 100755
index ec3cf8a..0000000
--- a/proto/gen_protoc-gen-go
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/bin/sh
-# Copyright 2018 Google Inc. All Rights Reserved.
-version="$(go list -m -f '{{.Version}}' google.golang.org/protobuf)"
-go install "google.golang.org/protobuf/cmd/protoc-gen-go@${version}"
-version="$(go list -m -f '{{.Version}}' google.golang.org/grpc/cmd/protoc-gen-go-grpc)"
-if [ -z "${version}" ]; then
-  go install "google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest"
-else
-  go install "google.golang.org/grpc/cmd/protoc-gen-go-grpc@${version}"
-fi
diff --git a/proto/google/protobuf/.gitignore b/proto/google/protobuf/.gitignore
deleted file mode 100644
index 09dc61e..0000000
--- a/proto/google/protobuf/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-timestamp.proto
diff --git a/proto/google/protobuf/timestamp.proto b/proto/google/protobuf/timestamp.proto
deleted file mode 100644
index fd0bc07..0000000
--- a/proto/google/protobuf/timestamp.proto
+++ /dev/null
@@ -1,144 +0,0 @@
-// Protocol Buffers - Google's data interchange format
-// Copyright 2008 Google Inc.  All rights reserved.
-// https://developers.google.com/protocol-buffers/
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-//     * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-//     * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-//     * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-syntax = "proto3";
-
-package google.protobuf;
-
-option cc_enable_arenas = true;
-option go_package = "google.golang.org/protobuf/types/known/timestamppb";
-option java_package = "com.google.protobuf";
-option java_outer_classname = "TimestampProto";
-option java_multiple_files = true;
-option objc_class_prefix = "GPB";
-option csharp_namespace = "Google.Protobuf.WellKnownTypes";
-
-// 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
-//
-// Example 1: Compute Timestamp from POSIX `time()`.
-//
-//     Timestamp timestamp;
-//     timestamp.set_seconds(time(NULL));
-//     timestamp.set_nanos(0);
-//
-// Example 2: Compute Timestamp from POSIX `gettimeofday()`.
-//
-//     struct timeval tv;
-//     gettimeofday(&tv, NULL);
-//
-//     Timestamp timestamp;
-//     timestamp.set_seconds(tv.tv_sec);
-//     timestamp.set_nanos(tv.tv_usec * 1000);
-//
-// Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`.
-//
-//     FILETIME ft;
-//     GetSystemTimeAsFileTime(&ft);
-//     UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
-//
-//     // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z
-//     // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z.
-//     Timestamp timestamp;
-//     timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL));
-//     timestamp.set_nanos((INT32) ((ticks % 10000000) * 100));
-//
-// Example 4: Compute Timestamp from Java `System.currentTimeMillis()`.
-//
-//     long millis = System.currentTimeMillis();
-//
-//     Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000)
-//         .setNanos((int) ((millis % 1000) * 1000000)).build();
-//
-// Example 5: Compute Timestamp from Java `Instant.now()`.
-//
-//     Instant now = Instant.now();
-//
-//     Timestamp timestamp =
-//         Timestamp.newBuilder().setSeconds(now.getEpochSecond())
-//             .setNanos(now.getNano()).build();
-//
-// Example 6: Compute Timestamp from current time in Python.
-//
-//     timestamp = Timestamp()
-//     timestamp.GetCurrentTime()
-//
-// # JSON Mapping
-//
-// In JSON format, the Timestamp type is encoded as a string in the
-// [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the
-// format is "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z"
-// where {year} is always expressed using four digits while {month}, {day},
-// {hour}, {min}, and {sec} are zero-padded to two digits each. The fractional
-// seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution),
-// are optional. The "Z" suffix indicates the timezone ("UTC"); the timezone
-// is required. A proto3 JSON serializer should always use UTC (as indicated by
-// "Z") when printing the Timestamp type and a proto3 JSON parser should be
-// able to accept both UTC and other timezones (as indicated by an offset).
-//
-// For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past
-// 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)
-// 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://joda-time.sourceforge.net/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime()
-// ) 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.
-  int64 seconds = 1;
-
-  // Non-negative fractions of a second at nanosecond resolution. Negative
-  // second values with fractions must still have non-negative nanos values
-  // that count forward in time. Must be from 0 to 999,999,999
-  // inclusive.
-  int32 nanos = 2;
-}
diff --git a/proto/importer.go b/proto/importer.go
deleted file mode 100644
index 5289990..0000000
--- a/proto/importer.go
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2021 Google LLC. All Rights Reserved.
-//go:build ignore
-// +build ignore
-
-// Binary importer imports proto files.
-//
-// Usage:
-//
-//	$ go run importer.go -src /path/to/devtools/goma
-//
-// TODO: implement this.
-package main
-
-func main() {}
diff --git a/proto/nsjail/README.md b/proto/nsjail/README.md
deleted file mode 100644
index 173a86f..0000000
--- a/proto/nsjail/README.md
+++ /dev/null
@@ -1,22 +0,0 @@
-# nsjail config proto
-
-protobuf schema for [nsjail](http://nsjail.com/)
-([GitHub](https://github.com/google/nsjail)).
-This is used for providing hermetic build environment with
-arbitrary toolchain support.
-
-## How to update the file?
-
-1. git clone
-
-```shell
-$ git clone https://github.com/google/nsjail.git
-```
-
-1. copy config.proto file.
-
-```shell
-$ cp nsjail/config.proto .
-```
-
-1. Add `option go_package = "go.chromium.org/goma/server/proto/nsjail";`
diff --git a/proto/nsjail/config.proto b/proto/nsjail/config.proto
deleted file mode 100644
index a2c43ea..0000000
--- a/proto/nsjail/config.proto
+++ /dev/null
@@ -1,273 +0,0 @@
-syntax = "proto2";
-
-package nsjail;
-
-option go_package = "go.chromium.org/goma/server/proto/nsjail";
-
-enum Mode {
-    LISTEN = 0; /* Listening on a TCP port */
-    ONCE = 1;	/* Running the command once only */
-    RERUN = 2;	/* Re-executing the command (forever) */
-    EXECVE = 3; /* Executing command w/o the supervisor */
-}
-/* Should be self explanatory */
-enum LogLevel {
-    DEBUG = 0;	 /* Equivalent to the '-v' cmd-line option */
-    INFO = 1;	 /* Default level */
-    WARNING = 2; /* Equivalent to the '-q' cmd-line option */
-    ERROR = 3;
-    FATAL = 4;
-}
-message IdMap {
-    /* Empty string means "current uid/gid" */
-    optional string inside_id = 1 [default = ""];
-    optional string outside_id = 2 [default = ""];
-    /* See 'man user_namespaces' for the meaning of count */
-    optional uint32 count = 3 [default = 1];
-    /* Does this map use /usr/bin/new[u|g]idmap binary? */
-    optional bool use_newidmap = 4 [default = false];
-}
-message MountPt {
-    /* Can be skipped for filesystems like 'proc' */
-    optional string src = 1 [default = ""];
-    /* Should 'src' path be prefixed with this envar? */
-    optional string prefix_src_env = 2 [default = ""];
-    /* If specified, contains buffer that will be written to the dst file */
-    optional bytes src_content = 3 [default = ""];
-    /* Mount point inside jail */
-    required string dst = 4 [default = ""];
-    /* Should 'dst' path be prefixed with this envar? */
-    optional string prefix_dst_env = 5 [default = ""];
-    /* Can be empty for mount --bind mounts */
-    optional string fstype = 6 [default = ""];
-    /* E.g. size=5000000 for 'tmpfs' */
-    optional string options = 7 [default = ""];
-    /* Is it a 'mount --bind src dst' type of mount? */
-    optional bool is_bind = 8 [default = false];
-    /* Is it a R/W mount? */
-    optional bool rw = 9 [default = false];
-    /* Is it a directory? If not specified an internal
-       heuristics will be used to determine that */
-    optional bool is_dir = 10;
-    /* Should the sandboxing fail if we cannot mount this resource? */
-    optional bool mandatory = 11 [default = true];
-    /* Is it a symlink (instead of real mount point)? */
-    optional bool is_symlink = 12 [default = false];
-    /* Is it a nosuid mount */
-    optional bool nosuid = 13 [default = false];
-    /* Is it a nodev mount */
-    optional bool nodev = 14 [default = false];
-    /* Is it a noexec mount */
-    optional bool noexec = 15 [default = false];
-}
-enum RLimit {
-    VALUE = 0; /* Use the provided value */
-    SOFT = 1;  /* Use the current soft rlimit */
-    HARD = 2;  /* Use the current hard rlimit */
-    INF = 3;   /* Use RLIM64_INFINITY */
-}
-message Exe {
-    /* Will be used both as execv's path and as argv[0] */
-    required string path = 1;
-    /* This will be argv[1] and so on.. */
-    repeated string arg = 2;
-    /* Override argv[0] */
-    optional string arg0 = 3;
-    /* Should execveat() be used to execute a file-descriptor instead? */
-    optional bool exec_fd = 4 [default = false];
-}
-message NsJailConfig {
-    /* Optional name and description for this config */
-    optional string name = 1 [default = ""];
-    repeated string description = 2;
-
-    /* Execution mode: see 'msg Mode' description for more */
-    optional Mode mode = 3 [default = ONCE];
-    /* Hostname inside jail */
-    optional string hostname = 4 [default = "NSJAIL"];
-    /* Initial current working directory for the binary */
-    optional string cwd = 5 [default = "/"];
-
-    /* Defines whether to use switch_root or pivot_root */
-    optional bool no_pivotroot = 6 [default = false];
-
-    /* TCP port to listen to. Valid with mode=LISTEN only */
-    optional uint32 port = 7 [default = 0];
-    /* Host to bind to for mode=LISTEN. Must be in IPv6 format */
-    optional string bindhost = 8 [default = "::"];
-    /* For mode=LISTEN, maximum number of connections across all IPs */
-    optional uint32 max_conns = 9 [default = 0];
-    /* For mode=LISTEN, maximum number of connections from a single IP */
-    optional uint32 max_conns_per_ip = 10 [default = 0];
-
-    /* Wall-time time limit for commands */
-    optional uint32 time_limit = 11 [default = 600];
-    /* Should nsjail go into background? */
-    optional bool daemon = 12 [default = false];
-    /* Maximum number of CPUs to use: 0 - no limit */
-    optional uint32 max_cpus = 13 [default = 0];
-
-    /* FD to log to. */
-    optional int32 log_fd = 14;
-    /* File to save logs to. */
-    optional string log_file = 15;
-    /* Minimum log level displayed.
-       See 'msg LogLevel' description for more */
-    optional LogLevel log_level = 16;
-
-    /* Should the current environment variables be kept
-       when executing the binary */
-    optional bool keep_env = 17 [default = false];
-    /* EnvVars to be set before executing binaries. If the envar doesn't contain '='
-       (e.g. just the 'DISPLAY' string), the current envar value will be used */
-    repeated string envar = 18;
-
-    /* Should capabilities be preserved or dropped */
-    optional bool keep_caps = 19 [default = false];
-    /* Which capabilities should be preserved if keep_caps == false.
-       Format: "CAP_SYS_PTRACE" */
-    repeated string cap = 20;
-    /* Should nsjail close FD=0,1,2 before executing the process */
-    optional bool silent = 21 [default = false];
-    /* Should the child process have control over terminal?
-       Can be useful to allow /bin/sh to provide
-       job control / signals. Dangerous, can be used to put
-       characters into the controlling terminal back */
-    optional bool skip_setsid = 22 [default = false];
-    /* Redirect sdterr of the process to /dev/null instead of the socket or original TTY */
-    optional bool stderr_to_null = 23 [default = false];
-    /* Which FDs should be passed to the newly executed process
-       By default only FD=0,1,2 are passed */
-    repeated int32 pass_fd = 24;
-    /* Setting it to true will allow to have set-uid binaries
-       inside the jail */
-    optional bool disable_no_new_privs = 25 [default = false];
-
-    /* Various rlimits, the rlimit_as/rlimit_core/... are used only if
-       rlimit_as_type/rlimit_core_type/... are set to RLimit::VALUE */
-    optional uint64 rlimit_as = 26 [default = 4096]; /* In MiB */
-    optional RLimit rlimit_as_type = 27 [default = VALUE];
-    optional uint64 rlimit_core = 28 [default = 0]; /* In MiB */
-    optional RLimit rlimit_core_type = 29 [default = VALUE];
-    optional uint64 rlimit_cpu = 30 [default = 600]; /* In seconds */
-    optional RLimit rlimit_cpu_type = 31 [default = VALUE];
-    optional uint64 rlimit_fsize = 32 [default = 1]; /* In MiB */
-    optional RLimit rlimit_fsize_type = 33 [default = VALUE];
-    optional uint64 rlimit_nofile = 34 [default = 32];
-    optional RLimit rlimit_nofile_type = 35 [default = VALUE];
-    /* RLIMIT_NPROC is system-wide - tricky to use; use the soft limit value by
-     * default here */
-    optional uint64 rlimit_nproc = 36 [default = 1024];
-    optional RLimit rlimit_nproc_type = 37 [default = SOFT];
-    /* In MiB, use the soft limit value by default */
-    optional uint64 rlimit_stack = 38 [default = 8];
-    optional RLimit rlimit_stack_type = 39 [default = SOFT];
-    /* In KB, use the soft limit value by default */
-    optional uint64 rlimit_memlock = 40 [default = 64];
-    optional RLimit rlimit_memlock_type = 41 [default = SOFT];
-    optional uint64 rlimit_rtprio = 42 [default = 0];
-    optional RLimit rlimit_rtprio_type = 43 [default = SOFT];
-    optional uint64 rlimit_msgqueue = 44 [default = 1024]; /* In bytes */
-    optional RLimit rlimit_msgqueue_type = 45 [default = SOFT];
-
-    /* Disable all rlimits, default to limits set by parent */
-    optional bool disable_rl = 46 [default = false];
-
-    /* See 'man personality' for more */
-    optional bool persona_addr_compat_layout = 47 [default = false];
-    optional bool persona_mmap_page_zero = 48 [default = false];
-    optional bool persona_read_implies_exec = 49 [default = false];
-    optional bool persona_addr_limit_3gb = 50 [default = false];
-    optional bool persona_addr_no_randomize = 51 [default = false];
-
-    /* Which name-spaces should be used? */
-    optional bool clone_newnet = 52 [default = true];
-    optional bool clone_newuser = 53 [default = true];
-    optional bool clone_newns = 54 [default = true];
-    optional bool clone_newpid = 55 [default = true];
-    optional bool clone_newipc = 56 [default = true];
-    optional bool clone_newuts = 57 [default = true];
-    /* Disable for kernel versions < 4.6 as it's not supported there */
-    optional bool clone_newcgroup = 58 [default = true];
-    /* Supported with kernel versions >= 5.3 */
-    optional bool clone_newtime = 59 [default = false];
-
-    /* Mappings for UIDs and GIDs. See the description for 'msg IdMap'
-       for more */
-    repeated IdMap uidmap = 60;
-    repeated IdMap gidmap = 61;
-
-    /* Should /proc be mounted (R/O)? This can also be added in the 'mount'
-       section below */
-    optional bool mount_proc = 62 [default = false];
-    /* Mount points inside the jail. See the description for 'msg MountPt'
-       for more */
-    repeated MountPt mount = 63;
-
-    /* Kafel seccomp-bpf policy file or a string:
-       Homepage of the project: https://github.com/google/kafel */
-    optional string seccomp_policy_file = 64;
-    repeated string seccomp_string = 65;
-    /* Setting it to true makes audit write seccomp logs to dmesg */
-    optional bool seccomp_log = 66 [default = false];
-
-    /* If > 0, maximum cumulative size of RAM used inside any jail */
-    optional uint64 cgroup_mem_max = 67 [default = 0]; /* In bytes */
-    /* If > 0, maximum cumulative size of RAM + swap used inside any jail */
-    optional uint64 cgroup_mem_memsw_max = 91 [default = 0]; /* In bytes */
-    /* If >= 0, maximum cumulative size of swap used inside any jail */
-    optional int64 cgroup_mem_swap_max = 92 [default = -1]; /* In bytes */
-    /* Mount point for cgroups-memory in your system */
-    optional string cgroup_mem_mount = 68 [default = "/sys/fs/cgroup/memory"];
-    /* Writeable directory (for the nsjail user) under cgroup_mem_mount */
-    optional string cgroup_mem_parent = 69 [default = "NSJAIL"];
-
-    /* If > 0, maximum number of PIDs (threads/processes) inside jail */
-    optional uint64 cgroup_pids_max = 70 [default = 0];
-    /* Mount point for cgroups-pids in your system */
-    optional string cgroup_pids_mount = 71 [default = "/sys/fs/cgroup/pids"];
-    /* Writeable directory (for the nsjail user) under cgroup_pids_mount */
-    optional string cgroup_pids_parent = 72 [default = "NSJAIL"];
-
-    /* If > 0, Class identifier of network packets inside jail */
-    optional uint32 cgroup_net_cls_classid = 73 [default = 0];
-    /* Mount point for cgroups-net-cls in your system */
-    optional string cgroup_net_cls_mount = 74 [default = "/sys/fs/cgroup/net_cls"];
-    /* Writeable directory (for the nsjail user) under cgroup_net_mount */
-    optional string cgroup_net_cls_parent = 75 [default = "NSJAIL"];
-
-    /* If > 0, number of milliseconds of CPU time per second that jailed processes can use */
-    optional uint32 cgroup_cpu_ms_per_sec = 76 [default = 0];
-    /* Mount point for cgroups-cpu in your system */
-    optional string cgroup_cpu_mount = 77 [default = "/sys/fs/cgroup/cpu"];
-    /* Writeable directory (for the nsjail user) under cgroup_cpu_mount */
-    optional string cgroup_cpu_parent = 78 [default = "NSJAIL"];
-
-    /* Mount point for cgroup v2 in your system */
-    optional string cgroupv2_mount = 79 [default = "/sys/fs/cgroup"];
-    /* Use cgroup v2 */
-    optional bool use_cgroupv2 = 80 [default = false];
-
-    /* Should the 'lo' interface be brought up (active) inside this jail? */
-    optional bool iface_no_lo = 81 [default = false];
-
-    /* Put this interface inside the jail */
-    repeated string iface_own = 82;
-
-    /* Parameters for the cloned MACVLAN interface inside jail */
-    optional string macvlan_iface = 83; /* Interface to be cloned, eg 'eth0' */
-    optional string macvlan_vs_ip = 84 [default = "192.168.0.2"];
-    optional string macvlan_vs_nm = 85 [default = "255.255.255.0"];
-    optional string macvlan_vs_gw = 86 [default = "192.168.0.1"];
-    optional string macvlan_vs_ma = 87 [default = ""];
-    optional string macvlan_vs_mo = 88 [default = "private"];
-
-    /* Niceness level of the jailed process */
-    optional int32 nice_level = 89 [default = 19];
-
-    /* Binary path (with arguments) to be executed. If not specified here, it
-       can be specified with cmd-line as "-- /path/to/command arg1 arg2" */
-    optional Exe exec_bin = 90;
-
-    optional bool disable_tsc = 93 [default = false];
-}
diff --git a/proto/settings/settings.proto b/proto/settings/settings.proto
deleted file mode 100644
index 21f59a7..0000000
--- a/proto/settings/settings.proto
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2018 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.
-
-syntax = "proto3";
-
-package settings;
-
-option go_package = "go.chromium.org/goma/server/proto/settings";
-
-message Settings {
-  string name = 3;
-
-  string endpoint_url = 1;
-  string certificate = 2;
-
-  // requires oauth2 for cloud service.
-  bool oauth2_cloud = 4;
-}
-
-message SettingsReq {
-  reserved 1;
-  string use_case = 2;
-}
-
-message SettingsResp {
-  Settings settings = 1;
-}
diff --git a/proto/settings/settings_service.proto b/proto/settings/settings_service.proto
deleted file mode 100644
index 0df136a..0000000
--- a/proto/settings/settings_service.proto
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright 2018 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.
-
-syntax = "proto3";
-
-package settings;
-
-option go_package = "go.chromium.org/goma/server/proto/settings";
-
-import "settings/settings.proto";
-
-service SettingsService {
-  rpc Get(SettingsReq) returns (SettingsResp) {}
-}
diff --git a/remoteexec/adapter.go b/remoteexec/adapter.go
deleted file mode 100644
index b8a64f7..0000000
--- a/remoteexec/adapter.go
+++ /dev/null
@@ -1,452 +0,0 @@
-// Copyright 2018 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.
-
-// Package remoteexec provides proxy to remoteexec server.
-//
-// TODO: goma client should use credential described in
-// https://cloud.google.com/remote-build-execution/docs/getting-started
-package remoteexec
-
-import (
-	"context"
-	"fmt"
-	"path"
-	"strings"
-	"sync"
-	"time"
-
-	rpb "github.com/bazelbuild/remote-apis/build/bazel/remote/execution/v2"
-
-	"go.opencensus.io/stats"
-	"go.opencensus.io/trace"
-	"google.golang.org/grpc"
-	"google.golang.org/grpc/credentials/oauth"
-	"google.golang.org/grpc/metadata"
-	"google.golang.org/protobuf/proto"
-	"google.golang.org/protobuf/types/known/durationpb"
-
-	"go.chromium.org/goma/server/auth/enduser"
-	"go.chromium.org/goma/server/exec"
-	"go.chromium.org/goma/server/log"
-	gomapb "go.chromium.org/goma/server/proto/api"
-	execpb "go.chromium.org/goma/server/proto/exec"
-	fpb "go.chromium.org/goma/server/proto/file"
-	"go.chromium.org/goma/server/remoteexec/cas"
-	"go.chromium.org/goma/server/remoteexec/digest"
-	"go.chromium.org/goma/server/server"
-)
-
-// DigetCache caches digest for goma file hash.
-type DigestCache interface {
-	Get(context.Context, string, digest.Source) (digest.Data, error)
-}
-
-// SpanTimeout specifies Timeout for exec span.
-// 0 is no time out.
-type SpanTimeout struct {
-	Inventory    time.Duration
-	InputTree    time.Duration
-	Setup        time.Duration
-	CheckCache   time.Duration
-	CheckMissing time.Duration
-	UploadBlobs  time.Duration
-	Execute      time.Duration
-	Response     time.Duration
-}
-
-// DefaultSpanTimeout is default timeout.
-var DefaultSpanTimeout = SpanTimeout{
-	Inventory:    1 * time.Second,
-	InputTree:    60 * time.Second,
-	Setup:        1 * time.Second,
-	CheckCache:   3 * time.Second,
-	CheckMissing: 10 * time.Second,
-	UploadBlobs:  60 * time.Second,
-	Execute:      0,
-	Response:     30 * time.Second,
-}
-
-// Adapter is an adapter from goma API to remoteexec API.
-type Adapter struct {
-	execpb.UnimplementedExecServiceServer
-	// InstancePrefix is the prefix (dirname) of the full RBE instance name.
-	// e.g. If instance name == "projects/$PROJECT/instances/default_instance",
-	// then InstancePrefix is "projects/$PROJECT/instances"
-	InstancePrefix string
-
-	// InstanceBaseName is the name (basename) of the full RBE instance name.
-	// If emtpy, use "default_instance".
-	InstanceBaseName string
-
-	Inventory exec.Inventory
-	// ExecTimeout is timeout of Action in RBE.
-	ExecTimeout time.Duration
-	// SpanTimeout is timeout of each span in a Goma Exec request.
-	SpanTimeout SpanTimeout
-
-	// Client is remoteexec API client.
-	Client         Client
-	InsecureClient bool
-
-	// GomaFile handles output files from remoteexec's cas to goma's FileBlob.
-	GomaFile fpb.FileServiceClient
-
-	// key: goma file hash.
-	DigestCache DigestCache
-
-	// CmdStorage is a storage for command files.
-	CmdStorage CmdStorage
-
-	// Tool details put in request metadata.
-	ToolDetails *rpb.ToolDetails
-
-	// FileLookupSema specifies concurrency to look up file
-	// contents from file-cache-server to be converted to CAS.
-	FileLookupSema chan struct{}
-
-	// CASBlobLookupSema specifies concurrency to look up file blobs in in cas.lookupBlobsInStore(),
-	// 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{}
-
-	// Ratio to enable hardening.
-	HardeningRatio float64
-	// Ratio to use nsjail for hardening.
-	NsjailRatio float64
-	// sha256 file hash to disable hardening.
-	DisableHardenings []string
-
-	// MissingInputLimit is the maximum number of missing inputs to list in
-	// a response. If there are more, the server randomly picks this many
-	// inputs to respond with. 0 indicates no limit.
-	MissingInputLimit int
-
-	capMu        sync.Mutex
-	capabilities *rpb.ServerCapabilities
-}
-
-func (f *Adapter) withRequestMetadata(ctx context.Context, reqInfo *gomapb.RequesterInfo) (context.Context, error) {
-	rmd := &rpb.RequestMetadata{
-		ToolDetails:      f.ToolDetails,
-		ActionId:         reqInfo.GetCompilerProxyId(),
-		ToolInvocationId: reqInfo.GetBuildId(),
-		// TODO: b/77176746
-		// CorrelatedInvocationsId:
-	}
-	// TODO: remove the following workaround.
-	//                    when autoninja is used for the build,
-	//                    and client is rolled, we do not need
-	//                    the following workaround.
-	//
-	// workaround b/77176764
-	// set compiler_proxy_id prefix to ToolInvocationId if chrome-bot@
-	// chrome-bot@ (aka buildbot) start/stop compiler_proxy per each
-	// compile step, so compiler_proxy_id prefix would be good fit
-	// for ToolInvocationId.
-	// Typical user keeps running compiler_proxy, so same
-	// compiler_proxy_id prefix will be used for several ninja invocation.
-	// We cannot use that for human users.
-	id := reqInfo.GetCompilerProxyId()
-	if rmd.ToolInvocationId == "" && strings.HasPrefix(id, "chrome-bot@") {
-		i := strings.LastIndexByte(id, '/')
-		if i > 0 {
-			rmd.ToolInvocationId = id[:i]
-		}
-	}
-
-	logger := log.FromContext(ctx)
-	logger.Infof("request metadata: %s", rmd)
-	b, err := proto.Marshal(rmd)
-	if err != nil {
-		return nil, err
-	}
-	// https://github.com/bazelbuild/remote-apis/blob/a5c577357528b33a4adff88c0c7911dd086c6923/build/bazel/remote/execution/v2/remote_execution.proto#L1460
-	// https://github.com/grpc/grpc-go/blob/a094a1095c07d37362f7ab37b92a6aa46c2d8b07/Documentation/grpc-metadata.md#storing-binary-data-in-metadata
-	return metadata.AppendToOutgoingContext(ctx,
-		"build.bazel.remote.execution.v2.requestmetadata-bin",
-		string(b)), nil
-}
-
-type contextKey int
-
-var requesterInfoKey contextKey
-
-func (f *Adapter) outgoingContext(ctx context.Context, reqInfo *gomapb.RequesterInfo) context.Context {
-	logger := log.FromContext(ctx)
-	ctx, err := f.withRequestMetadata(ctx, reqInfo)
-	if err != nil {
-		logger.Errorf("request metadata(%v)=_, %v", reqInfo, err)
-		return ctx
-	}
-	if reqInfo != nil {
-		reqInfo = proto.Clone(reqInfo).(*gomapb.RequesterInfo)
-	} else {
-		reqInfo = &gomapb.RequesterInfo{}
-	}
-	reqInfo.Addr = proto.String(server.HostName(ctx))
-	return context.WithValue(ctx, requesterInfoKey, reqInfo)
-}
-
-func requesterInfo(ctx context.Context) *gomapb.RequesterInfo {
-	r, _ := ctx.Value(requesterInfoKey).(*gomapb.RequesterInfo)
-	return r
-}
-
-func (f *Adapter) client(ctx context.Context) Client {
-	// grpc rejects call on insecure connection if credential is set.
-	if f.InsecureClient {
-		return f.Client
-	}
-	user, _ := enduser.FromContext(ctx)
-	client := f.Client
-	token := user.Token()
-	if token.AccessToken == "" {
-		return client
-	}
-	client.CallOptions = append(client.CallOptions,
-		grpc.PerRPCCredentials(oauth.NewOauthAccess(token)))
-
-	maxBytes := int64(cas.DefaultBatchByteLimit)
-	if s := f.capabilities.GetCacheCapabilities().GetMaxBatchTotalSizeBytes(); s > maxBytes {
-		maxBytes = s
-	}
-	// TODO: set MaxCallRecvMsgSize if it uses BatchReadBlobs
-	client.CallOptions = append(client.CallOptions, grpc.MaxCallSendMsgSize(int(maxBytes)))
-	return client
-}
-
-func (f *Adapter) Instance() string {
-	name := f.InstanceBaseName
-	if name == "" {
-		name = "default_instance"
-	}
-	return path.Join(f.InstancePrefix, name)
-}
-
-func (f *Adapter) ensureCapabilities(ctx context.Context) {
-	f.capMu.Lock()
-	defer f.capMu.Unlock()
-
-	if f.capabilities != nil {
-		return
-	}
-	logger := log.FromContext(ctx)
-	var err error
-	f.capabilities, err = f.client(ctx).GetCapabilities(ctx, &rpb.GetCapabilitiesRequest{
-		InstanceName: f.Instance(),
-	})
-	if err != nil {
-		logger.Errorf("GetCapabilities: %v", err)
-		return
-	}
-	logger.Infof("serverCapabilities: %v", f.capabilities)
-}
-
-func (f *Adapter) newRequest(ctx context.Context, gomaReq *gomapb.ExecReq) *request {
-	logger := log.FromContext(ctx)
-	userGroup := "unknown-group"
-	endUser, ok := enduser.FromContext(ctx)
-	if ok {
-		userGroup = endUser.Group
-	}
-	gs := digest.NewStore()
-	timeout := f.ExecTimeout
-	if timeout == 0 {
-		timeout = 600 * time.Second
-	}
-	client := f.client(ctx)
-	r := &request{
-		f:         f,
-		userGroup: userGroup,
-		client:    client,
-		cas: &cas.CAS{
-			Client:            client,
-			Store:             gs,
-			CacheCapabilities: f.capabilities.GetCacheCapabilities(),
-		},
-		gomaReq: gomaReq,
-		gomaResp: &gomapb.ExecResp{
-			Result: &gomapb.ExecResult{
-				ExitStatus: proto.Int32(-1),
-			},
-		},
-		digestStore: gs,
-		input: &gomaInput{
-			gomaFile:    f.GomaFile,
-			sema:        f.FileLookupSema,
-			digestCache: f.DigestCache,
-		},
-		action: &rpb.Action{
-			Timeout:    durationpb.New(timeout),
-			DoNotCache: doNotCache(gomaReq),
-		},
-	}
-	logger.Infof("%s: new request group:%q", r.ID(), userGroup)
-	return r
-}
-
-// Use this to collect all timestamps and then print on one line,
-// regardless of where this function returns.
-type execSpan struct {
-	t0         time.Time
-	req        *request
-	timestamps []string
-}
-
-var spanMeasures = map[string]*stats.Float64Measure{
-	"inventory":     execInventoryTime,
-	"input tree":    execInputTreeTime,
-	"setup":         execSetupTime,
-	"check cache":   execCheckCacheTime,
-	"check missing": execCheckMissingTime,
-	"upload blobs":  execUploadBlobsTime,
-	"execute":       execExecuteTime,
-	"response":      execResponseTime,
-}
-
-func (s *execSpan) Close(ctx context.Context) {
-	s.timestamps = append(s.timestamps, fmt.Sprintf("total: %s", time.Since(s.t0).Truncate(time.Millisecond)))
-	logger := log.FromContext(ctx)
-	logger.Infof("%s", strings.Join(s.timestamps, ", "))
-}
-
-func (s *execSpan) Do(ctx context.Context, desc string, d time.Duration, f func(ctx context.Context)) time.Duration {
-	t := time.Now()
-	if d != 0 {
-		var cancel context.CancelFunc
-		ctx, cancel = context.WithTimeout(ctx, d)
-		defer cancel()
-	}
-	f(ctx)
-	duration := time.Since(t)
-	if ctx.Err() != nil {
-		logger := log.FromContext(ctx)
-		logger.Errorf("exec %s %s: timed-out %s: %s: %v", s.req.ID(), desc, d, duration, ctx.Err())
-	}
-	s.timestamps = append(s.timestamps, fmt.Sprintf("%s: %s", desc, duration.Truncate(time.Millisecond)))
-
-	if m, ok := spanMeasures[desc]; ok {
-		stats.Record(ctx, m.M(float64(duration.Nanoseconds())/1e6))
-	}
-
-	return duration
-}
-
-// Exec handles goma Exec requests with remoteexec backend.
-//
-//  1. compute input tree and Action.
-//     1.1. construct input tree from req.
-//     1.2. construct Action message from req.
-//  2. checks the ActionCache using GetActionResult. if hit, go to 7.
-//  3. queries the ContentAddressableStorage using FindMissingBlobs
-//  4. uploads any missing blobs to the ContentAddressableStorage
-//     using bytestream.Write and BatchUpdateBlobs.
-//  5. executes the action using Execute.
-//  6. awaits completion of the action using the longrunning.Operations.
-//  7. looks a the ActionResult
-//  8. If the action is successful, uses bytestream.Read to download any outputs
-//     it does not already have;
-//     embed it in response, or will serve it by LookupFile later
-//  9. job is complete
-//     9.1.  convert ExecResp from ExecuteResponse.
-//     for small outputs, embed in resp. otherwise use FILE_META.
-func (f *Adapter) Exec(ctx context.Context, req *gomapb.ExecReq) (resp *gomapb.ExecResp, err error) {
-	ctx, span := trace.StartSpan(ctx, "go.chromium.org/goma/server/remoteexec.Adapter.Exec")
-	defer span.End()
-
-	logger := log.FromContext(ctx)
-	defer func() {
-		if err != nil {
-			return
-		}
-		err := exec.RecordAPIError(ctx, resp)
-		if err != nil {
-			logger.Errorf("failed to record stats: %v", err)
-		}
-	}()
-	err = exec.RecordRequesterInfo(ctx, req.GetRequesterInfo())
-	if err != nil {
-		logger.Errorf("failed to record stats: %v", err)
-	}
-
-	// Use this to collect all timestamps and then print on one line,
-	// regardless of where this function returns.
-	espan := &execSpan{t0: time.Now()}
-	defer espan.Close(ctx)
-
-	adjustExecReq(req)
-	ctx = f.outgoingContext(ctx, req.GetRequesterInfo())
-	f.ensureCapabilities(ctx)
-
-	r := f.newRequest(ctx, req)
-	defer r.Close()
-	espan.req = r
-
-	dur := espan.Do(ctx, "inventory", f.SpanTimeout.Inventory, func(ctx context.Context) {
-		resp = r.getInventoryData(ctx)
-	})
-	if resp != nil {
-		logger.Infof("fail fast in inventory lookup: %s", dur)
-		return resp, nil
-	}
-
-	dur = espan.Do(ctx, "input tree", f.SpanTimeout.InputTree, func(ctx context.Context) {
-		resp = r.newInputTree(ctx)
-	})
-	if resp != nil {
-		logger.Infof("fail fast in input tree: %s", dur)
-		return resp, nil
-	}
-
-	espan.Do(ctx, "setup", f.SpanTimeout.Setup, func(ctx context.Context) {
-		r.setupNewAction(ctx)
-	})
-
-	eresp := &rpb.ExecuteResponse{}
-	var cached bool
-	espan.Do(ctx, "check cache", f.SpanTimeout.CheckCache, func(ctx context.Context) {
-		eresp.Result, cached = r.checkCache(ctx)
-	})
-	if !cached {
-		var blobs []*rpb.Digest
-		var err error
-		espan.Do(ctx, "check missing", f.SpanTimeout.CheckMissing, func(ctx context.Context) {
-			blobs, err = r.missingBlobs(ctx)
-		})
-		if err != nil {
-			logger.Errorf("exec call: error in check missing blobs: %v", err)
-			return nil, err
-		}
-
-		espan.Do(ctx, "upload blobs", f.SpanTimeout.UploadBlobs, func(ctx context.Context) {
-			resp, err = r.uploadBlobs(ctx, blobs)
-		})
-		if err != nil {
-			logger.Errorf("exec call: error in upload blobs: %v", err)
-			return nil, err
-		}
-		if resp != nil {
-			logger.Infof("fail fast for uploading missing blobs: %v", resp)
-			return resp, nil
-		}
-
-		espan.Do(ctx, "execute", f.SpanTimeout.Execute, func(ctx context.Context) {
-			eresp, err = r.executeAction(ctx)
-		})
-		if err != nil {
-			logger.Errorf("exec call: execute err=%v", err)
-			return nil, err
-		}
-	}
-	espan.Do(ctx, "response", f.SpanTimeout.Response, func(ctx context.Context) {
-		resp, err = r.newResp(ctx, eresp, cached)
-	})
-	if err != nil {
-		logger.Errorf("exec call: resp err=%v", err)
-	}
-	return resp, err
-}
diff --git a/remoteexec/adapter_test.go b/remoteexec/adapter_test.go
deleted file mode 100644
index 8f6a295..0000000
--- a/remoteexec/adapter_test.go
+++ /dev/null
@@ -1,1374 +0,0 @@
-// Copyright 2018 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.
-
-package remoteexec
-
-import (
-	"bytes"
-	"context"
-	"fmt"
-	"path/filepath"
-	"reflect"
-	"sort"
-	"testing"
-	"time"
-
-	rpb "github.com/bazelbuild/remote-apis/build/bazel/remote/execution/v2"
-	"github.com/google/go-cmp/cmp"
-	bpb "google.golang.org/genproto/googleapis/bytestream"
-	"google.golang.org/protobuf/proto"
-
-	gomapb "go.chromium.org/goma/server/proto/api"
-	cachepb "go.chromium.org/goma/server/proto/cache"
-	cmdpb "go.chromium.org/goma/server/proto/command"
-	fpb "go.chromium.org/goma/server/proto/file"
-	"go.chromium.org/goma/server/remoteexec/cas"
-	"go.chromium.org/goma/server/remoteexec/digest"
-)
-
-func TestAdapterHandleMissingCompiler(t *testing.T) {
-	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
-	defer cancel()
-
-	cluster := &fakeCluster{
-		rbe: newFakeRBE(),
-	}
-	err := cluster.setup(ctx, cluster.rbe.instancePrefix)
-	if err != nil {
-		t.Fatal(err)
-	}
-	defer cluster.teardown()
-
-	clangUnknown := newFakeClang(&cluster.cmdStorage, "1111", "x86-64-linux-gnu")
-	clang := newFakeClang(&cluster.cmdStorage, "1234", "x86-64-linux-gnu")
-
-	err = cluster.pushToolchains(ctx, clang)
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	var localFiles fakeLocalFiles
-	localFiles.Add("/b/c/w/src/hello.cc", randomSize())
-	localFiles.Add("/b/c/w/include/hello.h", randomSize())
-
-	req := &gomapb.ExecReq{
-		// client requests with unknown clang for goma.
-		CommandSpec: clangUnknown.CommandSpec("clang", "bin/clang"),
-		Arg: []string{
-			"bin/clang", "-I../../include",
-			"-c", "../../src/hello.cc",
-		},
-		Env: []string{},
-		Cwd: proto.String("/b/c/w/out/Release"),
-		Input: []*gomapb.ExecReq_Input{
-			localFiles.mustInput(ctx, t, nil, "/b/c/w/src/hello.cc", "../../src/hello.cc"),
-			localFiles.mustInput(ctx, t, nil, "/b/c/w/include/hello.h", "../../include/hello.h"),
-		},
-		Subprogram:    []*gomapb.SubprogramSpec{},
-		RequesterInfo: &gomapb.RequesterInfo{},
-		HermeticMode:  proto.Bool(true),
-	}
-
-	resp, err := cluster.adapter.Exec(ctx, req)
-	if err != nil {
-		t.Fatalf("Exec(ctx, req)=%v; %v; want nil error", resp, err)
-	}
-
-	if resp.GetError() != gomapb.ExecResp_BAD_REQUEST {
-		t.Errorf("Exec error=%v; want=%v", resp.GetError(), gomapb.ExecResp_BAD_REQUEST)
-	}
-	// client CompileTask::CheckNoMatchingCommandSpec
-	if resp.GetResult().CommandSpec == nil {
-		t.Errorf("Exec missing command_spec")
-	}
-	commandSpec := resp.GetResult().GetCommandSpec()
-	if commandSpec.BinaryHash != nil {
-		t.Errorf("Exec command_spec.binary_hash=%q; want not set",
-			string(commandSpec.BinaryHash))
-	}
-}
-
-func handleMissingInputs(ctx context.Context, t *testing.T, gomaFile fpb.FileServiceClient, localFiles fakeLocalFiles, req *gomapb.ExecReq, resp *gomapb.ExecResp) {
-	t.Logf("client uploads/embeds missing inputs")
-	if resp.GetError() != gomapb.ExecResp_OK {
-		t.Errorf("Exec error=%v; want=%v", resp.GetError(), gomapb.ExecResp_OK)
-	}
-Loop:
-	for i, input := range req.Input {
-		for _, fname := range resp.MissingInput {
-			if input.GetFilename() == fname {
-				fullname := filepath.Join(req.GetCwd(), fname)
-				t.Logf("upload/embed %s", fullname)
-				req.Input[i] = localFiles.mustInput(ctx, t, gomaFile, fullname, fname)
-				continue Loop
-			}
-		}
-	}
-}
-
-func TestAdapterHandleMissingInput(t *testing.T) {
-	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
-	defer cancel()
-
-	cluster := &fakeCluster{
-		rbe: newFakeRBE(),
-	}
-	err := cluster.setup(ctx, cluster.rbe.instancePrefix)
-	if err != nil {
-		t.Fatal(err)
-	}
-	defer cluster.teardown()
-
-	clang := newFakeClang(&cluster.cmdStorage, "1234", "x86-64-linux-gnu")
-
-	err = cluster.pushToolchains(ctx, clang)
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	var localFiles fakeLocalFiles
-	localFiles.Add("/b/c/w/src/hello.cc", randomSize())
-	localFiles.Add("/b/c/w/include/hello.h", randomSize())
-
-	req := &gomapb.ExecReq{
-		CommandSpec: clang.CommandSpec("clang", "bin/clang"),
-		Arg: []string{
-			"bin/clang", "-I../../include",
-			"-c", "../../src/hello.cc",
-		},
-		Env: []string{},
-		Cwd: proto.String("/b/c/w/out/Release"),
-		Input: []*gomapb.ExecReq_Input{
-			// client sends hash only (fc==nil).
-			localFiles.mustInput(ctx, t, nil, "/b/c/w/src/hello.cc", "../../src/hello.cc"),
-			localFiles.mustInput(ctx, t, nil, "/b/c/w/include/hello.h", "../../include/hello.h"),
-		},
-		Subprogram:    []*gomapb.SubprogramSpec{},
-		RequesterInfo: &gomapb.RequesterInfo{},
-		HermeticMode:  proto.Bool(true),
-	}
-
-	t.Logf("first call")
-	resp, err := cluster.adapter.Exec(ctx, req)
-	if err != nil {
-		t.Fatalf("Exec(ctx, req)=%v; %v; want nil error", resp, err)
-	}
-	if resp.GetError() != gomapb.ExecResp_OK {
-		t.Errorf("Exec error=%v; want=%v", resp.GetError(), gomapb.ExecResp_OK)
-	}
-
-	wantMissing := []string{"../../src/hello.cc", "../../include/hello.h"}
-
-	if !reflect.DeepEqual(resp.MissingInput, wantMissing) {
-		t.Fatalf("missing=%q; want=%q", resp.MissingInput, wantMissing)
-	}
-	if len(resp.MissingInput) != len(resp.MissingReason) {
-		t.Fatalf("missing: len(input)=%d != len(reason)=%d", len(resp.MissingInput), len(resp.MissingReason))
-	}
-
-	handleMissingInputs(ctx, t, cluster.adapter.GomaFile, localFiles, req, resp)
-
-	t.Logf("second call")
-	resp, err = cluster.adapter.Exec(ctx, req)
-	if err != nil {
-		t.Fatalf("Exec(ctx, req)=%v; %v; want nil error", resp, err)
-	}
-	if resp.GetError() != gomapb.ExecResp_OK {
-		t.Errorf("Exec error=%v; want=%v", resp.GetError(), gomapb.ExecResp_OK)
-	}
-	if len(resp.MissingInput) > 0 {
-		t.Fatalf("missing=%v; want no missing", resp.MissingInput)
-	}
-}
-
-func TestAdapterHandleMissingInputFilename(t *testing.T) {
-	// http://b/132391933 should not get filename from digest_cache.
-	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
-	defer cancel()
-
-	cluster := &fakeCluster{
-		rbe: newFakeRBE(),
-	}
-	err := cluster.setup(ctx, cluster.rbe.instancePrefix)
-	if err != nil {
-		t.Fatal(err)
-	}
-	defer cluster.teardown()
-
-	clang := newFakeClang(&cluster.cmdStorage, "1234", "x86-64-linux-gnu")
-
-	err = cluster.pushToolchains(ctx, clang)
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	var localFiles fakeLocalFiles
-	localFiles.Add("/b/c/w/src/hello.cc", randomSize())
-	localFiles.Add("/b/c/w/include/hello.h", randomSize())
-
-	req := &gomapb.ExecReq{
-		CommandSpec: clang.CommandSpec("clang", "bin/clang"),
-		Arg: []string{
-			"bin/clang", "-I../../include",
-			"-c", "../../src/hello.cc",
-		},
-		Env: []string{},
-		Cwd: proto.String("/b/c/w/out/Release"),
-		Input: []*gomapb.ExecReq_Input{
-			localFiles.mustInput(ctx, t, cluster.adapter.GomaFile, "/b/c/w/src/hello.cc", "../../src/hello.cc"),
-			localFiles.mustInput(ctx, t, cluster.adapter.GomaFile, "/b/c/w/include/hello.h", "../../include/hello.h"),
-		},
-		Subprogram:    []*gomapb.SubprogramSpec{},
-		RequesterInfo: &gomapb.RequesterInfo{},
-		HermeticMode:  proto.Bool(true),
-	}
-
-	t.Logf("first call")
-	resp, err := cluster.adapter.Exec(ctx, req)
-	if err != nil {
-		t.Fatalf("Exec(ctx, req)=%v; %v; want nil error", resp, err)
-	}
-	if resp.GetError() != gomapb.ExecResp_OK {
-		t.Errorf("Exec error=%v; want=%v", resp.GetError(), gomapb.ExecResp_OK)
-	}
-	if len(resp.MissingInput) > 0 {
-		t.Fatalf("missing=%v; want no missing", resp.MissingInput)
-	}
-
-	t.Logf("clear in-memory digest cache, but still in redis.")
-	cluster.adapter.DigestCache = digest.NewCache(&cluster.redis, 1e6)
-
-	req = &gomapb.ExecReq{
-		CommandSpec: clang.CommandSpec("clang", "bin/clang"),
-		Arg: []string{
-			"bin/clang", "-I../../include",
-			"-c", "../../src/hello.cc",
-		},
-		Env: []string{},
-		Cwd: proto.String("/b/c/w/out/Release"),
-		Input: []*gomapb.ExecReq_Input{
-			localFiles.mustInput(ctx, t, nil, "/b/c/w/src/hello.cc", "../../src/hello.cc"),
-			localFiles.mustInput(ctx, t, nil, "/b/c/w/include/hello.h", "../../include/hello.h"),
-		},
-		Subprogram:    []*gomapb.SubprogramSpec{},
-		RequesterInfo: &gomapb.RequesterInfo{},
-		HermeticMode:  proto.Bool(true),
-	}
-	t.Logf("second call, clear in-memory content in digest cache")
-	resp, err = cluster.adapter.Exec(ctx, req)
-	if err != nil {
-		t.Fatalf("Exec(ctx, req)=%v; %v; want nil error", resp, err)
-	}
-	if resp.GetError() != gomapb.ExecResp_OK {
-		t.Errorf("Exec error=%v; want=%v", resp.GetError(), gomapb.ExecResp_OK)
-	}
-	if len(resp.MissingInput) > 0 {
-		t.Fatalf("missing=%v; want no missing", resp.MissingInput)
-	}
-
-	localFiles.Dup("/b/c/w/src/hello.cc", "/b/c/w/src/hello2.cc")
-	localFiles.Dup("/b/c/w/include/hello.h", "/b/c/w/include/hello2.h")
-
-	req = &gomapb.ExecReq{
-		CommandSpec: clang.CommandSpec("clang", "bin/clang"),
-		Arg: []string{
-			"bin/clang", "-I../../include",
-			"-c", "../../src/hello2.cc",
-		},
-		Env: []string{},
-		Cwd: proto.String("/b/c/w/out/Release"),
-		Input: []*gomapb.ExecReq_Input{
-			// client sends hash only (fc==nil).
-			localFiles.mustInput(ctx, t, nil, "/b/c/w/src/hello2.cc", "../../src/hello2.cc"),
-			localFiles.mustInput(ctx, t, nil, "/b/c/w/include/hello2.h", "../../include/hello2.h"),
-		},
-		Subprogram:    []*gomapb.SubprogramSpec{},
-		RequesterInfo: &gomapb.RequesterInfo{},
-		HermeticMode:  proto.Bool(true),
-	}
-	t.Logf("reset cas and file-server cache")
-	cluster.rbe.cas = digest.NewStore()
-	for _, input := range req.Input {
-		_, err = cluster.cache.Put(ctx, &cachepb.PutReq{
-			Kv: &cachepb.KV{
-				Key: input.GetHashKey(),
-			},
-		})
-		if err != nil {
-			t.Fatalf("reset cache %s: %v", input, err)
-		}
-	}
-
-	t.Logf("third call, different filename")
-
-	resp, err = cluster.adapter.Exec(ctx, req)
-	if err != nil {
-		t.Fatalf("Exec(ctx, req)=%v; %v; want nil error", resp, err)
-	}
-	if resp.GetError() != gomapb.ExecResp_OK {
-		t.Errorf("Exec error=%v; want=%v", resp.GetError(), gomapb.ExecResp_OK)
-	}
-
-	wantMissing := []string{"../../src/hello2.cc", "../../include/hello2.h"}
-
-	if !cmp.Equal(resp.MissingInput, wantMissing) {
-		t.Fatalf("missing=%v; want=%v", resp.MissingInput, wantMissing)
-	}
-	if len(resp.MissingInput) != len(resp.MissingReason) {
-		t.Fatalf("missing: len(input)=%d != len(reason)=%d", len(resp.MissingInput), len(resp.MissingReason))
-	}
-}
-
-func TestAdapterHandleMissingInputContents(t *testing.T) {
-	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
-	defer cancel()
-
-	cluster := &fakeCluster{
-		rbe: newFakeRBE(),
-	}
-	err := cluster.setup(ctx, cluster.rbe.instancePrefix)
-	if err != nil {
-		t.Fatal(err)
-	}
-	defer cluster.teardown()
-
-	clang := newFakeClang(&cluster.cmdStorage, "1234", "x86-64-linux-gnu")
-
-	err = cluster.pushToolchains(ctx, clang)
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	var localFiles fakeLocalFiles
-	localFiles.Add("/b/c/w/src/hello.cc", randomSize())
-	localFiles.Add("/b/c/w/include/hello.h", randomSize())
-
-	// these files exists in digest cache, but not in CAS yet.
-	input := localFiles.mustInput(ctx, t, nil, "/b/c/w/src/hello.cc", "../../src/hello.cc")
-	cluster.redis.mustSet(ctx, t, input.GetHashKey(), localFiles.mustDigest(ctx, t, "/b/c/w/src/hello.cc"))
-	input = localFiles.mustInput(ctx, t, nil, "/b/c/w/include/hello.h", "../../include/hello.h")
-	cluster.redis.mustSet(ctx, t, input.GetHashKey(), localFiles.mustDigest(ctx, t, "/b/c/w/include/hello.h"))
-
-	req := &gomapb.ExecReq{
-		CommandSpec: clang.CommandSpec("clang", "bin/clang"),
-		Arg: []string{
-			"bin/clang", "-I../../include",
-			"-c", "../../src/hello.cc",
-		},
-		Env: []string{},
-		Cwd: proto.String("/b/c/w/out/Release"),
-		Input: []*gomapb.ExecReq_Input{
-			// client sends hash only (fc==nil).
-			localFiles.mustInput(ctx, t, nil, "/b/c/w/src/hello.cc", "../../src/hello.cc"),
-			localFiles.mustInput(ctx, t, nil, "/b/c/w/include/hello.h", "../../include/hello.h"),
-		},
-		Subprogram:    []*gomapb.SubprogramSpec{},
-		RequesterInfo: &gomapb.RequesterInfo{},
-		HermeticMode:  proto.Bool(true),
-	}
-
-	t.Logf("first call")
-	// found in digest in digest cache, but no content in CAS yet.
-	// return missing input instead of internal error. http://b/123546251
-	resp, err := cluster.adapter.Exec(ctx, req)
-	if err != nil {
-		t.Fatalf("Exec(ctx, req)=%v; %v; want nil error", resp, err)
-	}
-	if resp.GetError() != gomapb.ExecResp_OK {
-		t.Errorf("Exec error=%v; want=%v", resp.GetError(), gomapb.ExecResp_OK)
-	}
-
-	wantMissing := []string{"../../src/hello.cc", "../../include/hello.h"}
-
-	if !reflect.DeepEqual(resp.MissingInput, wantMissing) {
-		t.Fatalf("missing=%q; want=%q", resp.MissingInput, wantMissing)
-	}
-	if len(resp.MissingInput) != len(resp.MissingReason) {
-		t.Fatalf("missing: len(input)=%d != len(reason)=%d", len(resp.MissingInput), len(resp.MissingReason))
-	}
-
-	handleMissingInputs(ctx, t, cluster.adapter.GomaFile, localFiles, req, resp)
-
-	t.Logf("second call")
-	resp, err = cluster.adapter.Exec(ctx, req)
-	if err != nil {
-		t.Fatalf("Exec(ctx, req)=%v; %v; want nil error", resp, err)
-	}
-	if resp.GetError() != gomapb.ExecResp_OK {
-		t.Errorf("Exec error=%v; want=%v", resp.GetError(), gomapb.ExecResp_OK)
-	}
-	if len(resp.MissingInput) > 0 {
-		t.Fatalf("missing=%v; want no missing", resp.MissingInput)
-	}
-}
-
-func TestAdapterHandleMissingInputLimit(t *testing.T) {
-	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
-	defer cancel()
-
-	cluster := &fakeCluster{
-		rbe: newFakeRBE(),
-	}
-	err := cluster.setup(ctx, cluster.rbe.instancePrefix)
-	if err != nil {
-		t.Fatal(err)
-	}
-	defer cluster.teardown()
-
-	clang := newFakeClang(&cluster.cmdStorage, "1234", "x86-64-linux-gnu")
-
-	err = cluster.pushToolchains(ctx, clang)
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	var localFiles fakeLocalFiles
-	localFiles.Add("/b/c/w/src/a.cc", randomSize())
-	localFiles.Add("/b/c/w/include/b.h", randomSize())
-	localFiles.Add("/b/c/w/include/c.h", randomSize())
-
-	req := &gomapb.ExecReq{
-		CommandSpec: clang.CommandSpec("clang", "bin/clang"),
-		Arg: []string{
-			"bin/clang", "-I../../include",
-			"-c", "../../src/a.cc",
-		},
-		Env: []string{},
-		Cwd: proto.String("/b/c/w/out/Release"),
-		Input: []*gomapb.ExecReq_Input{
-			// client sends hash only (fc==nil).
-			localFiles.mustInput(ctx, t, nil, "/b/c/w/src/a.cc", "../../src/a.cc"),
-			localFiles.mustInput(ctx, t, nil, "/b/c/w/include/b.h", "../../include/b.h"),
-			localFiles.mustInput(ctx, t, nil, "/b/c/w/include/c.h", "../../include/c.h"),
-		},
-		Subprogram:    []*gomapb.SubprogramSpec{},
-		RequesterInfo: &gomapb.RequesterInfo{},
-		HermeticMode:  proto.Bool(true),
-	}
-
-	t.Logf("call without limit")
-	resp, err := cluster.adapter.Exec(ctx, req)
-	if err != nil {
-		t.Fatalf("Exec(ctx, req)=%v; %v; want nil error", resp, err)
-	}
-	if resp.GetError() != gomapb.ExecResp_OK {
-		t.Errorf("Exec error=%v; want=%v", resp.GetError(), gomapb.ExecResp_OK)
-	}
-	if len(resp.MissingInput) != len(req.Input) {
-		t.Errorf("missing=%d; want=%d", len(resp.MissingInput), len(req.Input))
-	}
-
-	t.Logf("call with limit")
-	cluster.adapter.MissingInputLimit = 2
-	resp, err = cluster.adapter.Exec(ctx, req)
-	if err != nil {
-		t.Fatalf("Exec(ctx, req)=%v; %v; want nil error", resp, err)
-	}
-	if resp.GetError() != gomapb.ExecResp_OK {
-		t.Errorf("Exec error=%v; want=%v", resp.GetError(), gomapb.ExecResp_OK)
-	}
-	if len(resp.MissingInput) != cluster.adapter.MissingInputLimit {
-		t.Errorf("missing=%d; want=%d", len(resp.MissingInput), cluster.adapter.MissingInputLimit)
-	}
-}
-
-func TestAdapterHandleSameCwdAndInputRoot(t *testing.T) {
-	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
-	defer cancel()
-	cluster := &fakeCluster{
-		rbe: newFakeRBE(),
-	}
-	err := cluster.setup(ctx, cluster.rbe.instancePrefix)
-	if err != nil {
-		t.Fatal(err)
-	}
-	defer cluster.teardown()
-
-	clang := newFakeClang(&cluster.cmdStorage, "1234", "x86-64-linux-gnu")
-
-	err = cluster.pushToolchains(ctx, clang)
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	var localFiles fakeLocalFiles
-	localFiles.Add("/cwd/hello.cc", randomSize())
-
-	cs := clang.CommandSpec("clang", "clang")
-
-	req := &gomapb.ExecReq{
-		CommandSpec: cs,
-		Arg: []string{
-			"./clang", "-c", "./hello.cc",
-		},
-		Env: []string{},
-		Cwd: proto.String("/cwd"),
-		Input: []*gomapb.ExecReq_Input{
-			localFiles.mustInput(ctx, t, cluster.adapter.GomaFile, "/cwd/hello.cc", "hello.cc"),
-		},
-		Subprogram:    []*gomapb.SubprogramSpec{},
-		RequesterInfo: &gomapb.RequesterInfo{},
-		HermeticMode:  proto.Bool(true),
-	}
-
-	resp, err := cluster.adapter.Exec(ctx, req)
-	if err != nil {
-		t.Fatalf("Exec(ctx, req)=%v; %v; want nil error", resp, err)
-	}
-	if resp.GetError() != gomapb.ExecResp_OK {
-		t.Errorf("Exec error=%v; want=%v", resp.GetError(), gomapb.ExecResp_OK)
-	}
-
-	command := cluster.rbe.gotCommand
-	if command == nil {
-		t.Fatalf("gotCommand is nil")
-	}
-	if len(command.Arguments) == 0 {
-		t.Errorf("arguments must not be empty")
-	}
-
-	firstArg := command.Arguments[0]
-	if firstArg != "./run.sh" {
-		t.Errorf(`command.Arguments[0]=%q; want="./run.sh"`, firstArg)
-	}
-
-	if command.WorkingDirectory != "" {
-		t.Errorf(`command.WorkingDirectory=%q; want=""`, command.WorkingDirectory)
-	}
-	workDirExists := false
-	for _, v := range command.EnvironmentVariables {
-		if v.Name == "WORK_DIR" {
-			workDirExists = true
-			if v.Value != "." {
-				t.Errorf(`WORK_DIR=%q; want="."`, v.Value)
-			}
-		}
-	}
-
-	if !workDirExists {
-		t.Errorf("WORK_DIR not found")
-	}
-	// TODO: add test case that Command.WorkingDirectory is set.
-}
-
-func TestAdapterHandleOutputs(t *testing.T) {
-	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
-	defer cancel()
-
-	cluster := &fakeCluster{
-		rbe: newFakeRBE(),
-	}
-	err := cluster.setup(ctx, cluster.rbe.instancePrefix)
-	if err != nil {
-		t.Fatal(err)
-	}
-	defer cluster.teardown()
-
-	clang := newFakeClang(&cluster.cmdStorage, "1234", "x86-64-linux-gnu")
-
-	err = cluster.pushToolchains(ctx, clang)
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	var localFiles fakeLocalFiles
-	localFiles.Add("/b/c/w/src/hello.cc", randomSize())
-	localFiles.Add("/b/c/w/include/hello.h", randomSize())
-
-	cs := clang.CommandSpec("clang", "bin/clang")
-
-	req := &gomapb.ExecReq{
-		CommandSpec: cs,
-		Arg: []string{
-			"bin/clang", "-I../../include",
-			"-c", "../../src/hello.cc",
-		},
-		Env: []string{},
-		Cwd: proto.String("/b/c/w/out/Release"),
-		Input: []*gomapb.ExecReq_Input{
-			localFiles.mustInput(ctx, t, cluster.adapter.GomaFile, "/b/c/w/src/hello.cc", "../../src/hello.cc"),
-			localFiles.mustInput(ctx, t, cluster.adapter.GomaFile, "/b/c/w/include/hello.h", "../../include/hello.h"),
-		},
-		Subprogram:          []*gomapb.SubprogramSpec{},
-		RequesterInfo:       &gomapb.RequesterInfo{},
-		HermeticMode:        proto.Bool(true),
-		ExpectedOutputFiles: []string{"hello.o"},
-		ExpectedOutputDirs:  []string{"fake-directory"},
-	}
-
-	resp, err := cluster.adapter.Exec(ctx, req)
-	if err != nil {
-		t.Fatalf("Exec(ctx, req)=%v; %v; want nil error", resp, err)
-	}
-	if resp.GetError() != gomapb.ExecResp_OK {
-		t.Errorf("Exec error=%v; want=%v", resp.GetError(), gomapb.ExecResp_OK)
-	}
-
-	command := cluster.rbe.gotCommand
-	if command == nil {
-		t.Fatalf("gotCommand is nil")
-	}
-
-	wantOutputFiles := []string{
-		"out/Release/hello.o",
-	}
-	wantOutputDirs := []string{
-		"out/Release/fake-directory",
-	}
-
-	if !reflect.DeepEqual(command.OutputFiles, wantOutputFiles) {
-		t.Errorf("output files: got=%v, want=%v", command.OutputFiles, wantOutputFiles)
-	}
-	if !reflect.DeepEqual(command.OutputDirectories, wantOutputDirs) {
-		t.Errorf("output dirs: got=%v, want=%v", command.OutputDirectories, wantOutputDirs)
-	}
-}
-
-func TestAdapterHandleOutputsWithoutExpectedOutputs(t *testing.T) {
-	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
-	defer cancel()
-
-	cluster := &fakeCluster{
-		rbe: newFakeRBE(),
-	}
-	err := cluster.setup(ctx, cluster.rbe.instancePrefix)
-	if err != nil {
-		t.Fatal(err)
-	}
-	defer cluster.teardown()
-
-	clang := newFakeClang(&cluster.cmdStorage, "1234", "x86-64-linux-gnu")
-
-	err = cluster.pushToolchains(ctx, clang)
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	var localFiles fakeLocalFiles
-	localFiles.Add("/b/c/w/src/hello.cc", randomSize())
-	localFiles.Add("/b/c/w/include/hello.h", randomSize())
-
-	cs := clang.CommandSpec("clang", "bin/clang")
-
-	req := &gomapb.ExecReq{
-		CommandSpec: cs,
-		Arg: []string{
-			"bin/clang", "-I../../include",
-			"-c", "../../src/hello.cc",
-			"-o", "hello.o",
-		},
-		Env: []string{},
-		Cwd: proto.String("/b/c/w/out/Release"),
-		Input: []*gomapb.ExecReq_Input{
-			localFiles.mustInput(ctx, t, cluster.adapter.GomaFile, "/b/c/w/src/hello.cc", "../../src/hello.cc"),
-			localFiles.mustInput(ctx, t, cluster.adapter.GomaFile, "/b/c/w/include/hello.h", "../../include/hello.h"),
-		},
-		Subprogram:    []*gomapb.SubprogramSpec{},
-		RequesterInfo: &gomapb.RequesterInfo{},
-		HermeticMode:  proto.Bool(true),
-	}
-
-	resp, err := cluster.adapter.Exec(ctx, req)
-	if err != nil {
-		t.Fatalf("Exec(ctx, req)=%v; %v; want nil error", resp, err)
-	}
-	if resp.GetError() != gomapb.ExecResp_OK {
-		t.Errorf("Exec error=%v; want=%v", resp.GetError(), gomapb.ExecResp_OK)
-	}
-
-	command := cluster.rbe.gotCommand
-	if command == nil {
-		t.Fatalf("gotCommand is nil")
-	}
-
-	wantOutputFiles := []string{
-		"out/Release/hello.o",
-	}
-	var wantOutputDirs []string
-
-	if !reflect.DeepEqual(command.OutputFiles, wantOutputFiles) {
-		t.Errorf("output files: got=%v, want=%v", command.OutputFiles, wantOutputFiles)
-	}
-	if !reflect.DeepEqual(command.OutputDirectories, wantOutputDirs) {
-		t.Errorf("output dirs: got=%v, want=%v", command.OutputDirectories, wantOutputDirs)
-	}
-}
-
-func TestAdapterHandleCrossCompile(t *testing.T) {
-	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
-	defer cancel()
-
-	cluster := &fakeCluster{
-		rbe: newFakeRBE(),
-	}
-	err := cluster.setup(ctx, cluster.rbe.instancePrefix)
-	if err != nil {
-		t.Fatal(err)
-	}
-	defer cluster.teardown()
-
-	clang := newFakeClang(&cluster.cmdStorage, "1234", "x86_64-darwin")
-	for _, desc := range clang.descs {
-		desc.Cross = &cmdpb.CmdDescriptor_Cross{
-			ClangNeedTarget: true,
-		}
-	}
-
-	err = cluster.pushToolchains(ctx, clang)
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	var localFiles fakeLocalFiles
-	localFiles.Add("/b/c/w/src/hello.cc", randomSize())
-	localFiles.Add("/b/c/w/include/hello.h", randomSize())
-
-	cs := clang.CommandSpec("clang", "bin/clang")
-	cs.Target = proto.String("x86_64-apple-darwin10.6.0")
-
-	req := &gomapb.ExecReq{
-		CommandSpec: cs,
-		Arg: []string{
-			"bin/clang", "-I../../include",
-			"-c", "../../src/hello.cc",
-			"-o", "hello.o",
-		},
-		Env: []string{},
-		Cwd: proto.String("/b/c/w/out/Release"),
-		Input: []*gomapb.ExecReq_Input{
-			localFiles.mustInput(ctx, t, cluster.adapter.GomaFile, "/b/c/w/src/hello.cc", "../../src/hello.cc"),
-			localFiles.mustInput(ctx, t, cluster.adapter.GomaFile, "/b/c/w/include/hello.h", "../../include/hello.h"),
-		},
-		Subprogram:    []*gomapb.SubprogramSpec{},
-		RequesterInfo: &gomapb.RequesterInfo{},
-		HermeticMode:  proto.Bool(true),
-	}
-
-	resp, err := cluster.adapter.Exec(ctx, req)
-	if err != nil {
-		t.Fatalf("Exec(ctx, req)=%v; %v; want nil error", resp, err)
-	}
-	if resp.GetError() != gomapb.ExecResp_OK {
-		t.Errorf("Exec error=%v; want=%v", resp.GetError(), gomapb.ExecResp_OK)
-	}
-
-	command := cluster.rbe.gotCommand
-	if command == nil {
-		t.Fatalf("gotCommand is nil")
-	}
-
-	wantArguments := []string{
-		"out/Release/run.sh", "bin/clang",
-		"--target=x86_64-apple-darwin10.6.0",
-		"-I../../include",
-		"-c", "../../src/hello.cc",
-		"-o", "hello.o",
-	}
-	if !reflect.DeepEqual(command.Arguments, wantArguments) {
-		t.Errorf("arguments: got=%q, want=%q", command.Arguments, wantArguments)
-	}
-}
-
-type fileState struct {
-	digest       *rpb.Digest
-	isFile       bool
-	isDir        bool
-	isExecutable bool
-}
-
-// TODO: implement this with GetTree?
-func dumpDirIter(ctx context.Context, t *testing.T, bs bpb.ByteStreamClient, instance, dir string, d *rpb.Digest, files map[string]fileState) error {
-	t.Logf("dir:%s %s", dir, d)
-
-	resname := cas.ResName(instance, d)
-	var buf bytes.Buffer
-	size, err := cas.Download(ctx, bs, &buf, resname)
-	if err != nil {
-		return fmt.Errorf("download dir:%s %s: %v", dir, d, err)
-	}
-	if size != d.SizeBytes {
-		return fmt.Errorf("incomplete fetch %v: size=%d", d, size)
-	}
-	curdir := &rpb.Directory{}
-	err = proto.Unmarshal(buf.Bytes(), curdir)
-	if err != nil {
-		return fmt.Errorf("unmarshal dir:%s %s: %v", dir, d, err)
-	}
-	for _, f := range curdir.Files {
-		fname := filepath.Join(dir, f.Name)
-		files[fname] = fileState{
-			digest:       f.Digest,
-			isFile:       true,
-			isExecutable: f.IsExecutable,
-		}
-		t.Logf("file:%s %s x:%t", fname, f.Digest, f.IsExecutable)
-	}
-	for _, subdir := range curdir.Directories {
-		dname := filepath.Join(dir, subdir.Name)
-		files[dname] = fileState{
-			isDir: true,
-		}
-		err := dumpDirIter(ctx, t, bs, instance, dname, subdir.Digest, files)
-		if err != nil {
-			return err
-		}
-	}
-	return nil
-}
-
-// dumpDirs dumps file list and directory list.
-// The value of `files` means a file is executable or not.
-func dumpDir(ctx context.Context, t *testing.T, bs bpb.ByteStreamClient, instance, dir string, d *rpb.Digest) (map[string]fileState, error) {
-	files := make(map[string]fileState)
-	err := dumpDirIter(ctx, t, bs, instance, dir, d, files)
-	if err != nil {
-		return nil, err
-	}
-	return files, nil
-}
-
-func TestAdapterHandleOutputsWithSystemIncludePaths(t *testing.T) {
-	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
-	defer cancel()
-
-	cluster := &fakeCluster{
-		rbe: newFakeRBE(),
-	}
-	err := cluster.setup(ctx, cluster.rbe.instancePrefix)
-	if err != nil {
-		t.Fatal(err)
-	}
-	defer cluster.teardown()
-
-	clang := newFakeClang(&cluster.cmdStorage, "1234", "x86-64-linux-gnu")
-
-	err = cluster.pushToolchains(ctx, clang)
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	var localFiles fakeLocalFiles
-	localFiles.Add("/b/c/w/src/hello.cc", randomSize())
-	localFiles.Add("/b/c/w/include/hello.h", randomSize())
-
-	cs := clang.CommandSpec("clang", "bin/clang")
-
-	req := &gomapb.ExecReq{
-		CommandSpec: cs,
-		Arg: []string{
-			"bin/clang", "-I../../include",
-			"--sysroot=../../build/linux/debian_sid_amd64-sysroot",
-			"-c", "../../src/hello.cc",
-			"-o", "hello.o",
-		},
-		Env: []string{},
-		Cwd: proto.String("/b/c/w/out/Release"),
-		Input: []*gomapb.ExecReq_Input{
-			localFiles.mustInput(ctx, t, cluster.adapter.GomaFile, "/b/c/w/src/hello.cc", "../../src/hello.cc"),
-			localFiles.mustInput(ctx, t, cluster.adapter.GomaFile, "/b/c/w/include/hello.h", "../../include/hello.h"),
-		},
-		Subprogram:    []*gomapb.SubprogramSpec{},
-		RequesterInfo: &gomapb.RequesterInfo{},
-		HermeticMode:  proto.Bool(true),
-	}
-	req.CommandSpec.SystemIncludePath = []string{
-		"../../build/linux/debian_sid_amd64-sysroot/usr/include/x86_64-linux-gnu",
-	}
-
-	resp, err := cluster.adapter.Exec(ctx, req)
-	if err != nil {
-		t.Fatalf("Exec(ctx, req)=%v; %v; want nil error", resp, err)
-	}
-	if resp.GetError() != gomapb.ExecResp_OK {
-		t.Errorf("Exec error=%v; want=%v", resp.GetError(), gomapb.ExecResp_OK)
-	}
-
-	action := cluster.rbe.gotAction
-	if action == nil {
-		t.Fatalf("gotAction is nil")
-	}
-	files, err := dumpDir(ctx, t, cluster.adapter.Client, cluster.adapter.Instance(), ".", action.InputRootDigest)
-	if err != nil {
-		t.Fatalf("err %v", err)
-	}
-	if !files["build/linux/debian_sid_amd64-sysroot/usr/include/x86_64-linux-gnu"].isDir {
-		t.Errorf("want CAS has build/linux/debian_sid_amd64-sysroot/usr/include/x86_64-linux-gnu; files=%v", files)
-	}
-}
-
-func TestAdaptorHandleArbitraryToolchainSupport(t *testing.T) {
-	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
-	defer cancel()
-
-	cluster := &fakeCluster{
-		rbe: newFakeRBE(),
-	}
-	err := cluster.setup(ctx, cluster.rbe.instancePrefix)
-	if err != nil {
-		t.Fatal(err)
-	}
-	defer cluster.teardown()
-
-	// Instead of adding a new compiler, register toolchain platform.
-	err = cluster.pushPlatform(ctx, "docker://grpc.io/goma-dev/container-image@sha256:yyyy", []string{"os:linux"})
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	var localFiles fakeLocalFiles
-	localFiles.Add("/b/c/w/bin/clang", randomBigSize())
-	localFiles.Add("/b/c/w/include/hello.h", randomSize())
-	localFiles.Add("/b/c/w/src/hello.c", randomSize())
-
-	clangToolchainInput := localFiles.mustInput(ctx, t, cluster.adapter.GomaFile, "/b/c/w/bin/clang", "../../bin/clang")
-	clangHashKey := localFiles.mustFileHash(ctx, t, "/b/c/w/bin/clang")
-
-	req := &gomapb.ExecReq{
-		CommandSpec: &gomapb.CommandSpec{
-			Name:              proto.String("clang"),
-			Version:           proto.String("1234"),
-			Target:            proto.String("x86-64-linux-gnu"),
-			BinaryHash:        []byte(clangHashKey),
-			LocalCompilerPath: proto.String("../../bin/clang"),
-		},
-		Arg: []string{
-			"../../bin/clang", "-Iinclude",
-			"-c", "../../src/hello.c",
-			"-o", "hello.o",
-		},
-		Env: []string{"PWD=/b/c/w/out/Release"},
-		Cwd: proto.String("/b/c/w/out/Release"),
-		Input: []*gomapb.ExecReq_Input{
-			clangToolchainInput,
-			localFiles.mustInput(ctx, t, cluster.adapter.GomaFile, "/b/c/w/include/hello.h", "../../include/hello.h"),
-			localFiles.mustInput(ctx, t, cluster.adapter.GomaFile, "/b/c/w/src/hello.c", "../../src/hello.c"),
-		},
-		Subprogram:        []*gomapb.SubprogramSpec{},
-		ToolchainIncluded: proto.Bool(true),
-		ToolchainSpecs: []*gomapb.ToolchainSpec{
-			{
-				Path:         proto.String("../../bin/clang"),
-				Hash:         proto.String(clangHashKey),
-				Size:         clangToolchainInput.Content.FileSize,
-				IsExecutable: proto.Bool(true),
-			},
-		},
-		RequesterInfo: &gomapb.RequesterInfo{
-			Dimensions: []string{
-				"os:linux",
-			},
-			PathStyle: gomapb.RequesterInfo_POSIX_STYLE.Enum(),
-		},
-		ExpectedOutputFiles: []string{
-			"hello.o",
-		},
-	}
-
-	resp, err := cluster.adapter.Exec(ctx, req)
-	if err != nil {
-		t.Fatalf("Exec(ctx, req)=%v; %v; want nil error", resp, err)
-	}
-	if resp.GetError() != gomapb.ExecResp_OK {
-		t.Errorf("Exec error=%v; want=%v", resp.GetError(), gomapb.ExecResp_OK)
-	}
-
-	command := cluster.rbe.gotCommand
-	if command == nil {
-		t.Fatal("gotCommand is nil")
-	}
-	wantArgs := []string{
-		"out/Release/run.sh",
-		"../../bin/clang", "-Iinclude",
-		"-c", "../../src/hello.c",
-		"-o", "hello.o",
-	}
-	if !cmp.Equal(command.Arguments, wantArgs) {
-		t.Errorf("arguments=%q; want=%q", command.Arguments, wantArgs)
-	}
-	wantEnvs := []*rpb.Command_EnvironmentVariable{
-		{
-			Name:  "WORK_DIR",
-			Value: "out/Release",
-		},
-	}
-	if !cmp.Equal(command.EnvironmentVariables, wantEnvs, cmp.Comparer(proto.Equal)) {
-		t.Errorf("environment_variables=%s; want=%s", command.EnvironmentVariables, wantEnvs)
-	}
-
-	action := cluster.rbe.gotAction
-	if action == nil {
-		t.Fatalf("gotAction is nil")
-	}
-	files, err := dumpDir(ctx, t, cluster.adapter.Client, cluster.adapter.Instance(), ".", action.InputRootDigest)
-	if err != nil {
-		t.Fatalf("err %v", err)
-	}
-
-	// files and executables might contain extra "out/Release/run.sh".
-	wantFiles := []string{"out/Release/run.sh", "bin/clang", "include/hello.h", "src/hello.c"}
-	wantExecutables := []string{"bin/clang", "out/Release/run.sh"}
-
-	for _, f := range wantFiles {
-		if !files[f].isFile {
-			t.Errorf("%q was not found in files, but should: files=%v", f, files)
-		}
-	}
-	for _, e := range wantExecutables {
-		if !files[e].isExecutable {
-			t.Errorf("%q was not an executable file, but should: files=%v", e, files)
-		}
-	}
-
-	if got, want := files["out/Release/run.sh"].digest, digest.Bytes("wrapper-script", []byte(wrapperScript)).Digest(); !proto.Equal(got, want) {
-		t.Errorf("digest of out/Release/run.sh: %s != %s", got, want)
-	}
-}
-
-func TestAdaptorHandleArbitraryToolchainSupportNonRelocatable(t *testing.T) {
-	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
-	defer cancel()
-
-	cluster := &fakeCluster{
-		rbe: newFakeRBE(),
-	}
-	err := cluster.setup(ctx, cluster.rbe.instancePrefix)
-	if err != nil {
-		t.Fatal(err)
-	}
-	defer cluster.teardown()
-
-	// Instead of adding a new compiler, register toolchain platform.
-	err = cluster.pushPlatform(ctx, "docker://grpc.io/goma-dev/container-image@sha256:yyyy", []string{"os:linux"})
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	var localFiles fakeLocalFiles
-	localFiles.Add("/b/c/w/bin/clang", randomBigSize())
-	localFiles.Add("/b/c/w/include/hello.h", randomSize())
-	localFiles.Add("/b/c/w/src/hello.c", randomSize())
-
-	clangToolchainInput := localFiles.mustInput(ctx, t, cluster.adapter.GomaFile, "/b/c/w/bin/clang", "../../bin/clang")
-	clangHashKey := localFiles.mustFileHash(ctx, t, "/b/c/w/bin/clang")
-
-	req := &gomapb.ExecReq{
-		CommandSpec: &gomapb.CommandSpec{
-			Name:              proto.String("clang"),
-			Version:           proto.String("1234"),
-			Target:            proto.String("x86-64-linux-gnu"),
-			BinaryHash:        []byte(clangHashKey),
-			LocalCompilerPath: proto.String("../../bin/clang"),
-		},
-		Arg: []string{
-			"../../bin/clang", "-Iinclude",
-			"-g", "-c", "../../src/hello.c",
-			"-o", "hello.o",
-		},
-		Env: []string{"PWD=/b/c/w/out/Debug"},
-		Cwd: proto.String("/b/c/w/out/Debug"),
-		Input: []*gomapb.ExecReq_Input{
-			clangToolchainInput,
-			localFiles.mustInput(ctx, t, cluster.adapter.GomaFile, "/b/c/w/include/hello.h", "../../include/hello.h"),
-			localFiles.mustInput(ctx, t, cluster.adapter.GomaFile, "/b/c/w/src/hello.c", "../../src/hello.c"),
-		},
-		Subprogram:        []*gomapb.SubprogramSpec{},
-		ToolchainIncluded: proto.Bool(true),
-		ToolchainSpecs: []*gomapb.ToolchainSpec{
-			{
-				Path:         proto.String("../../bin/clang"),
-				Hash:         proto.String(clangHashKey),
-				Size:         clangToolchainInput.Content.FileSize,
-				IsExecutable: proto.Bool(true),
-			},
-		},
-		RequesterInfo: &gomapb.RequesterInfo{
-			Dimensions: []string{
-				"os:linux",
-			},
-			PathStyle: gomapb.RequesterInfo_POSIX_STYLE.Enum(),
-		},
-		ExpectedOutputFiles: []string{
-			"hello.o",
-		},
-	}
-
-	resp, err := cluster.adapter.Exec(ctx, req)
-	if err != nil {
-		t.Fatalf("Exec(ctx, req)=%v; %v; want nil error", resp, err)
-	}
-	if resp.GetError() != gomapb.ExecResp_OK {
-		t.Errorf("Exec error=%v; want=%v", resp.GetError(), gomapb.ExecResp_OK)
-	}
-
-	command := cluster.rbe.gotCommand
-	if command == nil {
-		t.Fatal("gotCommand is nil")
-	}
-	wantArgs := []string{
-		"out/Debug/run.sh",
-		"../../bin/clang", "-Iinclude",
-		"-g", "-c", "../../src/hello.c",
-		"-o", "hello.o",
-	}
-	if !cmp.Equal(command.Arguments, wantArgs) {
-		t.Errorf("arguments=%q; want=%q", command.Arguments, wantArgs)
-	}
-	wantEnvs := []*rpb.Command_EnvironmentVariable{
-		{
-			Name:  "PWD",
-			Value: "/b/c/w/out/Debug",
-		},
-		{
-			Name:  "WORK_DIR",
-			Value: "out/Debug",
-		},
-	}
-	if !cmp.Equal(command.EnvironmentVariables, wantEnvs, cmp.Comparer(proto.Equal)) {
-		t.Errorf("environment_variables=%s; want=%s", command.EnvironmentVariables, wantEnvs)
-	}
-	wantProperties := []*rpb.Platform_Property{
-		{
-			Name:  "InputRootAbsolutePath",
-			Value: "/b/c/w",
-		},
-		{
-			Name:  "container-image",
-			Value: "docker://grpc.io/goma-dev/container-image@sha256:yyyy",
-		},
-	}
-	if command.Platform == nil {
-		t.Errorf("platform is nil")
-	} else if !cmp.Equal(command.Platform.Properties, wantProperties, cmp.Comparer(proto.Equal)) {
-		t.Errorf("platform.properties=%s; want=%s", command.Platform.Properties, wantProperties)
-	}
-
-	action := cluster.rbe.gotAction
-	if action == nil {
-		t.Fatalf("gotAction is nil")
-	}
-	files, err := dumpDir(ctx, t, cluster.adapter.Client, cluster.adapter.Instance(), ".", action.InputRootDigest)
-	if err != nil {
-		t.Fatalf("err %v", err)
-	}
-
-	// files and executables might contain extra "out/Debug/run.sh".
-	wantFiles := []string{"out/Debug/run.sh", "bin/clang", "include/hello.h", "src/hello.c"}
-	wantExecutables := []string{"bin/clang", "out/Debug/run.sh"}
-
-	for _, f := range wantFiles {
-		if !files[f].isFile {
-			t.Errorf("%q was not found in files, but should: files=%v", f, files)
-		}
-	}
-	for _, e := range wantExecutables {
-		if !files[e].isExecutable {
-			t.Errorf("%q was not an executable file, but should: files=%v", e, files)
-		}
-	}
-
-	if got, want := files["out/Debug/run.sh"].digest, digest.Bytes("wrapper-script", []byte(wrapperScript)).Digest(); !proto.Equal(got, want) {
-		t.Errorf("digest of out/Debug/run.sh: %s != %s", got, want)
-	}
-}
-
-// TODO: add test for ATS+chroot case using symlinks.
-
-func TestAdapterDockerProperties(t *testing.T) {
-	for _, tc := range []struct {
-		desc string
-		args []string
-		want []*rpb.Platform_Property
-	}{
-		{
-			desc: "relocatable",
-			args: nil,
-			want: nil,
-		},
-		{
-			desc: "non relocatable",
-			args: []string{"-g"},
-			want: []*rpb.Platform_Property{
-				{
-					Name:  "InputRootAbsolutePath",
-					Value: "/b/c/w",
-				},
-			},
-		},
-	} {
-		t.Run(tc.desc, func(t *testing.T) {
-			ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
-			defer cancel()
-
-			cluster := &fakeCluster{
-				rbe: newFakeRBE(),
-			}
-			err := cluster.setup(ctx, cluster.rbe.instancePrefix)
-			if err != nil {
-				t.Fatal(err)
-			}
-			defer cluster.teardown()
-			clang := newFakeClang(&cluster.cmdStorage, "1234", "x86-64-linux-gnu")
-			err = cluster.pushToolchains(ctx, clang)
-			if err != nil {
-				t.Fatal(err)
-			}
-			var localFiles fakeLocalFiles
-			localFiles.Add("/b/c/w/src/hello.cc", randomSize())
-
-			req := &gomapb.ExecReq{
-				CommandSpec: clang.CommandSpec("clang", "bin/clang"),
-				Arg:         append([]string{"bin/clang", "-c", "../../src/hello.cc"}, tc.args...),
-				Env:         []string{},
-				Cwd:         proto.String("/b/c/w/out/Release"),
-				Input: []*gomapb.ExecReq_Input{
-					localFiles.mustInput(ctx, t, cluster.adapter.GomaFile, "/b/c/w/src/hello.cc", "../../src/hello.cc"),
-				},
-				Subprogram:    []*gomapb.SubprogramSpec{},
-				RequesterInfo: &gomapb.RequesterInfo{},
-				HermeticMode:  proto.Bool(true),
-			}
-			resp, err := cluster.adapter.Exec(ctx, req)
-			if err != nil {
-				t.Fatalf("Exec(ctx, req)=%v; %v; want nil error", resp, err)
-			}
-			if resp.GetError() != gomapb.ExecResp_OK {
-				t.Errorf("Exec error=%v; want=%v", resp.GetError(), gomapb.ExecResp_OK)
-			}
-
-			command := cluster.rbe.gotCommand
-			if command == nil {
-				t.Fatalf("gotCommand is nil")
-			}
-			want := []*rpb.Platform_Property{}
-			for _, p := range clang.RemoteexecPlatform.Properties {
-				want = append(want, &rpb.Platform_Property{
-					Name:  p.Name,
-					Value: p.Value,
-				})
-			}
-			want = append(want, tc.want...)
-			sort.Slice(want, func(i, j int) bool {
-				return want[i].Name < want[j].Name
-			})
-			if diff := cmp.Diff(want, command.Platform.GetProperties(), cmp.Comparer(proto.Equal)); diff != "" {
-				t.Errorf("platform.Properties diff want->got\n%s", diff)
-			}
-		})
-	}
-}
-
-func TestAdapterNsjailHardening(t *testing.T) {
-	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
-	defer cancel()
-
-	cluster := &fakeCluster{
-		rbe: newFakeRBE(),
-	}
-	err := cluster.setup(ctx, cluster.rbe.instancePrefix)
-	if err != nil {
-		t.Fatal(err)
-	}
-	defer cluster.teardown()
-	clang := newFakeClang(&cluster.cmdStorage, "1234", "x86-64-linux-gnu")
-	err = cluster.pushToolchains(ctx, clang)
-	if err != nil {
-		t.Fatal(err)
-	}
-	var localFiles fakeLocalFiles
-	localFiles.Add("/b/c/w/src/hello.cc", randomSize())
-
-	req := &gomapb.ExecReq{
-		CommandSpec: clang.CommandSpec("clang", "bin/clang"),
-		Arg:         []string{"bin/clang", "-c", "../../src/hello.cc"},
-		Env:         []string{},
-		Cwd:         proto.String("/b/c/w/out/Release"),
-		Input: []*gomapb.ExecReq_Input{
-			localFiles.mustInput(ctx, t, cluster.adapter.GomaFile, "/b/c/w/src/hello.cc", "../../src/hello.cc"),
-		},
-		Subprogram:    []*gomapb.SubprogramSpec{},
-		RequesterInfo: &gomapb.RequesterInfo{},
-		HermeticMode:  proto.Bool(true),
-	}
-
-	cluster.adapter.HardeningRatio = 1.0
-	cluster.adapter.NsjailRatio = 1.0
-
-	resp, err := cluster.adapter.Exec(ctx, req)
-	if err != nil {
-		t.Fatalf("Exec(ctx, req)=%v; %v; want nil error", resp, err)
-	}
-	if resp.GetError() != gomapb.ExecResp_OK {
-		t.Errorf("Exec error=%v; want=%v", resp.GetError(), gomapb.ExecResp_OK)
-	}
-
-	command := cluster.rbe.gotCommand
-	if command == nil {
-		t.Fatalf("gotCommand is nil")
-	}
-	wantArgs := []string{
-		"out/Release/run.sh",
-		"bin/clang", "-c", "../../src/hello.cc",
-	}
-	if diff := cmp.Diff(wantArgs, command.Arguments); diff != "" {
-		t.Errorf("arguments diff want->got\n%s", diff)
-	}
-	if command.WorkingDirectory != "" {
-		t.Errorf(`command.WorkingDirectory=%q; want=""`, command.WorkingDirectory)
-	}
-	workDirExists := false
-	wantWorkDir := "out/Release"
-	for _, v := range command.EnvironmentVariables {
-		if v.Name == "WORK_DIR" {
-			workDirExists = true
-			if v.Value != wantWorkDir {
-				t.Errorf("WORK_DIR=%q; want=%q", v.Value, wantWorkDir)
-			}
-		}
-	}
-
-	if !workDirExists {
-		t.Errorf("WORK_DIR not found")
-	}
-
-	action := cluster.rbe.gotAction
-	if action == nil {
-		t.Fatal("gotAction is nil")
-	}
-	files, err := dumpDir(ctx, t, cluster.adapter.Client, cluster.adapter.Instance(), ".", action.InputRootDigest)
-	if err != nil {
-		t.Fatalf("dumpDir err:%v", err)
-	}
-	runsh, exists := files["out/Release/run.sh"]
-	if !exists {
-		t.Errorf("out/Release/run.sh doesn't exist")
-	} else if !runsh.isExecutable {
-		t.Errorf("out/Release/run.sh is not executable")
-	} else if want := digest.Bytes("run.sh", nsjailHardeningWrapperScript).Digest(); !proto.Equal(runsh.digest, want) {
-		t.Errorf("out/Release/run.sh digest=%s; want=%s", runsh.digest, want)
-	}
-	nsjailCfg, exists := files["out/Release/nsjail.cfg"]
-	if !exists {
-		t.Errorf("out/Release/nsjail.cfg doesn't exist")
-	} else if want := digest.Bytes("nsjail.cfg", nsjailHardeningConfig).Digest(); !proto.Equal(nsjailCfg.digest, want) {
-		t.Errorf("out/Release/nsjail.cfg digest=%s; want=%s", nsjailCfg.digest, want)
-	}
-
-	var wantProps []*rpb.Platform_Property
-	for _, p := range clang.RemoteexecPlatform.Properties {
-		wantProps = append(wantProps, &rpb.Platform_Property{
-			Name:  p.Name,
-			Value: p.Value,
-		})
-	}
-	wantProps = append(wantProps, &rpb.Platform_Property{
-		Name:  "dockerPrivileged",
-		Value: "true",
-	})
-	sort.Slice(wantProps, func(i, j int) bool {
-		return wantProps[i].Name < wantProps[j].Name
-	})
-	if diff := cmp.Diff(wantProps, command.Platform.GetProperties(), cmp.Comparer(proto.Equal)); diff != "" {
-		t.Errorf("platform.Properties diff want->got\n%s", diff)
-	}
-}
diff --git a/remoteexec/bytestream.go b/remoteexec/bytestream.go
deleted file mode 100644
index a502f32..0000000
--- a/remoteexec/bytestream.go
+++ /dev/null
@@ -1,109 +0,0 @@
-// Copyright 2018 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.
-
-package remoteexec
-
-import (
-	"context"
-	"io"
-	"path"
-
-	"github.com/google/uuid"
-	"go.opencensus.io/trace"
-	pb "google.golang.org/genproto/googleapis/bytestream"
-)
-
-// ByteStream is a proxy that reads/writes data to/from a server, as defined in
-// googleapis/bytestream. In the context of the exec server, it accesses a resource on the
-// RBE server, specifically a resource under an RBE instance.
-type ByteStream struct {
-	// Adapter provides an interface to the RBE API.
-	Adapter *Adapter
-
-	// The name of the RBE instance, e.g. "projects/$PROJECT/instances/default_instance"
-	InstanceName string
-}
-
-// Read proxies bytestream Read stream.
-func (bs *ByteStream) Read(req *pb.ReadRequest, s pb.ByteStream_ReadServer) error {
-	ctx := s.Context()
-	ctx, span := trace.StartSpan(ctx, "go.chromium.org/goma/server/remoteexec.Adapter.Read")
-	defer span.End()
-
-	ctx = bs.Adapter.outgoingContext(ctx, nil)
-
-	rd, err := bs.Adapter.client(ctx).ByteStream().Read(ctx, &pb.ReadRequest{
-		ResourceName: path.Join(bs.InstanceName, req.ResourceName),
-		ReadOffset:   req.ReadOffset,
-		ReadLimit:    req.ReadLimit,
-	})
-	if err != nil {
-		return err
-	}
-	for {
-		resp, err := rd.Recv()
-		if err == io.EOF {
-			break
-		}
-		if err != nil {
-			return err
-		}
-		err = s.Send(resp)
-		if err != nil {
-			return err
-		}
-	}
-	return nil
-}
-
-// Write proxies bytestream Write stream.
-func (bs *ByteStream) Write(s pb.ByteStream_WriteServer) error {
-	ctx := s.Context()
-	ctx, span := trace.StartSpan(ctx, "go.chromium.org/goma/server/remoteexec.Adapter.Write")
-	defer span.End()
-
-	ctx = bs.Adapter.outgoingContext(ctx, nil)
-	uuid := uuid.New()
-	wr, err := bs.Adapter.client(ctx).ByteStream().Write(ctx)
-	if err != nil {
-		return err
-	}
-	for {
-		req, err := s.Recv()
-		if err == io.EOF {
-			break
-		}
-		if err != nil {
-			wr.CloseAndRecv()
-			return nil
-		}
-		err = wr.Send(&pb.WriteRequest{
-			ResourceName: path.Join(bs.InstanceName, "uploads", uuid.String(), req.ResourceName),
-			WriteOffset:  req.WriteOffset,
-			FinishWrite:  req.FinishWrite,
-			Data:         req.Data,
-		})
-		if err != nil {
-			wr.CloseAndRecv()
-			return err
-		}
-	}
-	resp, err := wr.CloseAndRecv()
-	if err != nil {
-		return err
-	}
-	return s.SendAndClose(resp)
-}
-
-// Write proxies bytestream QueryWriteStatus call.
-func (bs *ByteStream) QueryWriteStatus(ctx context.Context, req *pb.QueryWriteStatusRequest) (*pb.QueryWriteStatusResponse, error) {
-	ctx, span := trace.StartSpan(ctx, "go.chromium.org/goma/server/remoteexec.Adapter.QueryWriteStatus")
-	defer span.End()
-
-	ctx = bs.Adapter.outgoingContext(ctx, nil)
-
-	return bs.Adapter.client(ctx).ByteStream().QueryWriteStatus(ctx, &pb.QueryWriteStatusRequest{
-		ResourceName: path.Join(bs.InstanceName, req.ResourceName),
-	})
-}
diff --git a/remoteexec/cas/bytestream.go b/remoteexec/cas/bytestream.go
deleted file mode 100644
index 3a23cc0..0000000
--- a/remoteexec/cas/bytestream.go
+++ /dev/null
@@ -1,164 +0,0 @@
-/* Copyright 2018 Google Inc. All Rights Reserved. */
-
-package cas
-
-import (
-	"context"
-	"encoding/hex"
-	"fmt"
-	"io"
-	"path"
-	"strconv"
-	"strings"
-	"sync"
-	"time"
-
-	rpb "github.com/bazelbuild/remote-apis/build/bazel/remote/execution/v2"
-
-	"github.com/google/uuid"
-	"go.opencensus.io/trace"
-	bpb "google.golang.org/genproto/googleapis/bytestream"
-	"google.golang.org/grpc/status"
-
-	"go.chromium.org/goma/server/bytestreamio"
-	"go.chromium.org/goma/server/log"
-)
-
-// The current maximum data chunk size for both Read() and Write() is 2MB.
-const maxChunkSizeBytes = 2 * 1024 * 1024
-
-var bufPool = sync.Pool{
-	New: func() interface{} {
-		return make([]byte, maxChunkSizeBytes)
-	},
-}
-
-func ioCopyBuffer(wr io.Writer, rd io.Reader) (int64, error) {
-	buf := bufPool.Get().([]byte)
-	defer bufPool.Put(buf)
-	return io.CopyBuffer(wr, rd, buf)
-}
-
-// ParseResName parses resource name; digest string formatted as "blobs/<hash>/<sizebytes>".
-// It ignores prior to "blobs", and after <sizebytes>.
-// https://github.com/bazelbuild/remote-apis/blob/c1c1ad2c97ed18943adb55f06657440daa60d833/build/bazel/remote/execution/v2/remote_execution.proto#L168
-func ParseResName(name string) (*rpb.Digest, error) {
-	pc := strings.Split(name, "/")
-	for i, s := range pc {
-		if s == "blobs" && i+2 < len(pc) {
-			hash := strings.ToLower(pc[i+1])
-			if len(hash) != 64 {
-				return nil, fmt.Errorf("%q: hash %q is not 64 bytes", name, pc[i+1])
-			}
-			if hash != pc[i+1] {
-				return nil, fmt.Errorf("%q: hash %q is not lowercase", name, pc[i+1])
-			}
-			_, err := hex.DecodeString(hash)
-			if err != nil {
-				return nil, fmt.Errorf("%q: hash %q is not hex string: %v", name, pc[i+1], err)
-			}
-			n, err := strconv.ParseInt(pc[i+2], 10, 64)
-			if err != nil {
-				return nil, fmt.Errorf("%q: sizebytes %q is not number: %v", name, pc[i+2], err)
-			}
-			return &rpb.Digest{
-				Hash:      hash,
-				SizeBytes: n,
-			}, nil
-		}
-	}
-	return nil, fmt.Errorf("%q: not resource name", name)
-}
-
-func UploadDigest(ctx context.Context, bs bpb.ByteStreamClient, instance string, digest *rpb.Digest, rd io.Reader) error {
-	resname := UploadResName(instance, digest)
-	return Upload(ctx, bs, resname, digest.SizeBytes, rd)
-}
-
-// UploadResName returns resource name of digest in instance to upload.
-// https://github.com/bazelbuild/remote-apis/blob/c1c1ad2c97ed18943adb55f06657440daa60d833/build/bazel/remote/execution/v2/remote_execution.proto#L187
-func UploadResName(instance string, digest *rpb.Digest) string {
-	uuid := uuid.New()
-	return path.Join(instance, "uploads", uuid.String(), "blobs", digest.Hash, strconv.FormatInt(digest.SizeBytes, 10))
-}
-
-type ioReader struct {
-	io.Reader
-}
-
-// Upload uploads blob specified by resname from rd.
-func Upload(ctx context.Context, bs bpb.ByteStreamClient, resname string, size int64, rd io.Reader) error {
-	span := trace.FromContext(ctx)
-	logger := log.FromContext(ctx)
-	logger.Infof("upload %s", resname)
-	span.AddAttributes(trace.StringAttribute("resname", resname))
-
-	wr, err := bytestreamio.Create(ctx, bs, resname)
-	if err != nil {
-		s := status.Convert(err)
-		return status.Errorf(s.Code(), "upload write %s: %v", resname, s.Message())
-	}
-	t0 := time.Now()
-	// drop WriteTo method in rd.
-	written, err := ioCopyBuffer(wr, ioReader{rd})
-	if err != nil {
-		wr.Close()
-		logger.Warnf("upload failed %s %d in %s: %v", resname, written, time.Since(t0), err)
-		s := status.Convert(err)
-		return status.Errorf(s.Code(), "upload error %s %d: %v", resname, written, s.Message())
-	}
-	err = wr.Close()
-	if err != nil {
-		s := status.Convert(err)
-		return status.Errorf(s.Code(), "upload close: %s: %v", resname, s.Message())
-	}
-	logger.Infof("upload %s in %s", resname, time.Since(t0))
-	return nil
-}
-
-// DownloadDigest downloads blob specified resname/digest into w.
-func DownloadDigest(ctx context.Context, bs bpb.ByteStreamClient, wr io.Writer, instance string, digest *rpb.Digest) error {
-	resname := ResName(instance, digest)
-	size, err := Download(ctx, bs, wr, resname)
-	if err != nil {
-		return err
-	}
-	if size != digest.SizeBytes {
-		return fmt.Errorf("incomplete fetch %v: size=%d", digest, size)
-	}
-	return nil
-}
-
-// ResName returns resource name of digest in instance.
-// https://github.com/bazelbuild/remote-apis/blob/c1c1ad2c97ed18943adb55f06657440daa60d833/build/bazel/remote/execution/v2/remote_execution.proto#L220
-func ResName(instance string, digest *rpb.Digest) string {
-	return path.Join(instance, "blobs", digest.Hash, strconv.FormatInt(digest.SizeBytes, 10))
-}
-
-type ioWriter struct {
-	io.Writer
-}
-
-// Download downloads blob specified by resname into w.
-func Download(ctx context.Context, bs bpb.ByteStreamClient, wr io.Writer, resname string) (int64, error) {
-	span := trace.FromContext(ctx)
-	logger := log.FromContext(ctx)
-	t := time.Now()
-	logger.Infof("download %s", resname)
-	span.AddAttributes(trace.StringAttribute("resname", resname))
-
-	rd, err := bytestreamio.Open(ctx, bs, resname)
-	if err != nil {
-		s := status.Convert(err)
-		return 0, status.Errorf(s.Code(), "download read: %s: %v", resname, s.Message())
-	}
-	// drop ReadFrom method in wr
-	written, err := ioCopyBuffer(ioWriter{wr}, rd)
-	if err != nil {
-		logger.Warnf("download failed %s %d in %s: %v", resname, written, time.Since(t), err)
-		s := status.Convert(err)
-		return written, status.Errorf(s.Code(), "download error %s %d: %v", resname, written, s.Message())
-	}
-	logger.Infof("download %s in %s", resname, time.Since(t))
-	return int64(written), nil
-}
diff --git a/remoteexec/cas/cas.go b/remoteexec/cas/cas.go
deleted file mode 100644
index 93d247b..0000000
--- a/remoteexec/cas/cas.go
+++ /dev/null
@@ -1,388 +0,0 @@
-/* Copyright 2018 Google Inc. All Rights Reserved. */
-
-// Package cas manages content addressable storage.
-package cas
-
-import (
-	"context"
-	"errors"
-	"fmt"
-	"sort"
-	"sync"
-	"time"
-
-	rpb "github.com/bazelbuild/remote-apis/build/bazel/remote/execution/v2"
-	"go.opencensus.io/trace"
-	bpb "google.golang.org/genproto/googleapis/bytestream"
-	"google.golang.org/grpc"
-	"google.golang.org/grpc/codes"
-	"google.golang.org/grpc/status"
-	"google.golang.org/protobuf/proto"
-
-	"go.chromium.org/goma/server/log"
-	"go.chromium.org/goma/server/remoteexec/datasource"
-	"go.chromium.org/goma/server/remoteexec/digest"
-	"go.chromium.org/goma/server/rpc"
-)
-
-const (
-	// DefaultBatchByteLimit is bytes limit for cas BatchUploadBlobs.
-	DefaultBatchByteLimit = 4 * 1024 * 1024
-	// BatchBlobLimit is max number of blobs in BatchUploadBlobs.
-	batchBlobLimit = 1000
-)
-
-// Client is a client of cas service.
-type Client interface {
-	CAS() rpb.ContentAddressableStorageClient
-	ByteStream() bpb.ByteStreamClient
-}
-
-type client struct {
-	*grpc.ClientConn
-}
-
-func (c client) CAS() rpb.ContentAddressableStorageClient {
-	return rpb.NewContentAddressableStorageClient(c.ClientConn)
-}
-
-func (c client) ByteStream() bpb.ByteStreamClient {
-	return bpb.NewByteStreamClient(c.ClientConn)
-}
-
-// NewClient creates client from conn.
-func NewClient(conn *grpc.ClientConn) Client {
-	return client{conn}
-}
-
-// CAS is content-addressable-storage synced between local and cas service.
-type CAS struct {
-	Client
-	*digest.Store
-
-	CacheCapabilities *rpb.CacheCapabilities
-}
-
-// TODO: unit test
-
-// Missing checks blobs in local exists in instance of cas service,
-// and returns missing blobs.
-func (c CAS) Missing(ctx context.Context, instance string, blobs []*rpb.Digest) ([]*rpb.Digest, error) {
-	span := trace.FromContext(ctx)
-	logger := log.FromContext(ctx)
-	logger.Infof("check %d blobs in %s", len(blobs), instance)
-	span.Annotatef(nil, "check %d blobs", len(blobs))
-	resp, err := c.Client.CAS().FindMissingBlobs(ctx, &rpb.FindMissingBlobsRequest{
-		InstanceName: instance,
-		BlobDigests:  blobs,
-	})
-	if err != nil {
-		return nil, grpc.Errorf(grpc.Code(err), "missing blobs: %v", err)
-	}
-	span.Annotatef(nil, "missings %d blobs", len(resp.MissingBlobDigests))
-	logger.Infof("missings %v", resp.MissingBlobDigests)
-	return resp.MissingBlobDigests, nil
-}
-
-var (
-	errBlobNotInReq = errors.New("blob not in request")
-)
-
-// MissingBlob is a missing blog.
-type MissingBlob struct {
-	Digest *rpb.Digest
-	Err    error
-}
-
-// MissingError is an error about missing content for blobs.
-type MissingError struct {
-	Blobs []MissingBlob
-}
-
-func (e MissingError) Error() string {
-	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
-	}
-
-	sort.Slice(blobs, func(i, j int) bool {
-		return blobs[i].SizeBytes < blobs[j].SizeBytes
-	})
-
-	// 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
-	if maxSizeBytes >= byteLimit {
-		maxSizeBytes = byteLimit
-	}
-	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:]
-	}
-	return blobs, nil
-}
-
-func lookupBlobsInStore(ctx context.Context, blobs []*rpb.Digest, store *digest.Store, sema chan struct{}) ([]*rpb.BatchUpdateBlobsRequest_Request, []MissingBlob) {
-	span := trace.FromContext(ctx)
-
-	var wg sync.WaitGroup
-
-	type blobLookupResult struct {
-		err error
-		req *rpb.BatchUpdateBlobsRequest_Request
-	}
-	results := make([]blobLookupResult, len(blobs))
-
-	for i := range blobs {
-		wg.Add(1)
-		go func(blob *rpb.Digest, result *blobLookupResult) {
-			defer wg.Done()
-			sema <- struct{}{}
-			defer func() {
-				<-sema
-			}()
-
-			data, ok := store.Get(blob)
-			if !ok {
-				span.Annotatef(nil, "blob not found in cas: %v", blob)
-				result.err = errBlobNotInReq
-				return
-			}
-			b, err := datasource.ReadAll(ctx, data)
-			if err != nil {
-				span.Annotatef(nil, "blob data for %v: %v", blob, err)
-				result.err = err
-				return
-			}
-			// TODO: This is inefficient because we are reading all
-			// sources whether or not they are going to be returned, due to the
-			// size computation happening later. This might be okay as long as
-			// we are not reading too much extra data in one operation.
-			//
-			// We should instead return all blob requests for blobs < `byteLimit`,
-			// batched into multiple BatchUpdateBlobsRequests.
-			result.req = &rpb.BatchUpdateBlobsRequest_Request{
-				Digest: data.Digest(),
-				Data:   b,
-			}
-		}(blobs[i], &results[i])
-	}
-	wg.Wait()
-
-	var reqs []*rpb.BatchUpdateBlobsRequest_Request
-	var missingBlobs []MissingBlob
-
-	logger := log.FromContext(ctx)
-	for i, result := range results {
-		blob := blobs[i]
-		if result.err != nil {
-			missingBlobs = append(missingBlobs, MissingBlob{
-				Digest: blob,
-				Err:    result.err,
-			})
-			continue
-		}
-		if result.req != nil {
-			reqs = append(reqs, result.req)
-			continue
-		}
-		logger.Errorf("Lookup of blobs[%d]=%v yielded neither error nor request", i, blob)
-	}
-	return reqs, missingBlobs
-}
-
-func createBatchUpdateBlobsRequests(blobReqs []*rpb.BatchUpdateBlobsRequest_Request, instance string, byteLimit int64) []*rpb.BatchUpdateBlobsRequest {
-	var batchReqs []*rpb.BatchUpdateBlobsRequest
-
-	batchReqNoReqsSize := int64(proto.Size(&rpb.BatchUpdateBlobsRequest{InstanceName: instance}))
-	size := batchReqNoReqsSize
-
-	lastOffset := 0
-	for i := range blobReqs {
-		// This code assumes that all blobs in `blobReqs`, when added as the only element of
-		// `batchReq.Requests`, will keep the marshaled proto size of `batchReq` < `byteLimit`.
-		// If `byteLimit` is 0, then it is ignored.
-
-		// Determine the extra proto size introduced by adding the current req.
-		size += int64(proto.Size(&rpb.BatchUpdateBlobsRequest{Requests: blobReqs[i : i+1]}))
-
-		// Add a new BatchUpdateBlobsRequest with blobs from the first blob after the
-		// previous BatchUpdateBlobsRequest up to and including the current blob, if:
-		// - this is the final blob
-		// - adding this blob reaches the blob count limit
-		// - adding the next blob pushes the size over the byte limit
-		switch {
-		case i == len(blobReqs)-1:
-			fallthrough
-		case i+1 == lastOffset+batchBlobLimit:
-			fallthrough
-		case byteLimit > 0 && size+int64(proto.Size(&rpb.BatchUpdateBlobsRequest{Requests: blobReqs[i+1 : i+2]})) > byteLimit:
-			batchReqs = append(batchReqs, &rpb.BatchUpdateBlobsRequest{
-				InstanceName: instance,
-				Requests:     blobReqs[lastOffset : i+1],
-			})
-			size = batchReqNoReqsSize
-			lastOffset = i + 1
-		}
-	}
-	return batchReqs
-}
-
-// Upload uploads blobs stored in Store to instance of cas service.
-func (c CAS) Upload(ctx context.Context, instance string, sema chan struct{}, blobs ...*rpb.Digest) error {
-	span := trace.FromContext(ctx)
-	logger := log.FromContext(ctx)
-	logger.Infof("upload blobs %v", blobs)
-
-	// up to max_batch_total_size_bytes, use BatchUpdateBlobs.
-	// more than this, use bytestream.Write.
-	byteLimit := int64(DefaultBatchByteLimit)
-	if c.CacheCapabilities != nil && c.CacheCapabilities.MaxBatchTotalSizeBytes > 0 {
-		byteLimit = c.CacheCapabilities.MaxBatchTotalSizeBytes
-	}
-	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)
-	missing := MissingError{
-		Blobs: missingBlobs,
-	}
-
-	batchReqs := createBatchUpdateBlobsRequests(blobReqs, instance, byteLimit)
-	for _, batchReq := range batchReqs {
-		uploaded := false
-		for !uploaded {
-			t := time.Now()
-			span.Annotatef(nil, "batch update %d blobs", len(batchReq.Requests))
-			// TODO: should we report rpc error as missing input too?
-			var batchResp *rpb.BatchUpdateBlobsResponse
-			err := rpc.Retry{}.Do(ctx, func() error {
-				var err error
-				batchResp, err = c.Client.CAS().BatchUpdateBlobs(ctx, batchReq)
-				return fixRBEInternalError(err)
-			})
-			if err != nil {
-				if grpc.Code(err) == codes.ResourceExhausted {
-					// gRPC returns ResourceExhausted if request message is larger than max.
-					logger.Warnf("upload by batch %d blobs: %v", len(batchReq.Requests), err)
-					// try with bytestream.
-					// TODO: retry with fewer blobs?
-					continue
-				}
-
-				return grpc.Errorf(grpc.Code(err), "batch update blobs: %v", err)
-			}
-			for _, res := range batchResp.Responses {
-				st := status.FromProto(res.GetStatus())
-				if st.Code() != codes.OK {
-					span.Annotatef(nil, "batch update blob %v: %v", res.Digest, res.Status)
-					missing.Blobs = append(missing.Blobs, MissingBlob{
-						Digest: res.Digest,
-						Err:    grpc.Errorf(st.Code(), "batch update blob: %v", res.Status),
-					})
-				}
-			}
-			uploaded = true
-			logger.Infof("upload by batch %d blobs (missing:%d) in %s", len(batchReq.Requests), len(missing.Blobs), time.Since(t))
-		}
-	}
-	logger.Infof("upload by streaming from %d out of %d", len(largeBlobs), len(blobs))
-	t := time.Now()
-	for _, blob := range largeBlobs {
-		data, ok := c.Store.Get(blob)
-		if !ok {
-			span.Annotatef(nil, "blob not found in cas: %v", blob)
-			missing.Blobs = append(missing.Blobs, MissingBlob{
-				Digest: blob,
-				Err:    errBlobNotInReq,
-			})
-			continue
-		}
-		err := rpc.Retry{}.Do(ctx, func() error {
-			rd, err := data.Open(ctx)
-			if err != nil {
-				span.Annotatef(nil, "upload open %v: %v", blob, err)
-				return err
-			}
-			err = UploadDigest(ctx, c.Client.ByteStream(), instance, blob, rd)
-			if err != nil {
-				rd.Close()
-				return fixRBEInternalError(err)
-			}
-			rd.Close()
-			return nil
-		})
-		if err != nil {
-			logger.Errorf("upload streaming %s error: %v", blob, err)
-			missing.Blobs = append(missing.Blobs, MissingBlob{
-				Digest: blob,
-				Err:    err,
-			})
-			continue
-		}
-	}
-	logger.Infof("uploaded by streaming %d (missing:%d) in %s", len(blobs), len(missing.Blobs), time.Since(t))
-	if len(missing.Blobs) > 0 {
-		return missing
-	}
-	return nil
-}
-
-func fixRBEInternalError(err error) error {
-	if status.Code(err) == codes.Internal {
-		return status.Errorf(codes.Unavailable, "%v", err)
-	}
-	return err
-}
diff --git a/remoteexec/cas/cas_test.go b/remoteexec/cas/cas_test.go
deleted file mode 100644
index 02f1b75..0000000
--- a/remoteexec/cas/cas_test.go
+++ /dev/null
@@ -1,800 +0,0 @@
-// Copyright 2019 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.
-
-package cas
-
-import (
-	"context"
-	"crypto/sha256"
-	"fmt"
-	"reflect"
-	"testing"
-
-	rdigest "github.com/bazelbuild/remote-apis-sdks/go/pkg/digest"
-	rpb "github.com/bazelbuild/remote-apis/build/bazel/remote/execution/v2"
-	"github.com/google/go-cmp/cmp"
-	"github.com/google/go-cmp/cmp/cmpopts"
-	"google.golang.org/protobuf/testing/protocmp"
-
-	"go.chromium.org/goma/server/remoteexec/digest"
-)
-
-type blobData struct {
-	digest *rpb.Digest
-	data   []byte
-}
-
-func makeBlobData(data string) *blobData {
-	hash := sha256.Sum256([]byte(data))
-	return &blobData{
-		digest: &rpb.Digest{
-			Hash:      fmt.Sprintf("%x", hash),
-			SizeBytes: int64(len(data)),
-		},
-		data: []byte(data),
-	}
-}
-
-func getDigests(bds []*blobData) []*rpb.Digest {
-	var result []*rpb.Digest
-	for _, bd := range bds {
-		result = append(result, bd.digest)
-	}
-	return result
-}
-
-func concatDigests(digests ...[]*rpb.Digest) []*rpb.Digest {
-	var result []*rpb.Digest
-	for _, digest := range digests {
-		result = append(result, digest...)
-	}
-	return result
-}
-
-func blobDataToBatchUpdateReq(b *blobData) *rpb.BatchUpdateBlobsRequest_Request {
-	return &rpb.BatchUpdateBlobsRequest_Request{
-		Digest: b.digest,
-		Data:   b.data,
-	}
-}
-
-func protoEqual(x, y interface{}) bool {
-	return cmp.Equal(x, y, protocmp.Transform(), cmpopts.EquateEmpty())
-}
-
-func TestMissing(t *testing.T) {
-	// Blobs already in CAS.
-	presentBlobs := []*blobData{
-		makeBlobData("5WGm1JJ1x77KSrlRgzxL"),
-		makeBlobData("ZJ0BiCaayupcdD2nRTmXXrre772lCF"),
-		makeBlobData("o2JzZO7qr6dwwR2CmXZtWDJ65ZkT885aruPAe0nm"),
-	}
-	// Blobs not in CAS.
-	missingBlobs := []*rpb.Digest{
-		{
-			Hash:      "1a77aacc1ed3ea410230d66f1238d5a8",
-			SizeBytes: 50,
-		},
-		{
-			Hash:      "bad2614f186bf481ee339896089825b5",
-			SizeBytes: 60,
-		},
-		{
-			Hash:      "6f2bf26893e588575985446bf9fd116e",
-			SizeBytes: 70,
-		},
-	}
-
-	allBlobs := append(getDigests(presentBlobs), missingBlobs...)
-
-	for _, tc := range []struct {
-		desc         string
-		blobs        []*rpb.Digest
-		presentBlobs []*blobData
-		wantMissing  []*rpb.Digest
-	}{
-		{
-			desc:        "empty CAS",
-			blobs:       allBlobs,
-			wantMissing: allBlobs,
-		},
-		{
-			desc:         "only present blobs",
-			blobs:        allBlobs[:3],
-			presentBlobs: presentBlobs,
-		},
-		{
-			desc:         "present and missing blobs",
-			blobs:        allBlobs,
-			presentBlobs: presentBlobs,
-			wantMissing:  missingBlobs,
-		},
-	} {
-		t.Run(tc.desc, func(t *testing.T) {
-			instance := "instance"
-			fc, err := newFakeCASClient(0, instance)
-			defer fc.teardown()
-			if err != nil {
-				t.Errorf("err=%q, want nil", err)
-				return
-			}
-			for _, blob := range tc.presentBlobs {
-				fc.server.cas.Put(blob.data)
-			}
-
-			cas := CAS{Client: fc}
-			ctx := context.Background()
-			missing, err := cas.Missing(ctx, instance, tc.blobs)
-			if err != nil {
-				t.Errorf("err=%q; want nil", err)
-			}
-			if !protoEqual(missing, tc.wantMissing) {
-				t.Errorf("missing=%q; want=%q", missing, tc.wantMissing)
-			}
-		})
-	}
-}
-
-func TestSeparateBlobsByByteLimit(t *testing.T) {
-	blobs := []*rpb.Digest{
-		{
-			Hash:      "5baa6de0968b9ef4607ea7c62f847c4b",
-			SizeBytes: 20,
-		},
-		{
-			Hash:      "1acc7f1fc0f1c72e10f178f86b7d369b",
-			SizeBytes: 40,
-		},
-		{
-			Hash:      "bad2614f186bf481ee339896089825b5",
-			SizeBytes: 60,
-		},
-		{
-			Hash:      "87a890520c755d7b5fd322f6e3c487e2",
-			SizeBytes: 80,
-		},
-		{
-			Hash:      "51656a4fad2e76ec95dd969d18e87994",
-			SizeBytes: 100,
-		},
-		{
-			Hash:      "e0fe265acd2314151b4b5954ec1f748d",
-			SizeBytes: 130,
-		},
-		{
-			Hash:      "1a77aacc1ed3ea410230d66f1238d5a8",
-			SizeBytes: 150,
-		},
-		{
-			Hash:      "6f2bf26893e588575985446bf9fd116e",
-			SizeBytes: 170,
-		},
-		{
-			Hash:      "4381b565d55c06d4021488ecaed98704",
-			SizeBytes: 190,
-		},
-	}
-
-	for _, tc := range []struct {
-		desc      string
-		blobs     []*rpb.Digest
-		byteLimit int64
-		wantSmall []*rpb.Digest
-		wantLarge []*rpb.Digest
-	}{
-		{
-			desc: "all small blobs",
-			blobs: []*rpb.Digest{
-				blobs[0],
-				blobs[7],
-				blobs[4],
-				blobs[6],
-				blobs[2],
-				blobs[3],
-				blobs[5],
-				blobs[8],
-				blobs[1],
-			},
-			byteLimit: 300,
-			wantSmall: blobs,
-			wantLarge: []*rpb.Digest{},
-		},
-		{
-			desc: "all large blobs",
-			blobs: []*rpb.Digest{
-				blobs[6],
-				blobs[0],
-				blobs[1],
-				blobs[2],
-				blobs[7],
-				blobs[4],
-				blobs[5],
-				blobs[8],
-				blobs[3],
-			},
-			byteLimit: 40,
-			wantSmall: []*rpb.Digest{},
-			wantLarge: blobs,
-		},
-		{
-			desc: "small and large blobs",
-			blobs: []*rpb.Digest{
-				blobs[5],
-				blobs[3],
-				blobs[7],
-				blobs[1],
-				blobs[2],
-				blobs[4],
-				blobs[0],
-				blobs[6],
-				blobs[8],
-			},
-			byteLimit: 150,
-			wantSmall: blobs[:4],
-			wantLarge: blobs[4:],
-		},
-	} {
-		instance := "default"
-		t.Run(tc.desc, func(t *testing.T) {
-			small, large := separateBlobsByByteLimit(tc.blobs, instance, tc.byteLimit)
-			if !protoEqual(small, tc.wantSmall) {
-				t.Errorf("small=%q; want %q", small, tc.wantSmall)
-			}
-			if !protoEqual(large, tc.wantLarge) {
-				t.Errorf("large=%q; want %q", large, tc.wantLarge)
-			}
-		})
-	}
-}
-
-func TestUpload(t *testing.T) {
-	// Blobs that are present in both local Store and file_server.
-	presentBlobs := []*blobData{
-		makeBlobData("5WGm1JJ1x77KSrlRgzxL"),
-		makeBlobData("ZJ0BiCaayupcdD2nRTmXXrre772lCF"),
-		makeBlobData("o2JzZO7qr6dwwR2CmXZtWDJ65ZkT885aruPAe0nm"),
-	}
-
-	// Blobs present on local Store but missing from file_server.
-	missingFileBlobs := []*rpb.Digest{
-		{
-			Hash:      "1a77aacc1ed3ea410230d66f1238d5a8",
-			SizeBytes: 50,
-		}, {
-			Hash:      "bad2614f186bf481ee339896089825b5",
-			SizeBytes: 60,
-		}, {
-			Hash:      "6f2bf26893e588575985446bf9fd116e",
-			SizeBytes: 70,
-		},
-	}
-
-	// Blobs missing from local Store.
-	missingStoreBlobs := []*rpb.Digest{
-		{
-			Hash:      "87a890520c755d7b5fd322f6e3c487e2",
-			SizeBytes: 80,
-		},
-		{
-			Hash:      "4381b565d55c06d4021488ecaed98704",
-			SizeBytes: 90,
-		},
-		{
-			Hash:      "51656a4fad2e76ec95dd969d18e87994",
-			SizeBytes: 100,
-		},
-	}
-
-	store := digest.NewStore()
-	for _, blob := range presentBlobs {
-		store.Set(makeFakeDigestData(blob.digest, blob.data))
-	}
-	for _, blob := range missingFileBlobs {
-		store.Set(makeFakeDigestData(blob, nil))
-	}
-
-	toString := func(d *rpb.Digest) string {
-		return fmt.Sprintf("%s/%d", d.Hash, d.SizeBytes)
-	}
-
-	for _, tc := range []struct {
-		desc                    string
-		blobs                   []*rpb.Digest
-		byteLimit               int64
-		wantStored              map[string][]byte
-		wantMissing             []MissingBlob
-		wantNumBatchUpdates     int
-		wantNumByteStreamWrites int
-	}{
-		{
-			desc:  "present blobs",
-			blobs: getDigests(presentBlobs),
-			wantStored: map[string][]byte{
-				toString(presentBlobs[0].digest): presentBlobs[0].data,
-				toString(presentBlobs[1].digest): presentBlobs[1].data,
-				toString(presentBlobs[2].digest): presentBlobs[2].data,
-			},
-			wantNumBatchUpdates: 1,
-		},
-		{
-			desc:  "missing blobs",
-			blobs: concatDigests(missingStoreBlobs, missingFileBlobs),
-			wantMissing: []MissingBlob{
-				{
-					Digest: missingFileBlobs[0],
-					Err:    errFakeSourceNotFound,
-				},
-				{
-					Digest: missingFileBlobs[1],
-					Err:    errFakeSourceNotFound,
-				},
-				{
-					Digest: missingFileBlobs[2],
-					Err:    errFakeSourceNotFound,
-				},
-				{
-					Digest: missingStoreBlobs[0],
-					Err:    errBlobNotInReq,
-				},
-				{
-					Digest: missingStoreBlobs[1],
-					Err:    errBlobNotInReq,
-				},
-				{
-					Digest: missingStoreBlobs[2],
-					Err:    errBlobNotInReq,
-				},
-			},
-			wantStored: map[string][]byte{},
-		},
-		{
-			desc:  "present and missing blobs",
-			blobs: concatDigests(missingFileBlobs, missingStoreBlobs, getDigests(presentBlobs)),
-			wantStored: map[string][]byte{
-				toString(presentBlobs[0].digest): presentBlobs[0].data,
-				toString(presentBlobs[1].digest): presentBlobs[1].data,
-				toString(presentBlobs[2].digest): presentBlobs[2].data,
-			},
-			wantMissing: []MissingBlob{
-				{
-					Digest: missingFileBlobs[0],
-					Err:    errFakeSourceNotFound,
-				},
-				{
-					Digest: missingFileBlobs[1],
-					Err:    errFakeSourceNotFound,
-				},
-				{
-					Digest: missingFileBlobs[2],
-					Err:    errFakeSourceNotFound,
-				},
-				{
-					Digest: missingStoreBlobs[0],
-					Err:    errBlobNotInReq,
-				},
-				{
-					Digest: missingStoreBlobs[1],
-					Err:    errBlobNotInReq,
-				},
-				{
-					Digest: missingStoreBlobs[2],
-					Err:    errBlobNotInReq,
-				},
-			},
-			wantNumBatchUpdates: 1,
-		},
-		{
-			desc:      "present and missing blobs with limit > max blob size",
-			blobs:     concatDigests(missingStoreBlobs, getDigests(presentBlobs), missingFileBlobs),
-			byteLimit: 500,
-			wantStored: map[string][]byte{
-				toString(presentBlobs[0].digest): presentBlobs[0].data,
-				toString(presentBlobs[1].digest): presentBlobs[1].data,
-				toString(presentBlobs[2].digest): presentBlobs[2].data,
-			},
-			wantMissing: []MissingBlob{
-				{
-					Digest: missingFileBlobs[0],
-					Err:    errFakeSourceNotFound,
-				},
-				{
-					Digest: missingFileBlobs[1],
-					Err:    errFakeSourceNotFound,
-				},
-				{
-					Digest: missingFileBlobs[2],
-					Err:    errFakeSourceNotFound,
-				},
-				{
-					Digest: missingStoreBlobs[0],
-					Err:    errBlobNotInReq,
-				},
-				{
-					Digest: missingStoreBlobs[1],
-					Err:    errBlobNotInReq,
-				},
-				{
-					Digest: missingStoreBlobs[2],
-					Err:    errBlobNotInReq,
-				},
-			},
-			wantNumBatchUpdates: 1,
-		},
-		{
-			desc:      "present and missing blobs with limit < max blob size",
-			blobs:     concatDigests(missingStoreBlobs, missingFileBlobs, getDigests(presentBlobs)),
-			byteLimit: 110,
-			wantStored: map[string][]byte{
-				toString(presentBlobs[0].digest): presentBlobs[0].data,
-				toString(presentBlobs[1].digest): presentBlobs[1].data,
-				toString(presentBlobs[2].digest): presentBlobs[2].data,
-			},
-			wantMissing: []MissingBlob{
-				{
-					Digest: missingFileBlobs[0],
-					Err:    errFakeSourceNotFound,
-				},
-				{
-					Digest: missingFileBlobs[1],
-					Err:    errFakeSourceNotFound,
-				},
-				{
-					Digest: missingFileBlobs[2],
-					Err:    errFakeSourceNotFound,
-				},
-				{
-					Digest: missingStoreBlobs[0],
-					Err:    errBlobNotInReq,
-				},
-				{
-					Digest: missingStoreBlobs[1],
-					Err:    errBlobNotInReq,
-				},
-				{
-					Digest: missingStoreBlobs[2],
-					Err:    errBlobNotInReq,
-				},
-			},
-			wantNumBatchUpdates:     1,
-			wantNumByteStreamWrites: 2,
-		},
-	} {
-		t.Run(tc.desc, func(t *testing.T) {
-			instance := "instance"
-			fc, err := newFakeCASClient(tc.byteLimit, instance)
-			defer fc.teardown()
-			if err != nil {
-				t.Errorf("err=%q, want nil", err)
-				return
-			}
-
-			cas := CAS{
-				Client:            fc,
-				Store:             store,
-				CacheCapabilities: &rpb.CacheCapabilities{MaxBatchTotalSizeBytes: tc.byteLimit},
-			}
-			ctx := context.Background()
-			sema := make(chan struct{}, 100)
-			err = cas.Upload(ctx, instance, sema, tc.blobs...)
-
-			if tc.wantMissing != nil {
-				if missing, ok := err.(MissingError); ok {
-					if !reflect.DeepEqual(missing.Blobs, tc.wantMissing) {
-						t.Errorf("missing.Blobs=%q; want=%q", missing.Blobs, tc.wantMissing)
-					}
-				} else {
-					t.Errorf("Unexpected error: %q", err)
-				}
-			} else if err != nil {
-				t.Errorf("Unexpected error: %q", err)
-			}
-
-			casSrv := fc.server.cas
-			if casSrv.BatchReqs() != tc.wantNumBatchUpdates {
-				t.Errorf("casSrv.BatchReqs()=%d, want=%d", casSrv.BatchReqs(), tc.wantNumBatchUpdates)
-			}
-			if casSrv.WriteReqs() != tc.wantNumByteStreamWrites {
-				t.Errorf("casSrv.WriteReqs()=%d, want=%d", casSrv.WriteReqs(), tc.wantNumByteStreamWrites)
-			}
-
-			stored := map[string][]byte{}
-			for _, blob := range tc.blobs {
-				data, ok := casSrv.Get(rdigest.Digest{
-					Hash: blob.Hash,
-					Size: blob.SizeBytes,
-				})
-				if ok {
-					stored[toString(blob)] = data
-				}
-			}
-			if !reflect.DeepEqual(stored, tc.wantStored) {
-				t.Errorf("stored=%q; want=%q", stored, tc.wantStored)
-			}
-		})
-	}
-}
-
-func toBatchReqs(bds []*blobData) []*rpb.BatchUpdateBlobsRequest_Request {
-	var result []*rpb.BatchUpdateBlobsRequest_Request
-	for _, bd := range bds {
-		result = append(result, &rpb.BatchUpdateBlobsRequest_Request{
-			Digest: bd.digest,
-			Data:   bd.data,
-		})
-	}
-	return result
-}
-
-func TestLookupBlobsInStore(t *testing.T) {
-	// Blobs that are present in both local Store and file_server.
-	presentBlobs := []*blobData{
-		makeBlobData("5WGm1JJ1x77KSrlRgzxL"),
-		makeBlobData("ZJ0BiCaayupcdD2nRTmXXrre772lCF"),
-		makeBlobData("o2JzZO7qr6dwwR2CmXZtWDJ65ZkT885aruPAe0nm"),
-	}
-	// Blobs present on local Store but missing from file_server.
-	missingFileBlobs := []*rpb.Digest{
-		{
-			Hash:      "1a77aacc1ed3ea410230d66f1238d5a8",
-			SizeBytes: 50,
-		},
-		{
-			Hash:      "bad2614f186bf481ee339896089825b5",
-			SizeBytes: 60,
-		},
-		{
-			Hash:      "6f2bf26893e588575985446bf9fd116e",
-			SizeBytes: 70,
-		},
-	}
-	// Blobs missing from local Store.
-	missingStoreBlobs := []*rpb.Digest{
-		{
-			Hash:      "87a890520c755d7b5fd322f6e3c487e2",
-			SizeBytes: 80,
-		},
-		{
-			Hash:      "4381b565d55c06d4021488ecaed98704",
-			SizeBytes: 90,
-		},
-		{
-			Hash:      "51656a4fad2e76ec95dd969d18e87994",
-			SizeBytes: 100,
-		},
-	}
-	store := digest.NewStore()
-	for _, blob := range presentBlobs {
-		store.Set(makeFakeDigestData(blob.digest, blob.data))
-	}
-	for _, blob := range missingFileBlobs {
-		store.Set(makeFakeDigestData(blob, nil))
-	}
-	for _, tc := range []struct {
-		desc        string
-		blobs       []*rpb.Digest
-		wantReqs    []*rpb.BatchUpdateBlobsRequest_Request
-		wantMissing []MissingBlob
-	}{
-		{
-			desc:     "present blobs",
-			blobs:    getDigests(presentBlobs),
-			wantReqs: toBatchReqs(presentBlobs),
-		},
-		{
-			desc:  "missing blobs",
-			blobs: append(missingFileBlobs, missingStoreBlobs...),
-			wantMissing: []MissingBlob{
-				{
-					Digest: missingFileBlobs[0],
-					Err:    errFakeSourceNotFound,
-				},
-				{
-					Digest: missingFileBlobs[1],
-					Err:    errFakeSourceNotFound,
-				},
-				{
-					Digest: missingFileBlobs[2],
-					Err:    errFakeSourceNotFound,
-				},
-				{
-					Digest: missingStoreBlobs[0],
-					Err:    errBlobNotInReq,
-				},
-				{
-					Digest: missingStoreBlobs[1],
-					Err:    errBlobNotInReq,
-				},
-				{
-					Digest: missingStoreBlobs[2],
-					Err:    errBlobNotInReq,
-				},
-			},
-		},
-		{
-			desc: "present and missing blobs",
-			blobs: []*rpb.Digest{
-				presentBlobs[0].digest,
-				missingFileBlobs[0],
-				missingStoreBlobs[0],
-				presentBlobs[1].digest,
-				missingFileBlobs[1],
-				missingStoreBlobs[1],
-				presentBlobs[2].digest,
-				missingFileBlobs[2],
-				missingStoreBlobs[2],
-			},
-			wantReqs: toBatchReqs(presentBlobs),
-			wantMissing: []MissingBlob{
-				{
-					Digest: missingFileBlobs[0],
-					Err:    errFakeSourceNotFound,
-				},
-				{
-					Digest: missingStoreBlobs[0],
-					Err:    errBlobNotInReq,
-				},
-				{
-					Digest: missingFileBlobs[1],
-					Err:    errFakeSourceNotFound,
-				},
-				{
-					Digest: missingStoreBlobs[1],
-					Err:    errBlobNotInReq,
-				},
-				{
-					Digest: missingFileBlobs[2],
-					Err:    errFakeSourceNotFound,
-				},
-				{
-					Digest: missingStoreBlobs[2],
-					Err:    errBlobNotInReq,
-				},
-			},
-		},
-	} {
-		// Do test here
-		t.Run(tc.desc, func(t *testing.T) {
-			ctx := context.Background()
-			sema := make(chan struct{}, 100)
-			reqs, missing := lookupBlobsInStore(ctx, tc.blobs, store, sema)
-			if !protoEqual(reqs, tc.wantReqs) {
-				t.Errorf("reqs=%q; want %q", reqs, tc.wantReqs)
-			}
-			if !reflect.DeepEqual(missing, tc.wantMissing) {
-				t.Errorf("missing=%q; want %q", missing, tc.wantMissing)
-			}
-		})
-	}
-}
-
-func TestCreateBatchUpdateBlobsRequests(t *testing.T) {
-	blobs := []*blobData{
-		makeBlobData("2aMmqx86iH"),
-		makeBlobData("5WGm1JJ1x77KSrlRgzxL"),
-		makeBlobData("ZJ0BiCaayupcdD2nRTmXXrre772lCF"),
-		makeBlobData("o2JzZO7qr6dwwR2CmXZtWDJ65ZkT885aruPAe0nm"),
-		makeBlobData("q7cBg9I69ZiXwe1U883vSwLIXRZ2eGNUMD2gIeqSqWfLK9IYZh"),
-		makeBlobData("iyBzGRoMAqpTEaseblU5wl9S2aub0tzhOpQYlwhDcRCQh32XSTOIueVN29mC"),
-	}
-	var blobReqs []*rpb.BatchUpdateBlobsRequest_Request
-	for _, blob := range blobs {
-		blobReqs = append(blobReqs, blobDataToBatchUpdateReq(blob))
-	}
-
-	// Large group of blobs for testing `batchBlobLimit`
-	bigBlobReqs := make([]*rpb.BatchUpdateBlobsRequest_Request, batchBlobLimit+2, batchBlobLimit+2)
-	for i := range bigBlobReqs {
-		bigBlobReqs[i] = blobDataToBatchUpdateReq(blobs[i%len(blobs)])
-	}
-
-	instance := "instance"
-	for _, tc := range []struct {
-		desc      string
-		reqs      []*rpb.BatchUpdateBlobsRequest_Request
-		byteLimit int64
-		want      []*rpb.BatchUpdateBlobsRequest
-	}{
-		{
-			desc: "empty input",
-		},
-		{
-			desc: "all blobs in one request",
-			reqs: blobReqs,
-			want: []*rpb.BatchUpdateBlobsRequest{
-				{
-					InstanceName: instance,
-					Requests:     blobReqs,
-				},
-			},
-		},
-		{
-			// Each blob, when added to a BatchUpdateBlobsRequest as the only element,
-			// makes the BatchUpdateBlobsRequest proto size <= 150 bytes
-			desc:      "limit 150 bytes",
-			reqs:      blobReqs,
-			byteLimit: 150,
-			want: []*rpb.BatchUpdateBlobsRequest{
-				{
-					InstanceName: instance,
-					Requests:     blobReqs[0:1],
-				},
-				{
-					InstanceName: instance,
-					Requests:     blobReqs[1:2],
-				},
-				{
-					InstanceName: instance,
-					Requests:     blobReqs[2:3],
-				},
-				{
-					InstanceName: instance,
-					Requests:     blobReqs[3:4],
-				},
-				{
-					InstanceName: instance,
-					Requests:     blobReqs[4:5],
-				},
-				{
-					InstanceName: instance,
-					Requests:     blobReqs[5:6],
-				},
-			},
-		},
-		{
-			desc:      "limit 300 bytes",
-			reqs:      blobReqs,
-			byteLimit: 300,
-			want: []*rpb.BatchUpdateBlobsRequest{
-				{
-					InstanceName: instance,
-					Requests:     blobReqs[0:3],
-				},
-				{
-					InstanceName: instance,
-					Requests:     blobReqs[3:5],
-				},
-				{
-					InstanceName: instance,
-					Requests:     blobReqs[5:6],
-				},
-			},
-		},
-		{
-			desc:      "limit 500 bytes",
-			reqs:      blobReqs,
-			byteLimit: 500,
-			want: []*rpb.BatchUpdateBlobsRequest{
-				{
-					InstanceName: instance,
-					Requests:     blobReqs[0:4],
-				},
-				{
-					InstanceName: instance,
-					Requests:     blobReqs[4:6],
-				},
-			},
-		},
-		{
-			desc: "blob count limit",
-			reqs: bigBlobReqs,
-			want: []*rpb.BatchUpdateBlobsRequest{
-				{
-					InstanceName: instance,
-					Requests:     bigBlobReqs[0:batchBlobLimit],
-				},
-				{
-					InstanceName: instance,
-					Requests:     bigBlobReqs[batchBlobLimit:],
-				},
-			},
-		},
-	} {
-		t.Run(tc.desc, func(t *testing.T) {
-			batchReqs := createBatchUpdateBlobsRequests(tc.reqs, instance, tc.byteLimit)
-			if !cmp.Equal(batchReqs, tc.want, protocmp.Transform()) {
-				t.Errorf("batchReqs=%q; want %q", batchReqs, tc.want)
-			}
-		})
-	}
-}
diff --git a/remoteexec/cas/fake_cas_for_test.go b/remoteexec/cas/fake_cas_for_test.go
deleted file mode 100644
index 2663152..0000000
--- a/remoteexec/cas/fake_cas_for_test.go
+++ /dev/null
@@ -1,92 +0,0 @@
-// Copyright 2020 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.
-
-package cas
-
-import (
-	"fmt"
-
-	"github.com/bazelbuild/remote-apis-sdks/go/pkg/fakes"
-	rpb "github.com/bazelbuild/remote-apis/build/bazel/remote/execution/v2"
-	bpb "google.golang.org/genproto/googleapis/bytestream"
-	"google.golang.org/grpc"
-
-	"go.chromium.org/goma/server/rpc/grpctest"
-)
-
-var (
-	errNotImplemented = fmt.Errorf("Function not implemented.")
-)
-
-type fakeCASServer struct {
-	srv *grpc.Server
-	cas *fakes.CAS
-
-	addr string
-	stop func()
-}
-
-func newFakeCASServer() (*fakeCASServer, error) {
-	f := &fakeCASServer{
-		srv: grpc.NewServer(),
-		cas: fakes.NewCAS(),
-	}
-	bpb.RegisterByteStreamServer(f.srv, f.cas)
-	rpb.RegisterContentAddressableStorageServer(f.srv, f.cas)
-	var err error
-	f.addr, f.stop, err = grpctest.StartServer(f.srv)
-	if err != nil {
-		f.stop()
-		return nil, err
-	}
-	return f, nil
-}
-
-type fakeCASClient struct {
-	Client
-	casClient rpb.ContentAddressableStorageClient
-	bsClient  bpb.ByteStreamClient
-
-	batchUpdateByteLimit int64
-	server               *fakeCASServer
-	conn                 *grpc.ClientConn
-}
-
-func (f *fakeCASClient) CAS() rpb.ContentAddressableStorageClient {
-	return f.casClient
-}
-
-func (f *fakeCASClient) ByteStream() bpb.ByteStreamClient {
-	return f.bsClient
-}
-
-func (f *fakeCASClient) teardown() {
-	f.conn.Close()
-	f.server.stop()
-}
-
-func newFakeCASClient(byteLimit int64, instances ...string) (*fakeCASClient, error) {
-	if byteLimit == 0 {
-		byteLimit = DefaultBatchByteLimit
-	}
-
-	f := &fakeCASClient{
-		batchUpdateByteLimit: byteLimit,
-	}
-	var err error
-
-	f.server, err = newFakeCASServer()
-	if err != nil {
-		return nil, err
-	}
-
-	f.conn, err = grpc.Dial(f.server.addr, grpc.WithInsecure())
-	if err != nil {
-		return nil, err
-	}
-	f.bsClient = bpb.NewByteStreamClient(f.conn)
-	f.casClient = rpb.NewContentAddressableStorageClient(f.conn)
-
-	return f, nil
-}
diff --git a/remoteexec/cas/fake_digest_for_test.go b/remoteexec/cas/fake_digest_for_test.go
deleted file mode 100644
index cd6f6d5..0000000
--- a/remoteexec/cas/fake_digest_for_test.go
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright 2020 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.
-
-package cas
-
-import (
-	"context"
-	"errors"
-	"io"
-
-	rpb "github.com/bazelbuild/remote-apis/build/bazel/remote/execution/v2"
-
-	"go.chromium.org/goma/server/remoteexec/digest"
-)
-
-var (
-	errFakeSourceNotFound = errors.New("Source not found")
-)
-
-type testSource struct {
-	r io.ReadCloser
-}
-
-func (s testSource) Open(ctx context.Context) (io.ReadCloser, error) {
-	if s.r == nil {
-		return nil, errFakeSourceNotFound
-	}
-	return s.r, nil
-}
-
-func (s testSource) String() string {
-	return ""
-}
-
-type testReadCloser struct {
-	data []byte
-}
-
-func (rc *testReadCloser) Read(p []byte) (n int, err error) {
-	n = copy(p, rc.data)
-	return n, io.EOF
-}
-
-func (rc *testReadCloser) Close() error {
-	return nil
-}
-
-type fakeDigestData struct {
-	digest *rpb.Digest
-	digest.Source
-}
-
-func (d *fakeDigestData) Digest() *rpb.Digest {
-	return d.digest
-}
-
-func makeFakeDigestData(digest *rpb.Digest, data []byte) *fakeDigestData {
-	var source testSource
-	if data != nil {
-		source.r = &testReadCloser{
-			data: data,
-		}
-	}
-	return &fakeDigestData{
-		digest: digest,
-		Source: source,
-	}
-}
diff --git a/remoteexec/clang-cl.go b/remoteexec/clang-cl.go
deleted file mode 100644
index 3e70565..0000000
--- a/remoteexec/clang-cl.go
+++ /dev/null
@@ -1,372 +0,0 @@
-// Copyright 2018 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.
-
-package remoteexec
-
-import (
-	"errors"
-	"fmt"
-	"strconv"
-	"strings"
-)
-
-// TODO: share exec/clangcl.go ?
-
-// longest first
-var clangClPathFlags = []string{
-	"-fcoverage-compilation-dir=",
-	"-fcrash-diagnostics-dir=",
-	"-fdebug-compilation-dir=",
-	"-ffile-compilation-dir=",
-	"-fprofile-sample-use=",
-	"-fsanitize-blacklist=",
-	"-fprofile-instr-use=",
-	"-fprofile-list=",
-	"-resource-dir=",
-	"/vctoolsdir",
-	"/winsysroot",
-	"/winsdkdir",
-	"--sysroot=",
-	"-include=",
-	"-isystem",
-	"-imsvc",
-	"/FA",
-	"/FI",
-	"/FR",
-	"/FU",
-	"/Fa",
-	"/Fd",
-	"/Fe",
-	"/Fl",
-	"/Fm",
-	"/Fo",
-	"/Fp",
-	"/Fr",
-	"/Fx",
-	"-B",
-	"-I",
-	"-o",
-}
-
-func isClangclWarningFlag(arg string) bool {
-	if len(arg) < 2 || (arg[:2] != "/W" && arg[0:2] != "/w") {
-		return false
-	}
-
-	basicFlags := []string{
-		"/w",                              // Suppress all warnings
-		"/W0", "/W1", "/W2", "/W3", "/W4", // Warning levels 0-4
-		"/Wall", // Display all warnings
-		"/WX",   // Treats warnings as errors
-		"/Wv",   // Display only warnings added in current compiler version
-	}
-	for _, flag := range basicFlags {
-		if arg == flag {
-			return true
-		}
-	}
-	// Display warnings added in given compiler version
-	if strings.HasPrefix(arg, "/Wv:") {
-		return true
-	}
-
-	// All remaining possible warnings have the format /wXnnnn
-	if len(arg) != 7 {
-		return false
-	}
-
-	switch arg[2:3] {
-	case "1", "2", "3", "4": // Set warning level for numbered warning
-		fallthrough
-	case "d": // Suppress numbered warning
-		fallthrough
-	case "e": // Treat numbered warning as error
-		fallthrough
-	case "o": // Report numbered warning only once
-		// Possible warning values: 4000-5999:
-		// https://docs.microsoft.com/en-us/cpp/error-messages/compiler-errors-1/c-cpp-build-errors
-		warning, err := strconv.Atoi(arg[3:])
-		if err != nil {
-			return false
-		}
-		return warning >= 4000 && warning < 6000
-	}
-
-	return false
-}
-
-func isClangclOptimizationFlag(arg string) bool {
-	optFlags := []string{
-		"/O1",                          // Optimize for size
-		"/O2",                          // Optimize for speed
-		"/Ob0", "/Ob1", "/Ob2", "/Ob3", // Inline function expansino
-		"/Od",         // Turn off optimization
-		"/Og",         // Global optimization
-		"/Oi", "/Oi-", // Intrinsic functions
-		"/Os", "/Ot", // Favor small/fast code
-		"/Ox",         // Enable most speed optimizations
-		"/Oy", "/Oy-", // Frame pointer omission
-	}
-	for _, flag := range optFlags {
-		if arg == flag {
-			return true
-		}
-	}
-	return false
-}
-
-// 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: Combine some of this code with gccRelocatableReq.
-func clangclRelocatableReq(filepath clientFilePath, args, envs []string) error {
-	var debugFlags []string
-	subArgs := map[string][]string{}
-	var subCmd string
-	pathFlag := false
-	winsysrootFlag := false
-Loop:
-	for _, arg := range args {
-		if pathFlag {
-			if filepath.IsAbs(arg) {
-				return fmt.Errorf("abs path: %s", arg)
-			}
-			pathFlag = false
-			continue
-		}
-		if strings.HasPrefix(arg, "/winsysroot") || strings.HasPrefix(arg, "-imsvc") {
-			winsysrootFlag = true
-		}
-		for _, fp := range clangClPathFlags {
-			if arg != fp && strings.HasPrefix(arg, fp) {
-				if filepath.IsAbs(arg[len(fp):]) {
-					return fmt.Errorf("abs path: %s", arg)
-				}
-				continue Loop
-			}
-		}
-		switch {
-		case subCmd != "":
-			subArgs[subCmd] = append(subArgs[subCmd], arg)
-			subCmd = ""
-
-		case strings.HasPrefix(arg, "-g"):
-			if arg == "-g0" {
-				debugFlags = nil
-				continue
-			}
-			debugFlags = append(debugFlags, arg)
-
-		case strings.HasPrefix(arg, "-Wa,"): // assembler arg
-			subArgs["as"] = append(subArgs["as"], strings.Split(arg[len("-Wa,"):], ",")...)
-		case strings.HasPrefix(arg, "-Wl,"): // linker arg
-			subArgs["ld"] = append(subArgs["ld"], strings.Split(arg[len("-Wl,"):], ",")...)
-		case strings.HasPrefix(arg, "-Wp,"): // preproc arg
-			subArgs["cpp"] = append(subArgs["cpp"], strings.Split(arg[len("-Wp,"):], ",")...)
-		case arg == "-Xclang":
-			subCmd = "clang"
-		case arg == "-mllvm":
-			subCmd = "llvm"
-
-		case strings.HasPrefix(arg, "-w"): // inhibit all warnings
-		case strings.HasPrefix(arg, "-W"): // warning
-		case strings.HasPrefix(arg, "-D"): // define
-		case strings.HasPrefix(arg, "-U"): // undefine
-		case strings.HasPrefix(arg, "-O"): // optimize
-		case strings.HasPrefix(arg, "-f"): // feature
-		case strings.HasPrefix(arg, "-m"):
-			// -m64, -march=x86-64
-		case arg == "-arch":
-		case strings.HasPrefix(arg, "--target="):
-
-		case strings.HasPrefix(arg, "-no"):
-			// -no-canonical-prefixes, -nostdinc++
-		case arg == "-integrated-as":
-		case arg == "-pedantic":
-		case arg == "-pipe":
-		case arg == "-pthread":
-		case arg == "-c":
-		case strings.HasPrefix(arg, "-std"):
-		case strings.HasPrefix(arg, "--param="):
-		case arg == "-MMD" || arg == "-MD" || arg == "-M":
-		case arg == "-Qunused-arguments":
-			continue
-
-		case arg == "-o":
-			pathFlag = true
-		case arg == "-I" || arg == "-B" || arg == "-isystem" || arg == "-include":
-			pathFlag = true
-		case arg == "-MF":
-			pathFlag = true
-		case arg == "-isysroot":
-			pathFlag = true
-
-		// MSVC/clang-cl options are based on:
-		// https://docs.microsoft.com/en-us/cpp/build/reference/compiler-options-listed-alphabetically?view=vs-2019
-		// https://clang.llvm.org/docs/UsersManual.html#id9
-		case arg == "/nologo":
-		case arg == "/Brepro", arg == "/Brepro-": // Emit an object file which can/cannot be reproduced over time
-		case arg == "/FS": // Forces serialization of all writes to the program database (PDB) file through MSPDBSRV.EXE
-		case arg == "/Gy", arg == "/Gy-": // Function-level linking
-		case arg == "/bigobj": // Support more sections in obj file
-		case arg == "/utf-8": // Set source and execution character set as UTF-8
-		case arg == "/X": // Ignore standard include paths
-		case arg == "/Z7", arg == "/Zi", arg == "/ZI": // Set debug format
-		case arg == "/MD", arg == "/MT", arg == "/LD": // Use normal runtime library
-		case arg == "/MDd", arg == "/MTd", arg == "/LDd": // Use debug runtime library
-		case arg == "/Tc", arg == "/Tp", arg == "/TC", arg == "/TP": // Source file type
-		case arg == "/Gd", arg == "/Gr", arg == "/Gv", arg == "/Gz": // Calling convention
-		case arg == "/GR", arg == "/GR-": // Specify RTTI
-		case arg == "/GS", arg == "/GS-": // Buffer security check
-		case arg == "/Gr", arg == "/Gr-": // Use __fastcall calling convention
-		case arg == "/Gw", arg == "/Gw-": // Optimize global data
-		case arg == "/GF": // Enables string pooling
-		case arg == "/c": // Compile without linking
-		case arg == "/showIncludes": // List include files
-		case strings.HasPrefix(arg, "/D"): // Preprocessor
-		case strings.HasPrefix(arg, "/EH"): // Specify error handling
-		case strings.HasPrefix(arg, "/Zc:"): // Specify compiler behavior
-		case strings.HasPrefix(arg, "/arch:"): // Specify CPU architecture
-		case strings.HasPrefix(arg, "/clang:"): // Clang-specific option
-		case strings.HasPrefix(arg, "/guard:cf"): // Control flow guard security checks
-		case strings.HasPrefix(arg, "/showIncludes:"): // List include files
-		case strings.HasPrefix(arg, "/std:c"): // Specify C or C++ standard
-		case isClangclWarningFlag(arg): // Flags to handle warnings
-		case isClangclOptimizationFlag(arg): // Flags to handle optimization
-			continue
-
-		case arg == "/FC": // Display full path of source code files passed to cl.exe in diagnostic text.
-			return errors.New("need full path of soruce code for /FC")
-
-		case strings.HasPrefix(arg, "-"), strings.HasPrefix(arg, "/"): // unknown flag?
-			return unknownFlagError{arg: arg}
-
-		default: // input file?
-			if filepath.IsAbs(arg) {
-				return fmt.Errorf("abs path: %s", arg)
-			}
-		}
-	}
-
-	if len(subArgs) > 0 {
-		for cmd, args := range subArgs {
-			switch cmd {
-			case "clang":
-				err := clangclArgRelocatable(filepath, args)
-				if err != nil {
-					return err
-				}
-			case "llvm":
-				err := llvmArgRelocatable(filepath, args)
-				if err != nil {
-					return err
-				}
-			default:
-				return fmt.Errorf("unsupported subcommand args %s: %s", cmd, args)
-			}
-		}
-	}
-
-	// Don't check environment variables, if -imsvc or /winsysroot is set.
-	// Typically user sets `INCLUDE=C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Tools\MSVC\14.16.27023\ATLMFC\include;...`
-	// so it makes always non-relocatble.
-	// in chromium build, clang-cl uses /winsysroot (or -imsvc in past)
-	// instead of relying on %INCLUDE%, so it would be ok to ignore
-	// environment variables.
-	// http://b/173755650
-	if winsysrootFlag {
-		return nil
-	}
-	// otherwise, check environment variables.
-	for _, env := range envs {
-		e := strings.SplitN(env, "=", 2)
-		if len(e) != 2 {
-			return fmt.Errorf("bad environment variable: %s", env)
-		}
-		if e[0] == "PWD" {
-			continue
-		}
-		// TODO: need to split by list separator
-		// for %INCLUDE% or so?
-		if filepath.IsAbs(e[1]) {
-			return fmt.Errorf("abs path in env %s=%s", e[0], e[1])
-		}
-	}
-	return nil
-}
-
-func clangclArgRelocatable(filepath clientFilePath, args []string) error {
-	pathFlag := false
-	skipFlag := false
-	for _, arg := range args {
-		switch {
-		case pathFlag:
-			if filepath.IsAbs(arg) {
-				return fmt.Errorf("clang-cl abs path: %s", arg)
-			}
-			pathFlag = false
-		case skipFlag:
-			skipFlag = false
-
-		case arg == "-fdebug-compilation-dir":
-			pathFlag = true
-		case strings.HasPrefix(arg, "-debug-info-kind"):
-			continue
-		case arg == "-add-plugin", arg == "-mllvm":
-			// TODO: pass llvmArgRelocatable for -mllvm?
-			skipFlag = true
-			continue
-		case strings.HasPrefix(arg, "-plugin-arg-"):
-			skipFlag = true
-			continue
-		case strings.HasPrefix(arg, "-f"): // feature
-		case arg == "-no-opaque-pointers":
-		default:
-			return unknownFlagError{arg: fmt.Sprintf("clang-cl: %s", arg)}
-		}
-	}
-	return nil
-}
-
-// clangclOutputs returns output files from clang-cl command line.
-// https://clang.llvm.org/docs/UsersManual.html#id8
-//
-//	/Fo<obj> and /Fd<pdb> is used in Cross-compiling Chrome/win
-//	but clang-cl currently doesn't emit pdb.
-//
-// https://chromium.googlesource.com/chromium/src/+/lkcr/docs/win_cross.md
-// TODO: support output directory (ends in / or \)?
-func clangclOutputs(args []string) []string {
-	var outputs []string
-	outputArg := false
-
-	for _, arg := range args {
-		switch {
-		case outputArg:
-			outputs = append(outputs, arg)
-			outputArg = false
-
-		case arg == "/o" || arg == "-o": // /o <file or directory>
-			outputArg = true
-
-		case len(arg) > 2 &&
-			(arg[0] == '-' || arg[0] == '/') &&
-			arg[1] == 'o':
-			outputs = append(outputs, arg[2:])
-
-		case len(arg) > 3 &&
-			(arg[0] == '-' || arg[0] == '/') &&
-			arg[1] == 'F' &&
-			(arg[2] == 'o' || // /Fo<obj>
-				arg[2] == 'i' || // /Fi<file>  preproc output
-				arg[2] == 'a' || // /Fa<file>  asm output
-				arg[2] == 'e' || // /Fe<exec>
-				arg[2] == 'p'): // /Fp<pch>
-			// TODO: /Fd<pdb> if clang-cl emits pdb.
-			outputs = append(outputs, arg[3:])
-		}
-	}
-	return outputs
-}
diff --git a/remoteexec/clang-cl_test.go b/remoteexec/clang-cl_test.go
deleted file mode 100644
index f14ca6b..0000000
--- a/remoteexec/clang-cl_test.go
+++ /dev/null
@@ -1,797 +0,0 @@
-// Copyright 2018 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.
-
-package remoteexec
-
-import (
-	"reflect"
-	"strings"
-	"testing"
-
-	"go.chromium.org/goma/server/command/descriptor/winpath"
-)
-
-func TestCLangCLPathFlags(t *testing.T) {
-	lastP := clangClPathFlags[0]
-	for _, p := range clangClPathFlags[1:] {
-		if len(lastP) < len(p) {
-			t.Errorf("%q is longer than %q", p, lastP)
-		}
-		lastP = p
-	}
-}
-
-func TestIsClangclWarningFlag(t *testing.T) {
-	for _, tc := range []struct {
-		desc  string
-		flags []string
-		want  bool
-	}{
-		{
-			desc: "basic",
-			flags: []string{
-				"/w", "/W0", "/W1", "/W2", "/W3", "/W4", "/Wall", "/WX", "/Wv",
-			},
-			want: true,
-		},
-		{
-			desc: "version",
-			flags: []string{
-				"/Wv:foo", "/Wv:bar",
-			},
-			want: true,
-		},
-		{
-			desc: "numbered warning level",
-			flags: []string{
-				"/w14000", "/w24001", "/wd4999", "/we5000", "/wo5001",
-			},
-			want: true,
-		},
-		{
-			desc: "not warning flag",
-			flags: []string{
-				"/foo", "/bar", "/", "",
-			},
-			want: false,
-		},
-		{
-			desc: "invalid numbered warning",
-			flags: []string{
-				"/wd300", "/we1000", "/wo6000", "/w1nnnn",
-			},
-			want: false,
-		},
-	} {
-		t.Run(tc.desc, func(t *testing.T) {
-			for _, flag := range tc.flags {
-				if got := isClangclWarningFlag(flag); got != tc.want {
-					t.Errorf("isClangclWarningFlag(%q)=%t; want %t", flag, got, tc.want)
-				}
-			}
-		})
-	}
-}
-
-func TestClangclRelocatableReq(t *testing.T) {
-
-	baseReleaseArgs := []string{
-		// Taken from actual Chromium Windows build args.
-		"..\\..\\third_party\\llvm-build\\Release+Asserts\\bin\\clang-cl.exe",
-		"/nologo",
-		"/showIncludes:user",
-		"-imsvc..\\..\\third_party\\depot_tools\\win_toolchain\\vs_files",
-		"-DUSE_AURA=1",
-		"-I../..",
-		"-fcolor-diagnostics",
-		"-fmerge-all-constants",
-		// POSIX-style paths are sometimes passed to clang-cl in Chromium, but they are relative paths.
-		"-fcrash-diagnostics-dir=../../tools/clang/crashreports",
-		"-Xclang",
-		"-fdebug-compilation-dir",
-		"-Xclang",
-		"./debug_compilation_dir",
-		"-no-canonical-prefixes",
-		"-I../../buildtools/third_party/libc++/trunk/include",
-		"/c",
-		"../../base/win/com_init_util.cc",
-		"/Foobj/base/base/com_init_util.obj",
-		"/Fdobj/base/base_cc.pdb",
-	}
-
-	modifyArgs := func(args []string, prefix, replace string) []string {
-		var ret []string
-		found := false
-		for _, arg := range args {
-			if strings.HasPrefix(arg, prefix) {
-				ret = append(ret, replace)
-				found = true
-				continue
-			}
-			ret = append(ret, arg)
-		}
-		if !found {
-			ret = append(ret, replace)
-		}
-		return ret
-	}
-
-	for _, tc := range []struct {
-		desc        string
-		args        []string
-		envs        []string
-		relocatable bool
-		unknownFlag bool
-	}{
-		{
-			desc: "chromium base release",
-			args: baseReleaseArgs,
-			envs: []string{
-				"PWD=C:\\b\\c\\src\\out\\Release",
-			},
-			relocatable: true,
-		},
-		{
-			desc: "clang path absolute",
-			args: modifyArgs(baseReleaseArgs,
-				"..\\..\\third_party\\llvm-build\\Release+Asserts\\bin\\clang-cl.exe",
-				"C:\\chromium\\src\\third_party\\llvm-build\\Release+Asserts\\bin\\clang-cl.exe"),
-			relocatable: false,
-		},
-		{
-			desc: "msvc path absolute",
-			args: modifyArgs(baseReleaseArgs,
-				"-imsvc..\\..\\third_party\\depot_tools\\win_toolchain\\vs_files",
-				"-imsvcC:\\chromium\\src\\third_party\\depot_tools\\win_toolchain\\vs_files"),
-			relocatable: false,
-		},
-		{
-			desc: "include path absolute",
-			args: modifyArgs(baseReleaseArgs,
-				"-I../../buildtools/third_party/libc++/trunk/include",
-				"-IC:\\chromium\\src\\buildtools\\third_party\\libc++\\trunk\\include"),
-			relocatable: false,
-		},
-		{
-			desc: "-fcrash-diagnostics-dir path absolute",
-			args: modifyArgs(baseReleaseArgs,
-				"-fcrash-diagnostics-dir=../../tools/clang/crashreports",
-				"-fcrash-diagnostics-dir=C:\\chromium\\src\\tools\\clang\\crashreports"),
-			relocatable: false,
-		},
-		{
-			desc: "output path absolute",
-			args: modifyArgs(baseReleaseArgs,
-				"/Foobj/base/base/com_init_util.obj",
-				"/FoC:\\chromium\\src\\obj\\base\\base\\com_init_util.obj"),
-			relocatable: false,
-		},
-		{
-			desc: "debug output path absolute",
-			args: modifyArgs(baseReleaseArgs,
-				"/Fdobj/base/base_cc.pdb",
-				"/FdC:\\chromium\\src\\obj\\base\\base_cc.pdb"),
-			relocatable: false,
-		},
-		{
-			desc: "source path absolute",
-			args: modifyArgs(baseReleaseArgs,
-				"../../base/win/com_init_util.cc",
-				"C:\\chromium\\src\\base\\win\\com_init_util.cc"),
-			relocatable: false,
-		},
-		{
-			desc: "-fdebug-compilation-dir",
-			args: modifyArgs(baseReleaseArgs,
-				"./debug_compilation_dir",
-				"C:\\chromium\\src\\out\\Release\\debug_compilation_dir"),
-			relocatable: false,
-		},
-		{
-			desc: "-fdebug-compilation-dir= unrelocatable",
-			args: append(append([]string{},
-				baseReleaseArgs...),
-				"-fdebug-compilation-dir=C:\\chromium\\src\\out\\Release\\debug_compilation_dir"),
-			relocatable: false,
-		},
-		{
-			desc: "-fdebug-compilation-dir= relocatable",
-			args: append(append([]string{},
-				baseReleaseArgs...),
-				"-fdebug-compilation-dir=.\\debug_compilation_dir"),
-			relocatable: true,
-		},
-		{
-			desc: "-fcoverage-compilation-dir= unrelocatable",
-			args: append(append([]string{},
-				baseReleaseArgs...),
-				"-fcoverage-compilation-dir=C:\\chromium\\src\\out\\Release\\coverage_compilation_dir"),
-			relocatable: false,
-		},
-		{
-			desc: "-fcoverage-compilation-dir= relocatable",
-			args: append(append([]string{},
-				baseReleaseArgs...),
-				"-fcoverage-compilation-dir=.\\out\\Release\\coverage_compilation_dir"),
-			relocatable: true,
-		},
-		{
-			desc: "-ffile-compilation-dir= unrelocatable",
-			args: append(append([]string{},
-				baseReleaseArgs...),
-				"-ffile-compilation-dir=C:\\chromium\\src\\out\\Release\\file-compilation-dir"),
-			relocatable: false,
-		},
-		{
-			desc: "-ffile-compilation-dir= relocatable",
-			args: append(append([]string{},
-				baseReleaseArgs...),
-				"-ffile-compilation-dir=.\\out\\Release\\file-compilation-dir"),
-			relocatable: true,
-		},
-		{
-			desc: "-fprofile-list= relocatable",
-			args: append(append([]string{},
-				baseReleaseArgs...),
-				"-fprofile-list=C:\\chromium\\src\\out\\Release\\profile-list"),
-			relocatable: false,
-		},
-		{
-			desc: "-fprofile-list= relocatable",
-			args: append(append([]string{},
-				baseReleaseArgs...),
-				"-fprofile-list=.\\out\\Release\\profile-list"),
-			relocatable: true,
-		},
-		{
-			desc:        "invalid msvc flag",
-			args:        modifyArgs(baseReleaseArgs, "", "/invalid"),
-			relocatable: false,
-			unknownFlag: true,
-		},
-		{
-			desc:        "invalid dash flag",
-			args:        modifyArgs(baseReleaseArgs, "", "-invalid"),
-			relocatable: false,
-			unknownFlag: true,
-		},
-		{
-			desc:        "non-scoped showIncludes",
-			args:        modifyArgs(baseReleaseArgs, "/showIncludes:user", "/showIncludes"),
-			relocatable: true,
-			unknownFlag: false,
-		},
-		{
-			desc:        "full chromium release build args",
-			args:        fullChromiumReleaseBuildArgs,
-			relocatable: true,
-		},
-		{
-			desc:        "full chromium debug build args",
-			args:        fullChromiumDebugBuildArgs,
-			relocatable: true,
-		},
-		{
-			desc:        "full llvm release build args",
-			args:        fullLLVMReleaseBuildArgs,
-			relocatable: false,
-		},
-		{
-			desc: "-Xclang -fno-experimental-new-pass-manager",
-			args: append(append([]string{}, baseReleaseArgs...),
-				"-Xclang", "-fno-experimental-new-pass-manager"),
-			relocatable: true,
-		},
-		{
-			desc: "-Xclang plugin-args",
-			args: append(append([]string{}, baseReleaseArgs...),
-				"-Xclang", "-add-plugin",
-				"-Xclang", "find-bad-constructs",
-				"-Xclang", "-plugin-arg-find-bad-constructs",
-				"-Xclang", "checked-ptr-as-trivial-member"),
-			relocatable: true,
-		},
-		{
-			desc: "-Xclang -no-opaque-pointers",
-			args: append(append([]string{}, baseReleaseArgs...),
-				"-Xclang", "-no-opaque-pointers"),
-			relocatable: true,
-		},
-		{
-			desc: "-mllvm -instcombine-lower-dbg-declare=0",
-			args: append(append([]string{}, baseReleaseArgs...),
-				"-mllvm", "-instcombine-lower-dbg-declare=0"),
-			relocatable: true,
-		},
-		{
-			desc: "-mllvm -basic-aa-recphi=0",
-			args: append(append([]string{}, baseReleaseArgs...),
-				"-mllvm", "-basic-aa-recphi=0"),
-			relocatable: true,
-		},
-		{
-			desc: "/FIcompat/msvcrt/snprintf.h",
-			args: append(append([]string{}, baseReleaseArgs...),
-				"/FIcompat/msvcrt/snprintf.h"),
-			relocatable: true,
-		},
-		{
-			desc: `/FIC:\winsdk\compat\msvcrt\snprintf.h`,
-			args: append(append([]string{}, baseReleaseArgs...),
-				`/FIC:\winsdk\compat\msvcrt\snprintf.h`),
-			relocatable: false,
-		},
-		{
-			desc: "/FC",
-			args: append(append([]string{}, baseReleaseArgs...),
-				"/FC"),
-			relocatable: false,
-		},
-		{
-			desc: "/winsysroot relative",
-			args: append(append([]string{}, baseReleaseArgs...),
-				`/winsysroot..\..\third_party\depot_tools\win_toolchain\vs_files\20d5f253f`),
-			relocatable: true,
-		},
-		{
-			desc: "/winsysroot absolute",
-			args: append(append([]string{}, baseReleaseArgs...),
-				`/winsysrootC:\src\chromium\src\third_party\depot_tools\win_toolchain\vs_files\20d5f253f`),
-			relocatable: false,
-		},
-		{
-			desc: "/vctoolsdir relative",
-			args: append(append([]string{}, baseReleaseArgs...),
-				`/vctoolsdir..\..\third_party\depot_tools\win_toolchain\vs_files\20d5f253f\VC\Tools`),
-			relocatable: true,
-		},
-		{
-			desc: "/vctoolsdir absolute",
-			args: append(append([]string{}, baseReleaseArgs...),
-				`/vctoolsdirC:\src\chromium\src\third_party\depot_tools\win_toolchain\vs_files\20d5f253f\VC\Tools`),
-			relocatable: false,
-		},
-		{
-			desc: "/winsdkdir relative",
-			args: append(append([]string{}, baseReleaseArgs...),
-				`/winsdkdir..\..\third_party\depot_tools\win_toolchain\vs_files\20d5f253f\win_sdk`),
-			relocatable: true,
-		},
-		{
-			desc: "/winsdkdir absolute",
-			args: append(append([]string{}, baseReleaseArgs...),
-				`/winsdkdirC:\src\chromium\src\third_party\depot_tools\win_toolchain\vs_files\20d5f253f\win_sdk`),
-			relocatable: false,
-		},
-		{
-			desc: "/std:c++17",
-			args: append(append([]string{}, baseReleaseArgs...),
-				"/std:c++17"),
-			relocatable: true,
-		},
-		{
-			desc: "/std:c11",
-			args: append(append([]string{}, baseReleaseArgs...),
-				"/std:c11"),
-			relocatable: true,
-		},
-	} {
-		t.Run(tc.desc, func(t *testing.T) {
-			err := clangclRelocatableReq(winpath.FilePath{}, tc.args, tc.envs)
-			if (err == nil) != tc.relocatable {
-				t.Errorf("clangclRelocatableReq(winpath.FilePath, args, envs)=%v; relocatable=%t", err, tc.relocatable)
-			}
-			if err != nil && tc.unknownFlag != strings.Contains(err.Error(), "unknown flag") {
-				t.Errorf("clangclRelocatableReq(winpath.FilePath, args, envs)=%v; expected unknown flag", err)
-			}
-		})
-	}
-}
-
-func TestClangclOutputs(t *testing.T) {
-	for _, tc := range []struct {
-		desc string
-		args []string
-		want []string
-	}{
-		{
-			desc: "basic",
-			args: []string{
-				"clang-cl", "/c", "A/test.c",
-				"/I", "A/B/C",
-				"/ID/E/F",
-				"/o", "A/test.o",
-			},
-			want: []string{"A/test.o"},
-		},
-		{
-			desc: "basic /Fo /Fd",
-			args: []string{
-				"clang-cl", "/c", "A/test.c",
-				"/I", "A/B/C",
-				"/ID/E/F",
-				"/FoA/test.o",
-				"/FdA/test.pdb",
-			},
-			// TODO: capture pdb.
-			want: []string{"A/test.o"},
-		},
-		{
-			desc: "basic dash",
-			args: []string{
-				"clang-cl", "-c", "A/test.c",
-				"-I", "A/B/C",
-				"-ID/E/F",
-				"-o", "A/test.o",
-			},
-			want: []string{"A/test.o"},
-		},
-	} {
-		t.Run(tc.desc, func(t *testing.T) {
-			if got := clangclOutputs(tc.args); !reflect.DeepEqual(got, tc.want) {
-				t.Errorf("clangclOutputs(%q)=%q; want %q", tc.args, got, tc.want)
-			}
-		})
-	}
-}
-
-var fullChromiumReleaseBuildArgs = []string{
-	"..\\..\\third_party\\llvm-build\\Release+Asserts\\bin\\clang-cl.exe",
-	"/nologo",
-	"/showIncludes:user",
-	"-imsvc..\\..\\third_party\\depot_tools\\win_toolchain\\vs_files\\9ff60e43ba91947baca460d0ca3b1b980c3a2c23\\win_sdk",
-	"-DUSE_AURA=1",
-	"-DCR_CLANG_REVISION=\"n353803-99ac9ce7-1\"",
-	"-D_HAS_NODISCARD",
-	"-D_LIBCPP_ABI_UNSTABLE",
-	"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
-	"-D_LIBCPP_ENABLE_NODISCARD",
-	"-D_LIBCPP_NO_AUTO_LINK",
-	"-D__STD_C",
-	"-D_CRT_RAND_S",
-	"-D_CRT_SECURE_NO_DEPRECATE",
-	"-D_SCL_SECURE_NO_DEPRECATE",
-	"-D_ATL_NO_OPENGL",
-	"-D_WINDOWS",
-	"-DCERT_CHAIN_PARA_HAS_EXTRA_FIELDS",
-	"-DPSAPI_VERSION=2",
-	"-DWIN32",
-	"-D_SECURE_ATL",
-	"-DWINAPI_FAMILY=WINAPI_FAMILY_DESKTOP_APP",
-	"-DWIN32_LEAN_AND_MEAN",
-	"-DNOMINMAX",
-	"-D_UNICODE",
-	"-DUNICODE",
-	"-DNTDDI_VERSION=NTDDI_WIN10_RS2",
-	"-D_WIN32_WINNT=0x0A00",
-	"-DWINVER=0x0A00",
-	"-DNDEBUG",
-	"-DNVALGRIND",
-	"-DDYNAMIC_ANNOTATIONS_ENABLED=0",
-	"-DBASE_IMPLEMENTATION",
-	"-I../..",
-	"-Igen",
-	"-I../../third_party/boringssl/src/include",
-	"-fcolor-diagnostics",
-	"-fmerge-all-constants",
-	"-fcrash-diagnostics-dir=../../tools/clang/crashreports",
-	"-Xclang",
-	"-mllvm",
-	"-Xclang",
-	"-instcombine-lower-dbg-declare=0",
-	"-fcomplete-member-pointers",
-	"/Gy",
-	"/FS",
-	"/bigobj",
-	"/utf-8",
-	"/Zc:twoPhase",
-	"/Zc:sizedDealloc-",
-	"/X",
-	"-fmsc-version=1916",
-	"/guard:cf,nochecks",
-	"-m64",
-	"/Brepro",
-	"-Wno-builtin-macro-redefined",
-	"-D__DATE__=",
-	"-D__TIME__=",
-	"-D__TIMESTAMP__=",
-	"-Xclang",
-	"-fdebug-compilation-dir",
-	"-Xclang",
-	".",
-	"-no-canonical-prefixes",
-	"/W4",
-	"-Wimplicit-fallthrough",
-	"-Wunreachable-code",
-	"-Wthread-safety",
-	"-Wextra-semi",
-	"/WX",
-	"/wd4091",
-	"/wd4127",
-	"/wd4251",
-	"/wd4275",
-	"/wd4312",
-	"/wd4324",
-	"/wd4351",
-	"/wd4355",
-	"/wd4503",
-	"/wd4589",
-	"/wd4611",
-	"/wd4100",
-	"/wd4121",
-	"/wd4244",
-	"/wd4505",
-	"/wd4510",
-	"/wd4512",
-	"/wd4610",
-	"/wd4838",
-	"/wd4995",
-	"/wd4996",
-	"/wd4456",
-	"/wd4457",
-	"/wd4458",
-	"/wd4459",
-	"/wd4200",
-	"/wd4201",
-	"/wd4204",
-	"/wd4221",
-	"/wd4245",
-	"/wd4267",
-	"/wd4305",
-	"/wd4389",
-	"/wd4702",
-	"/wd4701",
-	"/wd4703",
-	"/wd4661",
-	"/wd4706",
-	"/wd4715",
-	"-Wno-missing-field-initializers",
-	"-Wno-unused-parameter",
-	"-Wno-c++11-narrowing",
-	"-Wno-unneeded-internal-declaration",
-	"-Wno-undefined-var-template",
-	"-Wno-nonportable-include-path",
-	"-Wno-ignored-pragma-optimize",
-	"-Wno-implicit-int-float-conversion",
-	"-Wno-final-dtor-non-final-class",
-	"-Wno-builtin-assume-aligned-alignment",
-	"-Wno-deprecated-copy",
-	"-Wno-non-c-typedef-for-linkage",
-	"-Wmax-tokens",
-	"/Z7",
-	"-gcodeview-ghash",
-	"-Xclang",
-	"-debug-info-kind=constructor",
-	"-ftrivial-auto-var-init=pattern",
-	"/MT",
-	"-Xclang",
-	"-add-plugin",
-	"-Xclang",
-	"find-bad-constructs",
-	"-Wheader-hygiene",
-	"-Wstring-conversion",
-	"-Wtautological-overlap-compare",
-	"-Wglobal-constructors",
-	"-Wexit-time-destructors",
-	"-Wshadow",
-	"-Wno-shorten-64-to-32",
-	"-Wexit-time-destructors",
-	"/O2",
-	"/Ob2",
-	"/Oy-",
-	"/Zc:inline",
-	"/Gw",
-	"/TP",
-	"/wd4577",
-	"/GR-",
-	"-I../../buildtools/third_party/libc++/trunk/include",
-	"/c",
-	"../../base/win/com_init_util.cc",
-	"/Foobj/base/base/com_init_util.obj",
-	"/Fdobj/base/base_cc.pdb",
-}
-
-var fullChromiumDebugBuildArgs = []string{
-	"..\\..\\third_party\\llvm-build\\Release+Asserts\\bin\\clang-cl.exe",
-	"/nologo",
-	"/showIncludes:user",
-	"-imsvc..\\..\\third_party\\depot_tools\\win_toolchain\\vs_files\\9ff60e43ba91947baca460d0ca3b1b980c3a2c23\\win_sdk",
-	"-DUSE_AURA=1",
-	"-DCR_CLANG_REVISION=\"n353803-99ac9ce7-1\"",
-	"-D_HAS_NODISCARD",
-	"-DCOMPONENT_BUILD",
-	"-D_LIBCPP_ABI_UNSTABLE",
-	"-D_LIBCPP_ENABLE_NODISCARD",
-	"-D_LIBCPP_NO_AUTO_LINK",
-	"-D__STD_C",
-	"-D_CRT_RAND_S",
-	"-D_CRT_SECURE_NO_DEPRECATE",
-	"-D_SCL_SECURE_NO_DEPRECATE",
-	"-D_ATL_NO_OPENGL",
-	"-D_WINDOWS",
-	"-DCERT_CHAIN_PARA_HAS_EXTRA_FIELDS",
-	"-DPSAPI_VERSION=2",
-	"-DWIN32",
-	"-D_SECURE_ATL",
-	"-DWINAPI_FAMILY=WINAPI_FAMILY_DESKTOP_APP",
-	"-DWIN32_LEAN_AND_MEAN",
-	"-DNOMINMAX",
-	"-D_UNICODE",
-	"-DUNICODE",
-	"-DNTDDI_VERSION=NTDDI_WIN10_RS2",
-	"-D_WIN32_WINNT=0x0A00",
-	"-DWINVER=0x0A00",
-	"-D_DEBUG",
-	"-DDYNAMIC_ANNOTATIONS_ENABLED=1",
-	"-I../..",
-	"-Igen",
-	"-fcolor-diagnostics",
-	"-fmerge-all-constants",
-	"-fcrash-diagnostics-dir=../../tools/clang/crashreports",
-	"-Xclang",
-	"-mllvm",
-	"-Xclang",
-	"-instcombine-lower-dbg-declare=0",
-	"-fcomplete-member-pointers",
-	"/Gy",
-	"/FS",
-	"/bigobj",
-	"/utf-8",
-	"/Zc:twoPhase",
-	"/Zc:sizedDealloc-",
-	"/X",
-	"-fmsc-version=1916",
-	"/guard:cf,nochecks",
-	"/Zc:dllexportInlines-",
-	"-m64",
-	"/Brepro",
-	"-Wno-builtin-macro-redefined",
-	"-D__DATE__=",
-	"-D__TIME__=",
-	"-D__TIMESTAMP__=",
-	"-Xclang",
-	"-fdebug-compilation-dir",
-	"-Xclang",
-	".",
-	"-no-canonical-prefixes",
-	"/W4",
-	"-Wimplicit-fallthrough",
-	"-Wunreachable-code",
-	"-Wthread-safety",
-	"-Wextra-semi",
-	"/WX",
-	"/wd4091",
-	"/wd4127",
-	"/wd4251",
-	"/wd4275",
-	"/wd4312",
-	"/wd4324",
-	"/wd4351",
-	"/wd4355",
-	"/wd4503",
-	"/wd4589",
-	"/wd4611",
-	"/wd4100",
-	"/wd4121",
-	"/wd4244",
-	"/wd4505",
-	"/wd4510",
-	"/wd4512",
-	"/wd4610",
-	"/wd4838",
-	"/wd4995",
-	"/wd4996",
-	"/wd4456",
-	"/wd4457",
-	"/wd4458",
-	"/wd4459",
-	"/wd4200",
-	"/wd4201",
-	"/wd4204",
-	"/wd4221",
-	"/wd4245",
-	"/wd4267",
-	"/wd4305",
-	"/wd4389",
-	"/wd4702",
-	"/wd4701",
-	"/wd4703",
-	"/wd4661",
-	"/wd4706",
-	"/wd4715",
-	"-Wno-missing-field-initializers",
-	"-Wno-unused-parameter",
-	"-Wno-c++11-narrowing",
-	"-Wno-unneeded-internal-declaration",
-	"-Wno-undefined-var-template",
-	"-Wno-nonportable-include-path",
-	"-Wno-ignored-pragma-optimize",
-	"-Wno-implicit-int-float-conversion",
-	"-Wno-final-dtor-non-final-class",
-	"-Wno-builtin-assume-aligned-alignment",
-	"-Wno-deprecated-copy",
-	"-Wno-non-c-typedef-for-linkage",
-	"-Wmax-tokens",
-	"/Od",
-	"/Ob0",
-	"/GF",
-	"/Z7",
-	"-gcodeview-ghash",
-	"-Xclang",
-	"-debug-info-kind=constructor",
-	"-ftrivial-auto-var-init=pattern",
-	"/MDd",
-	"-Xclang",
-	"-add-plugin",
-	"-Xclang",
-	"find-bad-constructs",
-	"-Wheader-hygiene",
-	"-Wstring-conversion",
-	"-Wtautological-overlap-compare",
-	"-Wno-unused-const-variable",
-	"-Wno-unused-function",
-	"-Wno-undefined-bool-conversion",
-	"-Wno-tautological-undefined-compare",
-	"/TP",
-	"/wd4577",
-	"/GR-",
-	"-I../../buildtools/third_party/libc++/trunk/include",
-	"/c",
-	"../../base/third_party/double_conversion/double-conversion/fast-dtoa.cc",
-	"/Foobj/base/third_party/double_conversion/double_conversion/fast-dtoa.obj",
-	"/Fdobj/base/third_party/double_conversion/double_conversion_cc.pdb",
-}
-
-var fullLLVMReleaseBuildArgs = []string{
-	"C:\\b\\s\\w\\ir\\cache\\builder\\v8\\third_party\\llvm-build\\Release+Asserts\\bin\\clang-cl.exe",
-	"/nologo",
-	"-TP",
-	"-DGTEST_HAS_RTTI=0",
-	"-DUNICODE",
-	"-D_CRT_NONSTDC_NO_DEPRECATE",
-	"-D_CRT_NONSTDC_NO_WARNINGS",
-	"-D_CRT_SECURE_NO_DEPRECATE",
-	"-D_CRT_SECURE_NO_WARNINGS",
-	"-D_HAS_EXCEPTIONS=0",
-	"-D_SCL_SECURE_NO_DEPRECATE",
-	"-D_SCL_SECURE_NO_WARNINGS",
-	"-D_UNICODE",
-	"-D__STDC_CONSTANT_MACROS",
-	"-D__STDC_FORMAT_MACROS",
-	"-D__STDC_LIMIT_MACROS",
-	"-Itools\\opt",
-	"-IC:\\b\\s\\w\\ir\\cache\\builder\\emscripten-releases\\llvm-project\\llvm\\tools\\opt",
-	"-Iinclude",
-	"-IC:\\b\\s\\w\\ir\\cache\\builder\\emscripten-releases\\llvm-project\\llvm\\include",
-	"-Wno-nonportable-include-path",
-	"/Zc:inline",
-	"/Zc:strictStrings",
-	"/Oi",
-	"/Zc:rvalueCast",
-	"/Brepro",
-	"/W4",
-	"-Wextra",
-	"-Wno-unused-parameter",
-	"-Wwrite-strings",
-	"-Wcast-qual",
-	"-Wmissing-field-initializers",
-	"-Wimplicit-fallthrough",
-	"-Wcovered-switch-default",
-	"-Wno-noexcept-type",
-	"-Wdelete-non-virtual-dtor",
-	"-Wstring-conversion",
-	"/Gw",
-	"/MD",
-	"/O2",
-	"/Ob2",
-	"/EHs-c-",
-	"/GR-",
-	"-UNDEBUG",
-	"-std:c++14",
-	"/showIncludes",
-	"/Fotools\\opt\\CMakeFiles\\opt.dir\\PrintSCC.cpp.obj",
-	"/Fdtools\\opt\\CMakeFiles\\opt.dir\\",
-	"-c",
-	"C:\\b\\s\\w\\ir\\cache\\builder\\emscripten-releases\\llvm-project\\llvm\\tools\\opt\\PrintSCC.cpp",
-}
diff --git a/remoteexec/client.go b/remoteexec/client.go
deleted file mode 100644
index f0a4335..0000000
--- a/remoteexec/client.go
+++ /dev/null
@@ -1,293 +0,0 @@
-// Copyright 2018 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.
-
-package remoteexec
-
-import (
-	"context"
-	"time"
-
-	rpb "github.com/bazelbuild/remote-apis/build/bazel/remote/execution/v2"
-
-	bpb "google.golang.org/genproto/googleapis/bytestream"
-	lpb "google.golang.org/genproto/googleapis/longrunning"
-	spb "google.golang.org/genproto/googleapis/rpc/status"
-	"google.golang.org/grpc"
-	"google.golang.org/grpc/codes"
-	"google.golang.org/grpc/status"
-	"google.golang.org/protobuf/proto"
-
-	"go.chromium.org/goma/server/log"
-	"go.chromium.org/goma/server/rpc"
-)
-
-// Client is a remoteexec API client to ClientConn.
-// CallOptions will be added when calling RPC.
-//
-//	prcred, _ := oauth.NewApplicationDefault(ctx,
-//	   "https://www.googleapis.com/auth/cloud-build-service")
-//	conn, _ := grpc.DialContext(ctx, target,
-//	  grpc.WithPerRPCCredentials(prcred),
-//	  grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{})))
-//	client := &remoteexec.Client{conn}
-type Client struct {
-	*grpc.ClientConn
-	CallOptions []grpc.CallOption
-	Retry       rpc.Retry
-}
-
-func (c Client) callOptions(opts ...grpc.CallOption) []grpc.CallOption {
-	return append(append([]grpc.CallOption(nil), opts...), c.CallOptions...)
-}
-
-// Cache returns action cache client.
-// https://github.com/bazelbuild/remote-apis/blob/c1c1ad2c97ed18943adb55f06657440daa60d833/build/bazel/remote/execution/v2/remote_execution.proto#L117
-func (c Client) Cache() rpb.ActionCacheClient {
-	return c
-}
-
-// GetActionResult retrieves a cached execution result.
-func (c Client) GetActionResult(ctx context.Context, req *rpb.GetActionResultRequest, opts ...grpc.CallOption) (*rpb.ActionResult, error) {
-	return rpb.NewActionCacheClient(c.ClientConn).GetActionResult(ctx, req, c.callOptions(opts...)...)
-}
-
-// UpdateActionResult uploads a new execution result.
-func (c Client) UpdateActionResult(ctx context.Context, req *rpb.UpdateActionResultRequest, opts ...grpc.CallOption) (*rpb.ActionResult, error) {
-	return rpb.NewActionCacheClient(c.ClientConn).UpdateActionResult(ctx, req, c.callOptions(opts...)...)
-}
-
-// Exec returns execution client.
-// https://github.com/bazelbuild/remote-apis/blob/c1c1ad2c97ed18943adb55f06657440daa60d833/build/bazel/remote/execution/v2/remote_execution.proto#L34
-func (c Client) Exec() rpb.ExecutionClient {
-	return c
-}
-
-// Execute executes an action remotely.
-func (c Client) Execute(ctx context.Context, req *rpb.ExecuteRequest, opts ...grpc.CallOption) (rpb.Execution_ExecuteClient, error) {
-	return rpb.NewExecutionClient(c.ClientConn).Execute(ctx, req, c.callOptions(opts...)...)
-}
-
-// WaitExecution waits for an execution operation to complete.
-func (c Client) WaitExecution(ctx context.Context, req *rpb.WaitExecutionRequest, opts ...grpc.CallOption) (rpb.Execution_WaitExecutionClient, error) {
-	return rpb.NewExecutionClient(c.ClientConn).WaitExecution(ctx, req, c.callOptions(opts...)...)
-}
-
-// CAS returns content addressable storage client.
-// https://github.com/bazelbuild/remote-apis/blob/c1c1ad2c97ed18943adb55f06657440daa60d833/build/bazel/remote/execution/v2/remote_execution.proto#L168
-func (c Client) CAS() rpb.ContentAddressableStorageClient {
-	return c
-}
-
-// FindMissingBlobs determines if blobs are present in the CAS.
-func (c Client) FindMissingBlobs(ctx context.Context, req *rpb.FindMissingBlobsRequest, opts ...grpc.CallOption) (*rpb.FindMissingBlobsResponse, error) {
-	return rpb.NewContentAddressableStorageClient(c.ClientConn).FindMissingBlobs(ctx, req, c.callOptions(opts...)...)
-}
-
-// BatchUpdateBlobs uploads many blobs at once.
-func (c Client) BatchUpdateBlobs(ctx context.Context, req *rpb.BatchUpdateBlobsRequest, opts ...grpc.CallOption) (*rpb.BatchUpdateBlobsResponse, error) {
-	return rpb.NewContentAddressableStorageClient(c.ClientConn).BatchUpdateBlobs(ctx, req, c.callOptions(opts...)...)
-}
-
-// BatchReadBlobs downloads many blobs at once.
-func (c Client) BatchReadBlobs(ctx context.Context, req *rpb.BatchReadBlobsRequest, opts ...grpc.CallOption) (*rpb.BatchReadBlobsResponse, error) {
-	return rpb.NewContentAddressableStorageClient(c.ClientConn).BatchReadBlobs(ctx, req, c.callOptions(opts...)...)
-}
-
-// GetTree fetches the entire directory tree rooted at a node.
-func (c Client) GetTree(ctx context.Context, req *rpb.GetTreeRequest, opts ...grpc.CallOption) (rpb.ContentAddressableStorage_GetTreeClient, error) {
-	return rpb.NewContentAddressableStorageClient(c.ClientConn).GetTree(ctx, req, c.callOptions(opts...)...)
-}
-
-// ByteStream returns byte stream client.
-// https://godoc.org/google.golang.org/genproto/googleapis/bytestream#ByteStreamClient
-func (c Client) ByteStream() bpb.ByteStreamClient {
-	return c
-}
-
-// Read is used to retrieve the contents of a resource as a sequence of bytes.
-func (c Client) Read(ctx context.Context, in *bpb.ReadRequest, opts ...grpc.CallOption) (bpb.ByteStream_ReadClient, error) {
-	return bpb.NewByteStreamClient(c.ClientConn).Read(ctx, in, c.callOptions(opts...)...)
-}
-
-// Write is used to send the contents of a resource as a sequence of bytes.
-func (c Client) Write(ctx context.Context, opts ...grpc.CallOption) (bpb.ByteStream_WriteClient, error) {
-	return bpb.NewByteStreamClient(c.ClientConn).Write(ctx, c.callOptions(opts...)...)
-}
-
-// QueryWriteStatus is used to find the committed_size for a resource
-// that is being written, which can be then be used as the write_offset
-// for the next Write call.
-func (c Client) QueryWriteStatus(ctx context.Context, in *bpb.QueryWriteStatusRequest, opts ...grpc.CallOption) (*bpb.QueryWriteStatusResponse, error) {
-	return bpb.NewByteStreamClient(c.ClientConn).QueryWriteStatus(ctx, in, c.callOptions(opts...)...)
-}
-
-// Capabilities returns capabilities client.
-func (c Client) Capabilities() rpb.CapabilitiesClient {
-	return c
-}
-
-// GetCapabilities returns the server capabilities configuration.
-func (c Client) GetCapabilities(ctx context.Context, req *rpb.GetCapabilitiesRequest, opts ...grpc.CallOption) (*rpb.ServerCapabilities, error) {
-	return rpb.NewCapabilitiesClient(c.ClientConn).GetCapabilities(ctx, req, c.callOptions(opts...)...)
-}
-
-func logOpMetadata(logger log.Logger, op *lpb.Operation) {
-	if op.GetMetadata() == nil {
-		logger.Infof("operation update: no metadata")
-		return
-	}
-	md := &rpb.ExecuteOperationMetadata{}
-	err := op.GetMetadata().UnmarshalTo(md)
-	if err != nil {
-		logger.Warnf("operation update: %s: metadata bad type %T: %v", op.GetName(), op.GetMetadata(), err)
-		return
-	}
-	logger.Infof("operation update: %s: %v", op.GetName(), md)
-}
-
-// ExecuteAndWait executes and action remotely and wait its response.
-// it returns operation name, response and error.
-func ExecuteAndWait(ctx context.Context, c Client, req *rpb.ExecuteRequest, opts ...grpc.CallOption) (string, *rpb.ExecuteResponse, error) {
-	logger := log.FromContext(ctx)
-	logger.Infof("execute action")
-
-	var opName string
-	var waitReq *rpb.WaitExecutionRequest
-	resp := &rpb.ExecuteResponse{}
-	type responseStream interface {
-		Recv() (*lpb.Operation, error)
-	}
-	pctx := ctx
-	err := c.Retry.Do(ctx, func() error {
-		ctx, cancel := context.WithTimeout(pctx, 1*time.Minute)
-		defer cancel()
-		var stream responseStream
-		var err error
-		if waitReq != nil {
-			stream, err = c.Exec().WaitExecution(ctx, waitReq, opts...)
-		} else {
-			recordRemoteExecStart(ctx)
-			stream, err = c.Exec().Execute(ctx, req, opts...)
-		}
-		if err != nil {
-			return grpc.Errorf(grpc.Code(err), "execute: %v", err)
-		}
-		for {
-			op, err := stream.Recv()
-			if err != nil {
-				// if not found, retry from execute
-				// otherwise, rerun from WaitExecution.
-				if status.Code(err) == codes.NotFound {
-					waitReq = nil
-					recordRemoteExecFinish(ctx)
-					return status.Errorf(codes.Unavailable, "operation stream lost: %v", err)
-				}
-				// record error details if any
-				// https://github.com/bazelbuild/remote-apis/blob/1aeb399731780e61dece542dc656da1775a20840/build/bazel/remote/execution/v2/remote_execution.proto#L102
-				st, ok := status.FromError(err)
-				if ok {
-					for _, dt := range st.Details() {
-						logger.Warnf("error details for %v: %v", err, dt)
-					}
-				}
-				return err
-			}
-			if opName == "" {
-				opName = op.GetName()
-				logger.Infof("operation starts: %s", opName)
-			}
-			if !op.GetDone() {
-				logOpMetadata(logger, op)
-				waitReq = &rpb.WaitExecutionRequest{
-					Name: opName,
-				}
-				continue
-			}
-			waitReq = nil
-			err = op.GetResponse().UnmarshalTo(resp)
-			if err != nil {
-				err = status.Errorf(codes.Internal, "op %s response bad type %T: %v", op.GetName(), op.GetResponse(), err)
-				logger.Errorf("%s", err)
-				return err
-			}
-			return erespErr(pctx, resp)
-		}
-	})
-	recordRemoteExecFinish(ctx)
-	if err == nil {
-		err = status.FromProto(resp.GetStatus()).Err()
-	}
-	return opName, resp, err
-}
-
-// erespErr returns codes.Unavailable if it has retriable failure result.
-// returns nil otherwise (to terminates retrying, even if eresp contains
-// error status).
-func erespErr(ctx context.Context, eresp *rpb.ExecuteResponse) error {
-	logger := log.FromContext(ctx)
-	st := eresp.GetStatus()
-	// record details if any
-	// when OK, it has empty google.devtools.remotebuildbot.ComamndStatus
-	// {code=0, message=""}.
-	if codes.Code(st.GetCode()) != codes.OK && len(st.GetDetails()) > 0 {
-		logger.Warnf("error details for %v: %v", codes.Code(st.GetCode()), st.GetDetails())
-	}
-
-	// https://github.com/bazelbuild/remote-apis/blob/e7282cf0f0e16e7ba84209be5417279e6815bee7/build/bazel/remote/execution/v2/remote_execution.proto#L83
-	// FAILED_PRECONDITION:
-	//   one or more errors occured in setting up the action
-	//   requested, such as a missing input or command or
-	//   no worker being available. The client may be able to
-	//   fix the errors and retry.
-	//   exec_server doesn't fix request, so don't retry.
-	// UNAVAILABLE:
-	//   Due to transient condition, such as all workers being
-	//   occupied (and the server does not support a queue), the
-	//   action could not be started. The client should retry.
-	// RESOURCE_EXHAUSTED:
-	//   There is insufficient quota of some resource to run the action.
-	// INTERNAL
-	//   An internal error occurred in the execution engine or the worker.
-	//   could be handled as unavailable error.
-	//
-	// Other error would be non retriable.
-	switch codes.Code(st.GetCode()) {
-	case codes.OK:
-	case codes.ResourceExhausted, codes.FailedPrecondition:
-		logger.Warnf("execute response: status=%s", st)
-		return status.FromProto(st).Err()
-
-	case codes.Internal:
-		logger.Warnf("execute response: status=%s", st)
-		fallthrough
-	case codes.Unavailable:
-		st = proto.Clone(st).(*spb.Status)
-		// codes.Unavailable, so that rpc.Retry will retry.
-		st.Code = int32(codes.Unavailable)
-		return status.FromProto(st).Err()
-
-	case codes.Aborted:
-		if ctx.Err() == nil {
-			// ctx is not cancelled, but returned
-			// code = Aborted, context canceled
-			// in this case, it would be retriable.
-			logger.Warnf("execute reponse: aborted %s, but ctx is still active", st)
-			st = proto.Clone(st).(*spb.Status)
-			// codes.Unavailable, so that rpc.Retry will retry.
-			st.Code = int32(codes.Unavailable)
-			return status.FromProto(st).Err()
-		}
-		fallthrough
-	default:
-		logger.Errorf("execute response: error %s", st)
-	}
-	return nil
-}
-
-func fixRBEInternalError(err error) error {
-	if status.Code(err) == codes.Internal {
-		return status.Errorf(codes.Unavailable, "%v", err)
-	}
-	return err
-}
diff --git a/remoteexec/cmdfile.go b/remoteexec/cmdfile.go
deleted file mode 100644
index 8760248..0000000
--- a/remoteexec/cmdfile.go
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright 2018 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.
-
-package remoteexec
-
-import (
-	"context"
-	"fmt"
-	"io"
-
-	rpb "github.com/bazelbuild/remote-apis/build/bazel/remote/execution/v2"
-
-	pb "go.chromium.org/goma/server/proto/command"
-	"go.chromium.org/goma/server/remoteexec/digest"
-	"go.chromium.org/goma/server/remoteexec/merkletree"
-)
-
-// CmdStorage is an interface to retrieve cmd file contents.
-type CmdStorage interface {
-	Open(ctx context.Context, hash string) (io.ReadCloser, error)
-}
-
-// fileSpecToEntry converts filespec to merkletree entry.
-func fileSpecToEntry(ctx context.Context, fs *pb.FileSpec, cmdStorage CmdStorage) (merkletree.Entry, error) {
-	if fs.HashKey != "" || fs.Blob != nil {
-		return merkletree.Entry{}, fmt.Errorf("%s: fileSpecToEntry used for goma input? %s %s", fs.Path, fs.HashKey, fs.Blob.GetBlobType())
-	}
-	if fs.Hash != "" && fs.Symlink != "" {
-		return merkletree.Entry{}, fmt.Errorf("%s: FileSpec has both `Hash` and `Symlink` fields", fs.Path)
-	}
-	if fs.Hash == "" {
-		if fs.Symlink != "" {
-			// Symlink
-			return merkletree.Entry{
-				Name:   fs.Path,
-				Target: fs.Symlink,
-			}, nil
-		}
-		// dir
-		return merkletree.Entry{
-			Name: fs.Path,
-		}, nil
-	}
-	d := &rpb.Digest{
-		Hash:      fs.Hash,
-		SizeBytes: fs.Size,
-	}
-	src := cmdFileObj{
-		storage: cmdStorage,
-		hash:    fs.Hash,
-	}
-	return merkletree.Entry{
-		Name:         fs.Path,
-		Data:         digest.New(src, d),
-		IsExecutable: fs.IsExecutable,
-	}, nil
-}
-
-type cmdFileObj struct {
-	storage CmdStorage
-	hash    string
-}
-
-func (o cmdFileObj) Open(ctx context.Context) (io.ReadCloser, error) {
-	r, err := o.storage.Open(ctx, o.hash)
-	return r, err
-}
-
-func (o cmdFileObj) String() string {
-	return fmt.Sprintf("cmd-file:%s", o.hash)
-}
diff --git a/remoteexec/cmdfile_test.go b/remoteexec/cmdfile_test.go
deleted file mode 100644
index 15f5a2a..0000000
--- a/remoteexec/cmdfile_test.go
+++ /dev/null
@@ -1,147 +0,0 @@
-// Copyright 2018 Google LLC. All Rights Reserved.
-
-package remoteexec
-
-import (
-	"context"
-	"io"
-	"reflect"
-	"testing"
-
-	rpb "github.com/bazelbuild/remote-apis/build/bazel/remote/execution/v2"
-
-	pb "go.chromium.org/goma/server/proto/command"
-	"go.chromium.org/goma/server/remoteexec/digest"
-	"go.chromium.org/goma/server/remoteexec/merkletree"
-)
-
-type dummyCmdStorage struct {
-}
-
-func (dcs dummyCmdStorage) Open(ctx context.Context, hash string) (io.ReadCloser, error) {
-	return nil, nil
-}
-
-func TestFileSpecToEntry(t *testing.T) {
-	tests := []struct {
-		input     *pb.FileSpec
-		wantEntry merkletree.Entry
-		wantErr   bool
-	}{
-		// Executable files.
-		{
-			&pb.FileSpec{
-				Path:         "../../native_client/toolchain/linux_x86/pnacl_newlib/bin/pnacl-clang++",
-				Hash:         "7bf4c008d0321a9956279edd58fd2078569e9595fbfe5775c228836b36d71796",
-				Size:         1234,
-				IsExecutable: true,
-			},
-			merkletree.Entry{
-				Name:         "../../native_client/toolchain/linux_x86/pnacl_newlib/bin/pnacl-clang++",
-				IsExecutable: true,
-				Data: digest.New(
-					cmdFileObj{
-						hash:    "7bf4c008d0321a9956279edd58fd2078569e9595fbfe5775c228836b36d71796",
-						storage: dummyCmdStorage{},
-					},
-					&rpb.Digest{
-						Hash:      "7bf4c008d0321a9956279edd58fd2078569e9595fbfe5775c228836b36d71796",
-						SizeBytes: 1234,
-					}),
-			}, false,
-		}, {
-			&pb.FileSpec{
-				Path:         "../../native_client/toolchain/linux_x86/pnacl_newlib/bin/clang",
-				Hash:         "42151bf3845e7b44d0339964cdc19ce55427e7eee9e4bf0d306fe313ed8b5db8",
-				Size:         4567,
-				IsExecutable: true,
-			},
-			merkletree.Entry{
-				Name:         "../../native_client/toolchain/linux_x86/pnacl_newlib/bin/clang",
-				IsExecutable: true,
-				Data: digest.New(
-					cmdFileObj{
-						hash:    "42151bf3845e7b44d0339964cdc19ce55427e7eee9e4bf0d306fe313ed8b5db8",
-						storage: dummyCmdStorage{},
-					},
-					&rpb.Digest{
-						Hash:      "42151bf3845e7b44d0339964cdc19ce55427e7eee9e4bf0d306fe313ed8b5db8",
-						SizeBytes: 4567,
-					}),
-			}, false,
-		}, {
-			// Test IsExecutable=false.
-			&pb.FileSpec{
-				Path:         "../../native_client/toolchain/linux_x86/pnacl_newlib/bin/../lib/libc++.so.1.0",
-				Hash:         "7fc88a31bbededbe1f276c23a66797b21cf8d7f837e6580e70b2755a817a08c7",
-				Size:         1111,
-				IsExecutable: false,
-			},
-			merkletree.Entry{
-				Name:         "../../native_client/toolchain/linux_x86/pnacl_newlib/bin/../lib/libc++.so.1.0",
-				IsExecutable: false,
-				Data: digest.New(
-					cmdFileObj{
-						hash:    "7fc88a31bbededbe1f276c23a66797b21cf8d7f837e6580e70b2755a817a08c7",
-						storage: dummyCmdStorage{},
-					},
-					&rpb.Digest{
-						Hash:      "7fc88a31bbededbe1f276c23a66797b21cf8d7f837e6580e70b2755a817a08c7",
-						SizeBytes: 1111,
-					}),
-			}, false,
-		}, {
-			// Invalid entry.
-			&pb.FileSpec{
-				Path:    "../../native_client/toolchain/linux_x86/pnacl_newlib/bin/../lib/libc++.so.1.0",
-				Hash:    "7fc88a31bbededbe1f276c23a66797b21cf8d7f837e6580e70b2755a817a08c7",
-				Symlink: "libc++.so.1.0",
-			},
-			merkletree.Entry{}, true,
-		}, {
-			// Dir.
-			&pb.FileSpec{
-				Path: "../../native_client/toolchain/linux_x86/pnacl_newlib/bin",
-			},
-			merkletree.Entry{
-				Name: "../../native_client/toolchain/linux_x86/pnacl_newlib/bin",
-			}, false,
-		}, {
-			// Symlinks.
-			&pb.FileSpec{
-				Path:    "../../native_client/toolchain/linux_x86/pnacl_newlib/bin/clang++",
-				Symlink: "clang",
-			},
-			merkletree.Entry{
-				Name:   "../../native_client/toolchain/linux_x86/pnacl_newlib/bin/clang++",
-				Target: "clang",
-			}, false,
-		}, {
-			&pb.FileSpec{
-				Path:    "../../native_client/toolchain/linux_x86/pnacl_newlib/bin/../lib/libc++.so",
-				Symlink: "libc++.so.1",
-			},
-			merkletree.Entry{
-				Name:   "../../native_client/toolchain/linux_x86/pnacl_newlib/bin/../lib/libc++.so",
-				Target: "libc++.so.1",
-			}, false,
-		}, {
-			&pb.FileSpec{
-				Path:    "../../native_client/toolchain/linux_x86/pnacl_newlib/bin/clang++",
-				Symlink: "libc++.so.1.0",
-			},
-			merkletree.Entry{
-				Name:   "../../native_client/toolchain/linux_x86/pnacl_newlib/bin/clang++",
-				Target: "libc++.so.1.0",
-			}, false,
-		},
-	}
-
-	for _, test := range tests {
-		entry, err := fileSpecToEntry(context.Background(), test.input, dummyCmdStorage{})
-		if !reflect.DeepEqual(entry, test.wantEntry) || test.wantErr != (err != nil) {
-			t.Errorf("fileSpecToEntry(ctx, %v, dummyCmdStorage)=%v, %v, want=%v, wantErr=%v", test.input,
-				entry, err, test.wantEntry, test.wantErr)
-		}
-	}
-}
diff --git a/remoteexec/datasource/datasource.go b/remoteexec/datasource/datasource.go
deleted file mode 100644
index 3e39f6a..0000000
--- a/remoteexec/datasource/datasource.go
+++ /dev/null
@@ -1,82 +0,0 @@
-/* Copyright 2018 Google Inc. All Rights Reserved. */
-
-// Package datasource provides data source from local file, bytes etc.
-package datasource
-
-import (
-	"bytes"
-	"context"
-	"fmt"
-	"io"
-	"io/ioutil"
-	"os"
-
-	"google.golang.org/protobuf/proto"
-)
-
-// Source accesses data source.
-type Source interface {
-	Open(context.Context) (io.ReadCloser, error)
-	String() string
-}
-
-type localFile struct {
-	fullpath string
-}
-
-// LocalFile creates new source for the local file.
-func LocalFile(fullpath string) Source {
-	return localFile{
-		fullpath: fullpath,
-	}
-}
-
-func (f localFile) String() string {
-	return fmt.Sprintf("localfile:%s", f.fullpath)
-}
-
-func (f localFile) Open(context.Context) (io.ReadCloser, error) {
-	fp, err := os.Open(f.fullpath)
-	return fp, err
-}
-
-type byteData struct {
-	name string
-	data []byte
-}
-
-// Bytes create new source for bytes.
-func Bytes(name string, b []byte) Source {
-	return byteData{
-		name: name,
-		data: b,
-	}
-}
-
-func (b byteData) String() string {
-	return b.name
-}
-
-func (b byteData) Open(context.Context) (io.ReadCloser, error) {
-	r := bytes.NewReader(b.data)
-	return ioutil.NopCloser(r), nil
-}
-
-// ReadAll reads all data from source.
-func ReadAll(ctx context.Context, src Source) ([]byte, error) {
-	f, err := src.Open(ctx)
-	if err != nil {
-		return nil, err
-	}
-	defer f.Close()
-	return ioutil.ReadAll(f)
-}
-
-// ReadProto reads data as proto message.
-func ReadProto(ctx context.Context, src Source, m proto.Message) error {
-	b, err := ReadAll(ctx, src)
-	if err != nil {
-		return err
-	}
-	return proto.Unmarshal(b, m)
-}
diff --git a/remoteexec/digest/digest.go b/remoteexec/digest/digest.go
deleted file mode 100644
index 9004364..0000000
--- a/remoteexec/digest/digest.go
+++ /dev/null
@@ -1,101 +0,0 @@
-/* Copyright 2018 Google Inc. All Rights Reserved. */
-
-// Package digest handles content digest for remote executon API,
-// https://github.com/bazelbuild/remote-apis/blob/c1c1ad2c97ed18943adb55f06657440daa60d833/build/bazel/remote/execution/v2/remote_execution.proto#L633
-package digest
-
-import (
-	"context"
-	"crypto/sha256"
-	"encoding/hex"
-	"fmt"
-	"io"
-
-	rpb "github.com/bazelbuild/remote-apis/build/bazel/remote/execution/v2"
-
-	"google.golang.org/protobuf/proto"
-
-	"go.chromium.org/goma/server/hash"
-	"go.chromium.org/goma/server/remoteexec/datasource"
-)
-
-// Data is data identified by digest.
-type Data interface {
-	Digest() *rpb.Digest
-	Source
-}
-
-// Source accesses data source for the digest.
-type Source interface {
-	Open(context.Context) (io.ReadCloser, error)
-	String() string
-}
-
-type data struct {
-	digest *rpb.Digest
-	source Source
-}
-
-// New creates digest data from source, which digest is d.
-func New(src Source, d *rpb.Digest) Data {
-	return data{
-		digest: d,
-		source: src,
-	}
-}
-
-// Digest returns digest of the data.
-func (d data) Digest() *rpb.Digest {
-	return d.digest
-}
-
-// Open opens the data source.
-func (d data) Open(ctx context.Context) (io.ReadCloser, error) {
-	return d.source.Open(ctx)
-}
-
-func (d data) String() string {
-	return fmt.Sprintf("%v %v", d.digest, d.source)
-}
-
-// Bytes creates data for bytes.
-func Bytes(name string, b []byte) Data {
-	h := hash.SHA256Content(b)
-	return data{
-		digest: &rpb.Digest{
-			Hash:      h,
-			SizeBytes: int64(len(b)),
-		},
-		source: datasource.Bytes(name, b),
-	}
-}
-
-// Proto creates data for proto message.
-func Proto(m proto.Message) (Data, error) {
-	b, err := proto.Marshal(m)
-	if err != nil {
-		return nil, err
-	}
-	return Bytes(fmt.Sprintf("%T", m), b), nil
-}
-
-// FromSource creates digests from source.
-func FromSource(ctx context.Context, src Source) (Data, error) {
-	f, err := src.Open(ctx)
-	if err != nil {
-		return nil, err
-	}
-	defer f.Close()
-	h := sha256.New()
-	n, err := io.Copy(h, f)
-	if err != nil {
-		return nil, err
-	}
-	return data{
-		digest: &rpb.Digest{
-			Hash:      hex.EncodeToString(h.Sum(nil)),
-			SizeBytes: n,
-		},
-		source: src,
-	}, nil
-}
diff --git a/remoteexec/digest/digest_cache.go b/remoteexec/digest/digest_cache.go
deleted file mode 100644
index c61c815..0000000
--- a/remoteexec/digest/digest_cache.go
+++ /dev/null
@@ -1,221 +0,0 @@
-/* Copyright 2018 Google Inc. All Rights Reserved. */
-
-package digest
-
-import (
-	"context"
-	"errors"
-	"fmt"
-	"path/filepath"
-	"strings"
-	"sync"
-	"time"
-
-	rpb "github.com/bazelbuild/remote-apis/build/bazel/remote/execution/v2"
-	"github.com/golang/groupcache/lru"
-	"go.opencensus.io/stats"
-	"go.opencensus.io/stats/view"
-	"go.opencensus.io/tag"
-	"google.golang.org/grpc/codes"
-	"google.golang.org/grpc/status"
-	"google.golang.org/protobuf/proto"
-
-	"go.chromium.org/goma/server/log"
-	cachepb "go.chromium.org/goma/server/proto/cache"
-)
-
-var (
-	cacheStats = stats.Int64(
-		"go.chromium.org/goma/server/remoteexec/digest.cache",
-		"digest cache operations",
-		stats.UnitDimensionless)
-
-	opKey      = tag.MustNewKey("op")
-	fileExtKey = tag.MustNewKey("file_ext")
-
-	DefaultViews = []*view.View{
-		{
-			Name:        "go.chromium.org/goma/server/remoteexec/digest.cache-entries",
-			Description: `number of digest cache entries`,
-			Measure:     cacheStats,
-			Aggregation: view.Sum(),
-		},
-		{
-			Name:        "go.chromium.org/goma/server/remoteexec/digest.cache-ops",
-			Description: `digest cache operations`,
-			Measure:     cacheStats,
-			TagKeys: []tag.Key{
-				opKey,
-				fileExtKey,
-			},
-			Aggregation: view.Count(),
-		},
-	}
-)
-
-// Cache caches file's digest data.
-type Cache struct {
-	c cachepb.CacheServiceClient
-
-	mu  sync.Mutex
-	lru lru.Cache
-}
-
-// NewCache creates new cache for digest data.
-func NewCache(c cachepb.CacheServiceClient, maxEntries int) *Cache {
-	cache := &Cache{
-		c: c,
-	}
-	cache.lru.MaxEntries = maxEntries
-	cache.lru.OnEvicted = cache.onEvicted
-	return cache
-}
-
-var errNoCacheClient = errors.New("no cache client")
-
-func (c *Cache) cacheGet(ctx context.Context, key string) (*rpb.Digest, error) {
-	if c == nil || c.c == nil {
-		return nil, errNoCacheClient
-	}
-	resp, err := c.c.Get(ctx, &cachepb.GetReq{
-		Key: key,
-	})
-	if err != nil {
-		return nil, err
-	}
-	d := &rpb.Digest{}
-	err = proto.Unmarshal(resp.Kv.Value, d)
-	if err != nil {
-		return nil, err
-	}
-	return d, nil
-}
-
-func (c *Cache) cacheSet(ctx context.Context, key string, d *rpb.Digest) error {
-	if c == nil || c.c == nil {
-		return errNoCacheClient
-	}
-	v, err := proto.Marshal(d)
-	if err != nil {
-		return err
-	}
-	_, err = c.c.Put(ctx, &cachepb.PutReq{
-		Kv: &cachepb.KV{
-			Key:   key,
-			Value: v,
-		},
-	})
-	return err
-}
-
-// Get gets source's digest.
-func (c *Cache) Get(ctx context.Context, key string, src Source) (Data, error) {
-	var fileExt string
-	if gi, ok := src.(interface {
-		Filename() string
-	}); ok {
-		fileExt = filepathExt(gi.Filename())
-	}
-
-	if c != nil {
-		c.mu.Lock()
-		data, ok := c.lru.Get(lru.Key(key))
-		c.mu.Unlock()
-		if ok {
-			stats.RecordWithTags(ctx, []tag.Mutator{
-				tag.Upsert(opKey, "hit"),
-				tag.Upsert(fileExtKey, fileExt),
-			}, cacheStats.M(0))
-			// stochastically put it to cache client
-			// to make lru/lfu work?
-			return data.(Data), nil
-		}
-	}
-	var keystr string
-	if s := src.String(); strings.Contains(s, key) {
-		keystr = s
-	} else {
-		keystr = fmt.Sprintf("key:%s src:%s", key, src)
-	}
-	start := time.Now()
-	logger := log.FromContext(ctx)
-	// singleflight?
-	dk, err := c.cacheGet(ctx, key)
-	if err == nil {
-		logger.Infof("digest cache get %s => %v: %s", keystr, dk, time.Since(start))
-		d := New(src, dk)
-		if c != nil {
-			c.mu.Lock()
-			c.lru.Add(lru.Key(key), d)
-			c.mu.Unlock()
-			stats.RecordWithTags(ctx, []tag.Mutator{
-				tag.Upsert(opKey, "cache-get"),
-				tag.Upsert(fileExtKey, fileExt),
-			}, cacheStats.M(1))
-		}
-		return d, nil
-	}
-	op := "get-error"
-	if status.Code(err) == codes.NotFound {
-		op = "miss"
-	}
-	logger.Infof("digest cache %s %s %v: %s", op, keystr, err, time.Since(start))
-	stats.RecordWithTags(ctx, []tag.Mutator{
-		tag.Upsert(opKey, op),
-		tag.Upsert(fileExtKey, fileExt),
-	}, cacheStats.M(0))
-	d, err := FromSource(ctx, src)
-	if err != nil {
-		logger.Warnf("digest from source %s %v: %s", keystr, err, time.Since(start))
-		return nil, err
-	}
-	if c != nil {
-		c.mu.Lock()
-		c.lru.Add(lru.Key(key), d)
-		c.mu.Unlock()
-		logger.Infof("digest cache set %s => %v: %s", keystr, d, time.Since(start))
-		err = c.cacheSet(ctx, key, d.Digest())
-		op := "cache-set"
-		if err != nil {
-			logger.Warnf("digest cache set fail %s => %v: %v: %s", keystr, d, err, time.Since(start))
-			op = "cache-set-fail"
-		}
-		stats.RecordWithTags(ctx, []tag.Mutator{
-			tag.Upsert(opKey, op),
-			tag.Upsert(fileExtKey, fileExt),
-		}, cacheStats.M(1))
-	}
-	return d, nil
-}
-
-func (c *Cache) onEvicted(k lru.Key, value interface{}) {
-	ctx := context.Background()
-	logger := log.FromContext(ctx)
-	key := k.(string)
-	var filename, fileExt string
-	d := value.(Data)
-	if dd, ok := d.(data); ok {
-		src := dd.source
-		if gi, ok := src.(interface {
-			Filename() string
-		}); ok {
-			filename = gi.Filename()
-			fileExt = filepathExt(gi.Filename())
-		}
-	}
-	logger.Infof("digest cache evict %s %s %q", key, d.Digest(), filename)
-	stats.RecordWithTags(ctx, []tag.Mutator{
-		tag.Upsert(opKey, "evict"),
-		tag.Upsert(fileExtKey, fileExt),
-	}, cacheStats.M(-1))
-}
-
-func filepathExt(fname string) string {
-	ext := filepath.Ext(fname)
-	if strings.ContainsAny(ext, `/\`) {
-		// ext should not have path separtor.
-		// if it has, it would be in directory part.
-		ext = ""
-	}
-	return ext
-}
diff --git a/remoteexec/digest/digest_cache_test.go b/remoteexec/digest/digest_cache_test.go
deleted file mode 100644
index 61f5fcb..0000000
--- a/remoteexec/digest/digest_cache_test.go
+++ /dev/null
@@ -1,38 +0,0 @@
-/* Copyright 2019 Google Inc. All Rights Reserved. */
-
-package digest
-
-import (
-	"context"
-	"testing"
-
-	"go.chromium.org/goma/server/cache"
-)
-
-func TestCacheGet(t *testing.T) {
-	c, err := cache.New(cache.Config{
-		MaxBytes: 1 * 1024 * 1024,
-	})
-	if err != nil {
-		t.Fatal(err)
-	}
-	dc := NewCache(cache.LocalClient{
-		CacheServiceServer: c,
-	}, 1000)
-
-	ctx := context.Background()
-
-	want := Bytes("first", []byte{12})
-	_, err = dc.Get(ctx, "12", want)
-	if err != nil {
-		t.Fatalf("Get(ctx, 12, 'first')=%v; want nil error", err)
-	}
-
-	d2, err := dc.Get(ctx, "12", Bytes("second", []byte{12}))
-	if err != nil {
-		t.Fatalf("Get(ctx, 12, 'second')=%v; want nil error", err)
-	}
-	if d2.String() == want.String() {
-		t.Errorf("Get(ctx, 12, 'second')=%v; want %v", d2, want)
-	}
-}
diff --git a/remoteexec/digest/digest_store.go b/remoteexec/digest/digest_store.go
deleted file mode 100644
index 785a24a..0000000
--- a/remoteexec/digest/digest_store.go
+++ /dev/null
@@ -1,70 +0,0 @@
-/* Copyright 2018 Google Inc. All Rights Reserved. */
-
-package digest
-
-import (
-	rpb "github.com/bazelbuild/remote-apis/build/bazel/remote/execution/v2"
-)
-
-type digestKey struct {
-	hash      string
-	sizeBytes int64
-}
-
-func asKey(d *rpb.Digest) digestKey {
-	return digestKey{
-		hash:      d.Hash,
-		sizeBytes: d.SizeBytes,
-	}
-}
-
-// Store works as local content addressable storage.
-type Store struct {
-	m map[digestKey]Data
-}
-
-// NewStore creates new local content addressable storage.
-func NewStore() *Store {
-	return &Store{
-		m: make(map[digestKey]Data),
-	}
-}
-
-// Set sets data in store.
-func (s *Store) Set(data Data) {
-	s.m[asKey(data.Digest())] = data
-}
-
-// Get gets data from store.
-func (s *Store) Get(digest *rpb.Digest) (Data, bool) {
-	v, ok := s.m[asKey(digest)]
-	return v, ok
-}
-
-// GetSource gets source from store.
-func (s *Store) GetSource(digest *rpb.Digest) (Source, bool) {
-	v, ok := s.Get(digest)
-	if !ok {
-		return nil, false
-	}
-	switch v := v.(type) {
-	case data:
-		return v.source, true
-	}
-	return v.(Source), true
-}
-
-// List lists known digests in cas.
-func (s *Store) List() []*rpb.Digest {
-	var digests []*rpb.Digest
-	for k := range s.m {
-		digests = append(digests, &rpb.Digest{
-			Hash:      k.hash,
-			SizeBytes: k.sizeBytes,
-		})
-	}
-	// sort?
-	return digests
-}
-
-// TODO: data for goma FileBlob.
diff --git a/remoteexec/exec.go b/remoteexec/exec.go
deleted file mode 100644
index 251b5e9..0000000
--- a/remoteexec/exec.go
+++ /dev/null
@@ -1,1714 +0,0 @@
-// Copyright 2018 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.
-
-package remoteexec
-
-import (
-	"bytes"
-	"context"
-	_ "embed"
-	"errors"
-	"fmt"
-	"math/rand"
-	"path"
-	"path/filepath"
-	"sort"
-	"strings"
-	"sync"
-	"time"
-
-	rpb "github.com/bazelbuild/remote-apis/build/bazel/remote/execution/v2"
-
-	"go.opencensus.io/stats"
-	"go.opencensus.io/tag"
-	"go.opencensus.io/trace"
-	"golang.org/x/sync/errgroup"
-	"google.golang.org/grpc/codes"
-	"google.golang.org/grpc/status"
-	"google.golang.org/protobuf/proto"
-	tspb "google.golang.org/protobuf/types/known/timestamppb"
-
-	"go.chromium.org/goma/server/command/descriptor"
-	"go.chromium.org/goma/server/command/descriptor/posixpath"
-	"go.chromium.org/goma/server/command/descriptor/winpath"
-	"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"
-	"go.chromium.org/goma/server/remoteexec/cas"
-	"go.chromium.org/goma/server/remoteexec/digest"
-	"go.chromium.org/goma/server/remoteexec/merkletree"
-	"go.chromium.org/goma/server/rpc"
-)
-
-type request struct {
-	f         *Adapter
-	userGroup string
-	gomaReq   *gomapb.ExecReq
-	gomaResp  *gomapb.ExecResp
-
-	client Client
-	cas    *cas.CAS
-
-	cmdConfig *cmdpb.Config
-	cmdFiles  []*cmdpb.FileSpec
-
-	digestStore *digest.Store
-	tree        *merkletree.MerkleTree
-	input       gomaInputInterface
-
-	filepath    clientFilePath
-	cmdFilepath clientFilePath
-
-	args         []string
-	envs         []string
-	outputs      []string
-	outputDirs   []string
-	platform     *rpb.Platform
-	action       *rpb.Action
-	actionDigest *rpb.Digest
-
-	allowChroot bool
-	needChroot  bool
-
-	crossTarget string
-
-	err error
-}
-
-func (r *request) Close() {
-	r.input.Close()
-}
-
-type clientFilePath interface {
-	IsAbs(path string) bool
-	Base(path string) string
-	Dir(path string) string
-	Join(elem ...string) string
-	Rel(basepath, targpath string) (string, error)
-	Clean(path string) string
-	SplitElem(path string) []string
-	PathSep() string
-}
-
-func doNotCache(req *gomapb.ExecReq) bool {
-	switch req.GetCachePolicy() {
-	case gomapb.ExecReq_LOOKUP_AND_STORE, gomapb.ExecReq_STORE_ONLY, gomapb.ExecReq_LOOKUP_AND_STORE_SUCCESS:
-		return false
-	default:
-		return true
-	}
-}
-
-func skipCacheLookup(req *gomapb.ExecReq) bool {
-	switch req.GetCachePolicy() {
-	case gomapb.ExecReq_STORE_ONLY:
-		return true
-	default:
-		return false
-	}
-}
-
-// Takes an input of environment flag defs, e.g. FLAG_NAME=value, and returns an array of
-// rpb.Command_EnvironmentVariable with these flag names and values.
-func createEnvVars(ctx context.Context, envs []string) []*rpb.Command_EnvironmentVariable {
-	envMap := make(map[string]string)
-	logger := log.FromContext(ctx)
-	for _, env := range envs {
-		e := strings.SplitN(env, "=", 2)
-		key, value := e[0], e[1]
-		storedValue, ok := envMap[key]
-		if ok {
-			logger.Infof("Duplicate env var: %s=%s => %s", key, storedValue, value)
-		}
-		envMap[key] = value
-	}
-
-	// EnvironmentVariables must be lexicographically sorted by name.
-	var envKeys []string
-	for k := range envMap {
-		envKeys = append(envKeys, k)
-	}
-	sort.Strings(envKeys)
-
-	var envVars []*rpb.Command_EnvironmentVariable
-	for _, k := range envKeys {
-		envVars = append(envVars, &rpb.Command_EnvironmentVariable{
-			Name:  k,
-			Value: envMap[k],
-		})
-	}
-	return envVars
-}
-
-// ID returns compiler proxy id of the request.
-func (r *request) ID() string {
-	if r == nil {
-		return "<unknown>"
-	}
-	return r.gomaReq.GetRequesterInfo().GetCompilerProxyId()
-}
-
-// Err returns error of the request.
-func (r *request) Err() error {
-	switch status.Code(r.err) {
-	case codes.OK:
-		return nil
-	case codes.Canceled, codes.DeadlineExceeded, codes.Aborted:
-		// report cancel/deadline exceeded/aborted as is
-		return r.err
-
-	case codes.Unauthenticated:
-		// unauthenticated happens when oauth2 access token
-		// is expired during exec call.
-		// e.g.
-		// desc = Request had invalid authentication credentials.
-		//   Expected OAuth 2 access token, login cookie or
-		//   other valid authentication credential.
-		//   See https://developers.google.com/identity/sign-in/web/devconsole-project.
-		// report it back to caller, so caller could retry it
-		// again with new refreshed oauth2 access token.
-		return r.err
-	default:
-		return status.Errorf(codes.Internal, "exec error: %v", r.err)
-	}
-}
-
-func (r *request) instanceName() string {
-	basename := r.cmdConfig.GetRemoteexecPlatform().GetRbeInstanceBasename()
-	if basename == "" {
-		return r.f.Instance()
-	}
-	return path.Join(r.f.InstancePrefix, basename)
-}
-
-// getInventoryData looks up Config and FileSpec from Inventory, and creates
-// execution platform properties from Config.
-// It returns non-nil ExecResp for:
-// - compiler/subprogram not found
-// - bad path_type in command config
-func (r *request) getInventoryData(ctx context.Context) *gomapb.ExecResp {
-	if r.err != nil {
-		return nil
-	}
-
-	logger := log.FromContext(ctx)
-
-	cmdConfig, cmdFiles, err := r.f.Inventory.Pick(ctx, r.gomaReq, r.gomaResp)
-	if err != nil {
-		logger.Errorf("Inventory.Pick failed: %v", err)
-		return r.gomaResp
-	}
-
-	r.filepath, err = descriptor.FilePathOf(cmdConfig.GetCmdDescriptor().GetSetup().GetPathType())
-	if err != nil {
-		logger.Errorf("bad path type in setup %s: %v", cmdConfig.GetCmdDescriptor().GetSelector(), err)
-		r.gomaResp.Error = gomapb.ExecResp_BAD_REQUEST.Enum()
-		r.gomaResp.ErrorMessage = append(r.gomaResp.ErrorMessage, fmt.Sprintf("bad compiler config: %v", err))
-		return r.gomaResp
-	}
-	if cmdConfig.GetCmdDescriptor().GetCross().GetWindowsCross() {
-		r.filepath = winpath.FilePath{}
-		r.cmdFilepath = posixpath.FilePath{}
-		// drop .bat suffix
-		// http://b/185210502#comment12
-		cmdFiles[0].Path = strings.TrimSuffix(cmdFiles[0].Path, ".bat")
-	} else {
-		r.cmdFilepath = r.filepath
-	}
-
-	r.cmdConfig = cmdConfig
-	r.cmdFiles = cmdFiles
-
-	r.platform = &rpb.Platform{}
-	for _, prop := range cmdConfig.GetRemoteexecPlatform().GetProperties() {
-		r.addPlatformProperty(ctx, prop.Name, prop.Value)
-	}
-	if len(r.gomaReq.GetRequesterInfo().GetPlatformProperties()) > 0 {
-		for _, pp := range r.gomaReq.GetRequesterInfo().GetPlatformProperties() {
-			if !isSafePlatformProperty(pp.GetName(), pp.GetValue()) {
-				logger.Errorf("unsafe user platform property: %v", pp)
-				r.gomaResp.Error = gomapb.ExecResp_BAD_REQUEST.Enum()
-				r.gomaResp.ErrorMessage = append(r.gomaResp.ErrorMessage, fmt.Sprintf("unsafe platform property: %v", pp))
-				continue
-			}
-			logger.Infof("override by user platform property: %v", pp)
-			r.addPlatformProperty(ctx, pp.GetName(), pp.GetValue())
-		}
-		if len(r.gomaResp.ErrorMessage) > 0 {
-			return r.gomaResp
-		}
-	}
-	r.allowChroot = cmdConfig.GetRemoteexecPlatform().GetHasNsjail()
-	logger.Infof("platform: %s, allowChroot=%t path_tpye=%s windows_cross=%t", r.platform, r.allowChroot, cmdConfig.GetCmdDescriptor().GetSetup().GetPathType(), cmdConfig.GetCmdDescriptor().GetCross().GetWindowsCross())
-	return nil
-}
-
-func isSafePlatformProperty(name, value string) bool {
-	switch name {
-	case "container-image", "InputRootAbsolutePath", "cache-silo":
-		return true
-	case "dockerRuntime":
-		return value == "runsc"
-	}
-	return false
-}
-
-func (r *request) addPlatformProperty(ctx context.Context, name, value string) {
-	for _, p := range r.platform.Properties {
-		if p.Name == name {
-			p.Value = value
-			return
-		}
-	}
-	r.platform.Properties = append(r.platform.Properties, &rpb.Platform_Property{
-		Name:  name,
-		Value: value,
-	})
-}
-
-type inputDigestData struct {
-	filename string
-	digest.Data
-}
-
-func (id inputDigestData) String() string {
-	return fmt.Sprintf("%s %s", id.Data.String(), id.filename)
-}
-
-func changeSymlinkAbsToRel(e merkletree.Entry) (merkletree.Entry, error) {
-	dir := filepath.Dir(e.Name)
-	if !filepath.IsAbs(dir) {
-		return merkletree.Entry{}, fmt.Errorf("absolute symlink path not allowed: %s -> %s", e.Name, e.Target)
-	}
-	target, err := filepath.Rel(dir, e.Target)
-	if err != nil {
-		return merkletree.Entry{}, fmt.Errorf("failed to make relative for absolute symlink path: %s in %s -> %s: %v", e.Name, dir, e.Target, err)
-	}
-	e.Target = target
-	return e, nil
-}
-
-type gomaInputInterface interface {
-	toDigest(context.Context, *gomapb.ExecReq_Input) (digest.Data, error)
-	upload(context.Context, []*gomapb.FileBlob) ([]string, error)
-	Close()
-}
-
-func uploadInputFiles(ctx context.Context, inputs []*gomapb.ExecReq_Input, gi gomaInputInterface) error {
-	ctx, span := trace.StartSpan(ctx, "go.chromium.org/goma/server/remoteexec.request.uploadInputFiles")
-	defer span.End()
-	span.AddAttributes(trace.Int64Attribute("uploads", int64(len(inputs))))
-	count := 0
-	size := 0
-	batchLimit := 500
-	sizeLimit := 10 * 1024 * 1024
-
-	beginOffset := 0
-	hashKeys := make([]string, len(inputs))
-
-	eg, ctx := errgroup.WithContext(ctx)
-
-	for i, input := range inputs {
-		count++
-		size += len(input.Content.Content)
-
-		// Upload a bunch of file blobs if one of the following:
-		// - inputs[uploadBegin:i] reached the upload blob count limit
-		// - inputs[uploadBegin:i] exceeds the upload blob size limit
-		// - we are on the last blob to be uploaded
-		if count < batchLimit && size < sizeLimit && i < len(inputs)-1 {
-			continue
-		}
-
-		inputs := inputs[beginOffset : i+1]
-		results := hashKeys[beginOffset : i+1]
-		eg.Go(func() error {
-			contents := make([]*gomapb.FileBlob, len(inputs))
-			for i, input := range inputs {
-				contents[i] = input.Content
-			}
-
-			var hks []string
-			var err error
-			err = rpc.Retry{}.Do(ctx, func() error {
-				hks, err = gi.upload(ctx, contents)
-				return err
-			})
-
-			if err != nil {
-				return fmt.Errorf("setup %s input error: %v", inputs[0].GetFilename(), err)
-			}
-			if len(hks) != len(contents) {
-				return fmt.Errorf("invalid number of hash keys: %d, want %d", len(hks), len(contents))
-			}
-			for i, hk := range hks {
-				input := inputs[i]
-				if input.GetHashKey() != hk {
-					return fmt.Errorf("hashkey missmatch: embedded input %s %s != %s", input.GetFilename(), input.GetHashKey(), hk)
-				}
-				results[i] = hk
-			}
-			return nil
-		})
-		beginOffset = i + 1
-		count = 0
-		size = 0
-	}
-
-	defer func() {
-		maxOutputSize := len(inputs)
-		if maxOutputSize > 10 {
-			maxOutputSize = 10
-		}
-		successfulUploadsMsg := make([]string, 0, maxOutputSize+1)
-		for i, input := range inputs {
-			if len(hashKeys[i]) == 0 {
-				continue
-			}
-			if i == maxOutputSize && i < len(inputs)-1 {
-				successfulUploadsMsg = append(successfulUploadsMsg, "...")
-				break
-			}
-			successfulUploadsMsg = append(successfulUploadsMsg, fmt.Sprintf("%s -> %s", input.GetFilename(), hashKeys[i]))
-		}
-		logger := log.FromContext(ctx)
-		logger.Infof("embedded inputs: %v", successfulUploadsMsg)
-
-		numSuccessfulUploads := 0
-		for _, hk := range hashKeys {
-			if len(hk) > 0 {
-				numSuccessfulUploads++
-			}
-		}
-		if numSuccessfulUploads < len(inputs) {
-			logger.Errorf("%d file blobs successfully uploaded, out of %d", numSuccessfulUploads, len(inputs))
-		}
-	}()
-
-	return eg.Wait()
-}
-
-func dedupInputs(filepath clientFilePath, cwd string, inputs []*gomapb.ExecReq_Input) []*gomapb.ExecReq_Input {
-	var deduped []*gomapb.ExecReq_Input
-	m := make(map[string]int) // key name -> index in deduped
-
-	for _, input := range inputs {
-		fname := input.GetFilename()
-		if !filepath.IsAbs(fname) {
-			fname = filepath.Join(cwd, fname)
-		}
-		k := strings.ToLower(fname)
-		i, found := m[k]
-		if !found {
-			m[k] = len(deduped)
-			deduped = append(deduped, input)
-			continue
-		}
-		// If there is already registered filename, compare and take shorter one.
-		if len(input.GetFilename()) < len(deduped[i].GetFilename()) {
-			deduped[i] = input
-			continue
-		}
-		// If length is same, take lexicographically smaller one.
-		if len(input.GetFilename()) == len(deduped[i].GetFilename()) && input.GetFilename() < deduped[i].GetFilename() {
-			deduped[i] = input
-		}
-	}
-	return deduped
-}
-
-type inputFileResult struct {
-	missingInput  string
-	missingReason string
-	file          merkletree.Entry
-	needUpload    bool
-	err           error
-}
-
-func inputFiles(ctx context.Context, inputs []*gomapb.ExecReq_Input, gi gomaInputInterface, rootRel func(string) (string, error), executableInputs map[string]bool) []inputFileResult {
-	logger := log.FromContext(ctx)
-	var wg sync.WaitGroup
-	ctx, span := trace.StartSpan(ctx, "go.chromium.org/goma/server/remoteexec.request.inputFiles")
-	defer span.End()
-	span.AddAttributes(trace.Int64Attribute("inputs", int64(len(inputs))))
-	results := make([]inputFileResult, len(inputs))
-	for i, input := range inputs {
-		wg.Add(1)
-		go func(input *gomapb.ExecReq_Input, result *inputFileResult) {
-			defer wg.Done()
-			fname, err := rootRel(input.GetFilename())
-			if err != nil {
-				if err == errOutOfRoot {
-					logger.Warnf("filename %s: %v", input.GetFilename(), err)
-					return
-				}
-				result.err = fmt.Errorf("input file: %s %v", input.GetFilename(), err)
-				return
-			}
-
-			data, err := gi.toDigest(ctx, input)
-			if err != nil {
-				result.missingInput = input.GetFilename()
-				result.missingReason = fmt.Sprintf("input: %v", err)
-				return
-			}
-			file := merkletree.Entry{
-				Name: fname,
-				Data: inputDigestData{
-					filename: input.GetFilename(),
-					Data:     data,
-				},
-				IsExecutable: executableInputs[input.GetFilename()],
-			}
-			result.file = file
-			if input.Content == nil {
-				return
-			}
-			result.needUpload = true
-		}(input, &results[i])
-	}
-	wg.Wait()
-	return results
-}
-
-// newInputTree constructs input tree from req.
-// it returns non-nil ExecResp for:
-// - missing inputs
-// - input root detection failed
-// - non-relative and non C: drive on windows.
-func (r *request) newInputTree(ctx context.Context) *gomapb.ExecResp {
-	if r.err != nil {
-		return nil
-	}
-	ctx, span := trace.StartSpan(ctx, "go.chromium.org/goma/server/remoteexec.request.newInputTree")
-	defer span.End()
-	logger := log.FromContext(ctx)
-
-	execPaths, err := execPaths(r.filepath, r.gomaReq, r.cmdFiles[0].Path)
-	if err != nil {
-		logger.Errorf("bad input: %v", err)
-		r.gomaResp.Error = gomapb.ExecResp_BAD_REQUEST.Enum()
-		r.gomaResp.ErrorMessage = append(r.gomaResp.ErrorMessage, fmt.Sprintf("bad input: %v", err))
-		return r.gomaResp
-	}
-	execRootDir := r.gomaReq.GetRequesterInfo().GetExecRoot()
-	rootDir, needChroot, err := deriveExecRoot(r.filepath, execPaths, r.allowChroot, execRootDir)
-	if err != nil {
-		logger.Errorf("exec root detection failed: %v", err)
-		logFileList(logger, "exec paths", execPaths)
-		r.gomaResp.Error = gomapb.ExecResp_BAD_REQUEST.Enum()
-		r.gomaResp.ErrorMessage = append(r.gomaResp.ErrorMessage, fmt.Sprintf("exec root detection failed: %v", err))
-		return r.gomaResp
-	}
-	r.tree = merkletree.New(r.filepath, rootDir, r.digestStore)
-	r.needChroot = needChroot
-
-	logger.Infof("new input tree cwd:%s root:%s execRoot:%s %s", r.gomaReq.GetCwd(), r.tree.RootDir(), execRootDir, r.filepath)
-	// If toolchain_included is true, r.gomaReq.Input and cmdFiles will contain the same files.
-	// To avoid dup, if it's added in r.gomaReq.Input, we don't add it as cmdFiles.
-	// While processing r.gomaReq.Input, we handle missing input, so the main routine is in
-	// r.gomaReq.Input.
-
-	// path from cwd -> is_executable. Don't confuse "path from cwd" and "path from input root".
-	// Everything (except symlink) in ToolchainSpec should be in r.gomaReq.Input.
-	// If not and it's necessary to execute, a runtime error (while compile) can happen.
-	// e.g. *.so is missing etc.
-	toolchainInputs := make(map[string]bool)
-	executableInputs := make(map[string]bool)
-	if r.gomaReq.GetToolchainIncluded() {
-		for _, ts := range r.gomaReq.ToolchainSpecs {
-			if ts.GetSymlinkPath() != "" {
-				// If toolchain is a symlink, it is not included in r.gomaReq.Input.
-				// So, toolchainInputs should not contain it.
-				continue
-			}
-			toolchainInputs[ts.GetPath()] = true
-			if ts.GetIsExecutable() {
-				executableInputs[ts.GetPath()] = true
-			}
-		}
-	}
-
-	cleanCWD := r.filepath.Clean(r.gomaReq.GetCwd())
-	cleanRootDir := r.filepath.Clean(r.tree.RootDir())
-
-	start := time.Now()
-	reqInputs := r.gomaReq.Input
-	if _, ok := r.filepath.(winpath.FilePath); ok && !r.cmdConfig.GetCmdDescriptor().GetCross().GetWindowsCross() {
-		// need to dedup filename for windows,
-		// except windows cross case.
-		reqInputs = dedupInputs(r.filepath, cleanCWD, r.gomaReq.Input)
-		if len(reqInputs) != len(r.gomaReq.Input) {
-			logger.Infof("input dedup %d -> %d", len(r.gomaReq.Input), len(reqInputs))
-		}
-	}
-	results := inputFiles(ctx, reqInputs, r.input, func(filename string) (string, error) {
-		return rootRel(r.filepath, filename, cleanCWD, cleanRootDir)
-	}, executableInputs)
-	uploads := make([]*gomapb.ExecReq_Input, 0, len(reqInputs))
-	for i, input := range reqInputs {
-		result := &results[i]
-		if r.err == nil && result.err != nil {
-			r.err = result.err
-		}
-		if result.needUpload {
-			uploads = append(uploads, input)
-		}
-	}
-	if r.err != nil {
-		logger.Warnf("inputFiles=%d uploads=%d in %s err:%v", len(reqInputs), len(uploads), time.Since(start), r.err)
-		return nil
-	}
-	logger.Infof("inputFiles=%d uploads=%d in %s", len(reqInputs), len(uploads), time.Since(start))
-
-	var files []merkletree.Entry
-	var missingInputs []string
-	var missingReason []string
-	for _, in := range results {
-		if in.missingInput != "" {
-			missingInputs = append(missingInputs, in.missingInput)
-			missingReason = append(missingReason, in.missingReason)
-			continue
-		}
-		if in.file.Name == "" {
-			// ignore out of root files.
-			continue
-		}
-		files = append(files, in.file)
-	}
-	if len(missingInputs) > 0 {
-		logger.Infof("missing %d inputs out of %d. need to uploads=%d", len(missingInputs), len(reqInputs), len(uploads))
-
-		r.gomaResp.MissingInput = missingInputs
-		r.gomaResp.MissingReason = missingReason
-		thinOutMissing(r.gomaResp, r.f.MissingInputLimit)
-		sortMissing(r.gomaReq.Input, r.gomaResp)
-		logFileList(logger, "missing inputs", r.gomaResp.MissingInput)
-		return r.gomaResp
-	}
-
-	// create wrapper scripts
-	err = r.newWrapperScript(ctx, r.cmdConfig, r.cmdFiles[0].Path)
-	if err != nil {
-		var badReqErr badRequestError
-		if errors.As(err, &badReqErr) {
-			r.gomaResp.Error = gomapb.ExecResp_BAD_REQUEST.Enum()
-			r.gomaResp.ErrorMessage = append(r.gomaResp.ErrorMessage, badReqErr.Error())
-			return r.gomaResp
-		}
-		// otherwise, internal error.
-		r.err = fmt.Errorf("wrapper script: %v", err)
-		return nil
-	}
-
-	symAbsOk := r.f.capabilities.GetCacheCapabilities().GetSymlinkAbsolutePathStrategy() == rpb.SymlinkAbsolutePathStrategy_ALLOWED
-
-	cmdCleanCWD := cleanCWD
-	cmdCleanRootDir := cleanRootDir
-	if (r.filepath == winpath.FilePath{}) && (r.cmdFilepath == posixpath.FilePath{}) {
-		cmdCleanCWD = winpath.ToPosix(cleanCWD)
-		cmdCleanRootDir = winpath.ToPosix(cleanRootDir)
-	}
-
-	for _, f := range r.cmdFiles {
-		if _, found := toolchainInputs[f.Path]; found {
-			// Must be processed in r.gomaReq.Input. So, skip this.
-			// TODO: cmdFiles should be empty instead if toolchain_included = true case?
-			continue
-		}
-
-		e, err := fileSpecToEntry(ctx, f, r.f.CmdStorage)
-		if err != nil {
-			r.err = fmt.Errorf("fileSpecToEntry: %v", err)
-			return nil
-		}
-		if !symAbsOk && e.Target != "" && filepath.IsAbs(e.Target) {
-			e, err = changeSymlinkAbsToRel(e)
-			if err != nil {
-				r.err = err
-				return nil
-			}
-		}
-		fname, err := rootRel(r.cmdFilepath, e.Name, cmdCleanCWD, cmdCleanRootDir)
-		if err != nil {
-			if err == errOutOfRoot {
-				logger.Warnf("cmd files: out of root: %s", e.Name)
-				continue
-			}
-			r.err = fmt.Errorf("command file: %v", err)
-			return nil
-		}
-		e.Name = fname
-		files = append(files, e)
-	}
-
-	addDirs := func(name string, dirs []string) {
-		if r.err != nil {
-			return
-		}
-		for _, d := range dirs {
-			rel, err := rootRel(r.filepath, d, cleanCWD, cleanRootDir)
-			if err != nil {
-				if err == errOutOfRoot {
-					logger.Warnf("%s %s: %v", name, d, err)
-					continue
-				}
-				r.err = fmt.Errorf("%s %s: %v", name, d, err)
-				return
-			}
-			files = append(files, merkletree.Entry{
-				// directory
-				Name: rel,
-			})
-		}
-	}
-	// Set up system include and framework paths (b/119072207)
-	// -isystem etc can be set for a compile, and non-existence of a directory specified by -isystem may cause compile error even if no file inside the directory is used.
-	addDirs("cxx system include path", r.gomaReq.GetCommandSpec().GetCxxSystemIncludePath())
-	addDirs("system include path", r.gomaReq.GetCommandSpec().GetSystemIncludePath())
-	addDirs("system framework path", r.gomaReq.GetCommandSpec().GetSystemFrameworkPath())
-
-	// prepare output dirs.
-	r.outputs = outputs(ctx, r.cmdConfig, r.gomaReq)
-	var outDirs []string
-	for _, d := range r.outputs {
-		outDirs = append(outDirs, r.filepath.Dir(d))
-	}
-	addDirs("output file", outDirs)
-	r.outputDirs = outputDirs(ctx, r.cmdConfig, r.gomaReq)
-	addDirs("output dir", r.outputDirs)
-	if r.err != nil {
-		return nil
-	}
-
-	for _, f := range files {
-		err = r.tree.Set(f)
-		if err != nil {
-			r.err = fmt.Errorf("input file: %v: %v", f, err)
-			return nil
-		}
-	}
-
-	root, err := r.tree.Build(ctx)
-	if err != nil {
-		r.err = err
-		return nil
-	}
-	logger.Infof("input root digest: %v", root)
-	r.action.InputRootDigest = root
-
-	// uploads embedded contents to file-server
-	// for the case the file was not yet uploaded to RBE CAS.
-	// even if client sends input with embedded content,
-	// the content may be already uploaded to RBE CAS,
-	// and uploaded content may not be needed,
-	// so we could ignore error of these uploads.
-	start = time.Now()
-	err = uploadInputFiles(ctx, uploads, r.input)
-	logger.Infof("upload %d inputs out of %d in %s: %v", len(uploads), len(r.gomaReq.Input), time.Since(start), err)
-	return nil
-}
-
-type wrapperType int
-
-const (
-	wrapperRelocatable wrapperType = iota
-	wrapperInputRootAbsolutePath
-	wrapperNsjailChroot
-	wrapperWin
-	wrapperWinInputRootAbsolutePath
-)
-
-func (w wrapperType) String() string {
-	switch w {
-	case wrapperRelocatable:
-		return "wrapper-relocatable"
-	case wrapperInputRootAbsolutePath:
-		return "wrapper-input-root-absolute-path"
-	case wrapperNsjailChroot:
-		return "wrapper-nsjail-chroot"
-	case wrapperWin:
-		return "wrapper-win"
-	case wrapperWinInputRootAbsolutePath:
-		return "wrapper-win-input-root-absolute-path"
-	default:
-		return fmt.Sprintf("wrapper-unknown-%d", int(w))
-	}
-}
-
-var (
-	// TODO: use working_directory in action.
-	// need to fix output path to be relative to working_directory.
-	// http://b/113370588
-	//go:embed run.sh
-	wrapperScript []byte
-)
-
-type badRequestError struct {
-	err error
-}
-
-func (b badRequestError) Error() string {
-	return b.err.Error()
-}
-
-// TODO: put wrapper script in platform container?
-func (r *request) newWrapperScript(ctx context.Context, cmdConfig *cmdpb.Config, argv0 string) error {
-	logger := log.FromContext(ctx)
-
-	cwd := r.gomaReq.GetCwd()
-	cleanCWD := r.filepath.Clean(cwd)
-	cleanRootDir := r.filepath.Clean(r.tree.RootDir())
-	wd, err := rootRel(r.filepath, cwd, cleanCWD, cleanRootDir)
-	if err != nil {
-		return badRequestError{err: fmt.Errorf("bad cwd=%s: %v", cwd, err)}
-	}
-	if wd == "" {
-		wd = "."
-	}
-	if cmdConfig.GetCmdDescriptor().GetCross().GetWindowsCross() {
-		wd = winpath.ToPosix(wd)
-	}
-	envs := []string{fmt.Sprintf("WORK_DIR=%s", wd)}
-
-	// The developer of this program can make multiple wrapper scripts
-	// to be used by adding fileDesc instances to `files`.
-	// However, only the first one is called in the command line.
-	// The other scripts should be called from the first wrapper script
-	// if needed.
-	var files []merkletree.Entry
-
-	args := buildArgs(ctx, cmdConfig, argv0, r.gomaReq)
-	// TODO: only allow specific envs.
-	r.crossTarget = targetFromArgs(args)
-
-	var relocatableErr error
-	wt := wrapperRelocatable
-	switch r.filepath.(type) {
-	case posixpath.FilePath:
-		if r.needChroot {
-			wt = wrapperNsjailChroot
-		} else {
-			relocatableErr = relocatableReq(ctx, cmdConfig, r.filepath, r.gomaReq.Arg, r.gomaReq.Env)
-			if relocatableErr != nil {
-				wt = wrapperInputRootAbsolutePath
-				logger.Infof("non relocatable: %v", relocatableErr)
-			}
-		}
-	case winpath.FilePath:
-		relocatableErr = relocatableReq(ctx, cmdConfig, r.filepath, r.gomaReq.Arg, r.gomaReq.Env)
-		if relocatableErr != nil {
-			wt = wrapperWinInputRootAbsolutePath
-			logger.Infof("non relocatable: %v", relocatableErr)
-		} else {
-			wt = wrapperWin
-		}
-		if cmdConfig.GetCmdDescriptor().GetCross().GetWindowsCross() {
-			switch wt {
-			case wrapperWinInputRootAbsolutePath:
-				// we expect most case is relocatable
-				// with -fdebug-compilation-dir=.
-				// but it would break if user uses unknown
-				// flags, which makes request unrelocatable.
-				// See rootDir fix in wrapperInputRootAbsolutePath below.
-				wt = wrapperInputRootAbsolutePath
-			case wrapperWin:
-				wt = wrapperRelocatable
-			}
-		}
-	default:
-		// internal error? maybe toolchain config is broken.
-		return fmt.Errorf("bad path type: %T", r.filepath)
-	}
-
-	const posixWrapperName = "run.sh"
-	switch wt {
-	case wrapperNsjailChroot:
-		logger.Infof("run with nsjail chroot")
-		// needed for bind mount.
-		r.addPlatformProperty(ctx, "dockerPrivileged", "true")
-		// needed for chroot command and mount command.
-		r.addPlatformProperty(ctx, "dockerRunAsRoot", "true")
-		nsjailCfg := nsjailChrootConfig(cwd, r.filepath, r.gomaReq.GetToolchainSpecs(), r.gomaReq.Env)
-		files = []merkletree.Entry{
-			{
-				Name:         posixWrapperName,
-				Data:         digest.Bytes("nsjail-chroot-run-wrapper-script", nsjailChrootRunWrapperScript),
-				IsExecutable: true,
-			},
-			{
-				Name: "nsjail.cfg",
-				Data: digest.Bytes("nsjail-config-file", []byte(nsjailCfg)),
-			},
-		}
-	case wrapperInputRootAbsolutePath:
-		wrapperData := digest.Bytes("wrapper-script", wrapperScript)
-		files, wrapperData = r.maybeApplyHardening(ctx, "InputRootAbsolutePath", files, wrapperData)
-		// https://cloud.google.com/remote-build-execution/docs/remote-execution-properties#container_properties
-		rootDir := r.tree.RootDir()
-		if cmdConfig.GetCmdDescriptor().GetCross().GetWindowsCross() {
-			// we can't use windows path as absolute path.
-			// drop first two letters (i.e. `C:`), and
-			// convert \ to /.
-			// instead of making relocatable check loose,
-			// better to omit drive letter and make the
-			// effective for the same drive letter.
-			rootDir = winpath.ToPosix(rootDir)
-		}
-		r.addPlatformProperty(ctx, "InputRootAbsolutePath", rootDir)
-		for _, e := range r.gomaReq.Env {
-			envs = append(envs, e)
-		}
-		files = append([]merkletree.Entry{
-			{
-				Name:         posixWrapperName,
-				Data:         wrapperData,
-				IsExecutable: true,
-			},
-		}, files...)
-	case wrapperRelocatable:
-		wrapperData := digest.Bytes("wrapper-script", wrapperScript)
-		files, wrapperData = r.maybeApplyHardening(ctx, "chdir: relocatble", files, wrapperData)
-		for _, e := range r.gomaReq.Env {
-			if strings.HasPrefix(e, "PWD=") {
-				// PWD is usually absolute path.
-				// if relocatable, then we should remove
-				// PWD environment variable.
-				continue
-			}
-			envs = append(envs, e)
-		}
-		files = append([]merkletree.Entry{
-			{
-				Name:         posixWrapperName,
-				Data:         wrapperData,
-				IsExecutable: true,
-			},
-		}, files...)
-	case wrapperWin:
-		logger.Infof("run on win")
-		wn, data, err := wrapperForWindows(ctx)
-		if err != nil {
-			// missing run.exe?
-			return err
-		}
-		// no need to set environment variables??
-		files = []merkletree.Entry{
-			{
-				Name:         wn,
-				Data:         data,
-				IsExecutable: true,
-			},
-		}
-	case wrapperWinInputRootAbsolutePath:
-		logger.Infof("run on win with InputRootAbsolutePath")
-		if relocatableErr != nil && !strings.HasPrefix(strings.ToUpper(r.tree.RootDir()), `C:\`) {
-			// TODO Docker Internal Errors
-			// see also http://b/161274896 Catch specific case where drive letter other than C: specified for input root on Windows
-			logger.Errorf("non relocatable on windows, but absolute path is not C: drive. %s", r.tree.RootDir())
-			return badRequestError{err: fmt.Errorf("non relocatable %v, but root dir is %q. make request relocatable, or use `C:`", relocatableErr, r.tree.RootDir())}
-		}
-		// https://cloud.google.com/remote-build-execution/docs/remote-execution-properties#container_properties
-		r.addPlatformProperty(ctx, "InputRootAbsolutePath", r.tree.RootDir())
-		wn, data, err := wrapperForWindows(ctx)
-		if err != nil {
-			// missing run.exe?
-			return err
-		}
-		// This is necessary for Win emscripten-releases LLVM build, which uses env vars to specify e.g. include
-		// dirs. See crbug.com/1040150.
-		// The build uses abs paths, which is why the env vars are stored here. Whether or not they should also
-		// be in stored in the case of `wrapperWin` is left for future consideration.
-		for _, e := range r.gomaReq.Env {
-			if strings.HasPrefix(e, "INCLUDE=") || strings.HasPrefix(e, "LIB=") {
-				envs = append(envs, e)
-			}
-		}
-		files = []merkletree.Entry{
-			{
-				Name:         wn,
-				Data:         data,
-				IsExecutable: true,
-			},
-		}
-	default:
-		// coding error?
-		return fmt.Errorf("bad wrapper type: %v", wt)
-	}
-
-	// Only the first one is called in the command line via storing
-	// `wrapperPath` in `r.args` later.
-	wrapperPath := ""
-	for i, w := range files {
-		w.Name, err = rootRel(r.filepath, w.Name, cleanCWD, cleanRootDir)
-		if err != nil {
-			// rootRel should not fail with any user input at this point?
-			return err
-		}
-
-		logger.Infof("file (%d) %s => %v", i, w.Name, w.Data.Digest())
-		r.tree.Set(w)
-		if wrapperPath == "" {
-			wrapperPath = w.Name
-		}
-	}
-
-	r.envs = envs
-
-	// if a wrapper exists in cwd, `wrapper` does not have a directory name.
-	// It cannot be callable on POSIX because POSIX do not contain "." in
-	// its PATH.
-	if wrapperPath == posixWrapperName {
-		wrapperPath = "./" + posixWrapperName
-	}
-	if cmdConfig.GetCmdDescriptor().GetCross().GetWindowsCross() {
-		wrapperPath = winpath.ToPosix(wrapperPath)
-	}
-	r.args = append([]string{wrapperPath}, args...)
-
-	err = stats.RecordWithTags(ctx, []tag.Mutator{tag.Upsert(wrapperTypeKey, wt.String())}, wrapperCount.M(1))
-	if err != nil {
-		logger.Errorf("record wrapper-count %s: %v", wt, err)
-	}
-	return nil
-}
-
-func (r *request) maybeApplyHardening(ctx context.Context, wt string, files []merkletree.Entry, wrapperData digest.Data) ([]merkletree.Entry, digest.Data) {
-	logger := log.FromContext(ctx)
-	if f, disable := disableHardening(r.f.DisableHardenings, r.cmdFiles); disable {
-		logger.Infof("run with %s (disable hardening for %v)", wt, f)
-	} else if rand.Float64() < r.f.HardeningRatio {
-		if rand.Float64() < r.f.NsjailRatio {
-			logger.Infof("run with %s + nsjail", wt)
-			wrapperData = digest.Bytes("nsjail-hardening-wrapper-scrpt", nsjailHardeningWrapperScript)
-			// needed for nsjail
-			r.addPlatformProperty(ctx, "dockerPrivileged", "true")
-			files = append(files, merkletree.Entry{
-				Name: "nsjail.cfg",
-				Data: digest.Bytes("nsjail.cfg", nsjailHardeningConfig),
-			})
-		} else {
-			logger.Infof("run with %s + runsc", wt)
-			r.addPlatformProperty(ctx, "dockerRuntime", "runsc")
-		}
-	} else {
-		logger.Infof("run with %s", wt)
-	}
-	return files, wrapperData
-}
-
-func disableHardening(hashes []string, cmdFiles []*cmdpb.FileSpec) (*cmdpb.FileSpec, bool) {
-	for _, h := range hashes {
-		if h == "" {
-			continue
-		}
-		for _, f := range cmdFiles {
-			if f.GetSymlink() != "" {
-				continue
-			}
-			if f.GetHash() == h {
-				return f, true
-			}
-		}
-	}
-	return nil, false
-}
-
-// TODO: refactor with exec/clang.go, exec/clangcl.go?
-
-// buildArgs builds args in RBE from arg0 and req, respecting cmdConfig.
-func buildArgs(ctx context.Context, cmdConfig *cmdpb.Config, arg0 string, req *gomapb.ExecReq) []string {
-	// TODO: need compiler specific handling?
-	args := append([]string{arg0}, req.Arg[1:]...)
-	if cmdConfig.GetCmdDescriptor().GetCross().GetWindowsCross() {
-		args[0] = winpath.ToPosix(args[0])
-		pathFlag := false
-	argLoop:
-		for i := 1; i < len(args); i++ {
-			if pathFlag {
-				args[i] = winpath.ToPosix(args[i])
-				pathFlag = false
-				continue argLoop
-			}
-			// JoinedOrSeparate
-			for _, f := range []string{"/winsysroot", "-winsysroot", "-imsvc", "/imsvc", "-I", "/I"} {
-				if args[i] == f {
-					pathFlag = true
-					continue argLoop
-				}
-				if strings.HasPrefix(args[i], f) {
-					args[i] = f + winpath.ToPosix(strings.TrimPrefix(args[i], f))
-					continue argLoop
-				}
-			}
-			// Joined
-			// Fd is ignored, though
-			for _, f := range []string{"-resource-dir=", "/Fo", "-Fo", "/Fd", "-Fd"} {
-				if strings.HasPrefix(args[i], f) {
-					args[i] = f + winpath.ToPosix(strings.TrimPrefix(args[i], f))
-					continue argLoop
-				}
-			}
-			// TODO: need to handle other args?
-			if strings.HasPrefix(args[i], "-") || strings.HasPrefix(args[i], "/") {
-				continue argLoop
-			}
-			// input file, or arg of some flag?
-			// assume arg of some flag (e.g. -D) won't be windows
-			// absolute path.
-			if winpath.IsAbs(args[i]) {
-				args[i] = winpath.ToPosix(args[i])
-				continue argLoop
-			}
-		}
-		envs := req.Env
-		req.Env = nil
-		for _, e := range envs {
-			switch {
-			case strings.HasPrefix(e, "INCLUDE="):
-				includes := strings.Split(strings.TrimPrefix(e, "INCLUDE="), ";")
-				var imsvcArgs []string
-				for _, inc := range includes {
-					imsvcArgs = append(imsvcArgs, "-imsvc"+winpath.ToPosix(inc))
-				}
-				args = addFlagsToArgs(args, imsvcArgs...)
-			case strings.HasPrefix(e, "LIB="):
-				// unnecessary?
-			default:
-				req.Env = append(req.Env, e)
-			}
-		}
-
-	}
-	if cmdConfig.GetCmdDescriptor().GetCross().GetClangNeedTarget() {
-		args = addTargetIfNotExist(args, req.GetCommandSpec().GetTarget())
-	}
-	return args
-}
-
-func addFlagsToArgs(args []string, flagArgs ...string) []string {
-	for i := 0; i < len(args); i++ {
-		if args[i] == "--" {
-			// we should add flags before --
-			// append to empty slice to avoid overwriting after args[i:]
-			return append(append(append([]string{}, args[:i]...), flagArgs...), args[i:]...)
-		}
-	}
-	// -- is not used
-	return append(args, flagArgs...)
-}
-
-// add target option to args if args doesn't already have target option.
-func addTargetIfNotExist(args []string, target string) []string {
-	// no need to add -target if arg already have it.
-	for _, arg := range args {
-		if arg == "-target" || strings.HasPrefix(arg, "--target=") {
-			return args
-		}
-	}
-	// https://clang.llvm.org/docs/CrossCompilation.html says
-	// `-target <triple>`, but clang --help shows
-	//  --target=<value>        Generate code for the given target
-	// add --target at front, not at end
-	// if it is after "--", it would fail "no such file or directory: `--target=xxx`"
-	// since we're setting default target, it's fine to set flag just after command name.
-	return append([]string{args[0], fmt.Sprintf("--target=%s", target)}, args[1:]...)
-}
-
-func targetFromArgs(args []string) string {
-	for i, arg := range args {
-		if arg == "-target" {
-			if i < len(args)-1 {
-				return args[i+1]
-			}
-			return ""
-		}
-		if strings.HasPrefix(arg, "--target=") {
-			return strings.TrimPrefix(arg, "--target=")
-		}
-	}
-	return ""
-}
-
-type unknownFlagError struct {
-	arg string
-}
-
-func (e unknownFlagError) Error() string {
-	return fmt.Sprintf("unknown flag: %s", e.arg)
-}
-
-// relocatableReq checks args, envs is relocatable, respecting cmdConfig.
-func relocatableReq(ctx context.Context, cmdConfig *cmdpb.Config, filepath clientFilePath, args, envs []string) error {
-	name := cmdConfig.GetCmdDescriptor().GetSelector().GetName()
-	var err error
-	switch name {
-	case "gcc", "g++", "clang", "clang++":
-		err = gccRelocatableReq(filepath, args, envs)
-	case "clang-cl":
-		err = clangclRelocatableReq(filepath, args, envs)
-	case "javac":
-		// 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.
-		err = nil
-	default:
-		// "cl.exe", "clang-tidy"
-		err = fmt.Errorf("no relocatable check for %s", name)
-	}
-	if err != nil {
-		var uerr unknownFlagError
-		if errors.As(err, &uerr) {
-			if serr := stats.RecordWithTags(ctx, []tag.Mutator{tag.Upsert(compilerNameKey, name)}, unknownFlagCount.M(1)); serr != nil {
-				logger := log.FromContext(ctx)
-				logger.Errorf("record unknown-flag %s: %v", name, serr)
-			}
-		}
-	}
-	return err
-}
-
-// outputs gets output filenames from gomaReq.
-// If either expected_output_files or expected_output_dirs is specified,
-// expected_output_files is used.
-// Otherwise, it's calculated from args.
-func outputs(ctx context.Context, cmdConfig *cmdpb.Config, gomaReq *gomapb.ExecReq) []string {
-	if len(gomaReq.ExpectedOutputFiles) > 0 || len(gomaReq.ExpectedOutputDirs) > 0 {
-		return gomaReq.GetExpectedOutputFiles()
-	}
-
-	args := gomaReq.Arg
-	switch name := cmdConfig.GetCmdDescriptor().GetSelector().GetName(); name {
-	case "gcc", "g++", "clang", "clang++":
-		return gccOutputs(args)
-	case "clang-cl":
-		return clangclOutputs(args)
-	default:
-		// "cl.exe", "javac", "clang-tidy"
-		return nil
-	}
-}
-
-// outputDirs gets output dirnames from gomaReq.
-// If either expected_output_files or expected_output_dirs is specified,
-// expected_output_dirs is used.
-// Otherwise, it's calculated from args.
-func outputDirs(ctx context.Context, cmdConfig *cmdpb.Config, gomaReq *gomapb.ExecReq) []string {
-	if len(gomaReq.ExpectedOutputFiles) > 0 || len(gomaReq.ExpectedOutputDirs) > 0 {
-		return gomaReq.GetExpectedOutputDirs()
-	}
-
-	args := gomaReq.Arg
-	switch cmdConfig.GetCmdDescriptor().GetSelector().GetName() {
-	case "javac":
-		return javacOutputDirs(args)
-	default:
-		return nil
-	}
-}
-
-func (r *request) setupNewAction(ctx context.Context) {
-	if r.err != nil {
-		return
-	}
-	command, err := r.newCommand(ctx)
-	if err != nil {
-		r.err = err
-		return
-	}
-
-	// we'll run  wrapper script that chdir, so don't set chdir here.
-	// see newWrapperScript.
-	// TODO: set command.WorkingDirectory
-	data, err := digest.Proto(command)
-	if err != nil {
-		r.err = err
-		return
-	}
-	logger := log.FromContext(ctx)
-	logger.Infof("command digest: %v", data.Digest())
-
-	r.digestStore.Set(data)
-	r.action.CommandDigest = data.Digest()
-
-	data, err = digest.Proto(r.action)
-	if err != nil {
-		r.err = err
-		return
-	}
-	r.digestStore.Set(data)
-	logger.Infof("action digest: %v %s", data.Digest(), r.action)
-	r.actionDigest = data.Digest()
-}
-
-func (r *request) newCommand(ctx context.Context) (*rpb.Command, error) {
-	logger := log.FromContext(ctx)
-
-	envVars := createEnvVars(ctx, r.envs)
-	sort.Slice(r.platform.Properties, func(i, j int) bool {
-		return r.platform.Properties[i].Name < r.platform.Properties[j].Name
-	})
-	command := &rpb.Command{
-		Arguments:            r.args,
-		EnvironmentVariables: envVars,
-		Platform:             r.platform,
-	}
-
-	logger.Debugf("setup for outputs: %v", r.outputs)
-	cleanCWD := r.filepath.Clean(r.gomaReq.GetCwd())
-	cleanRootDir := r.filepath.Clean(r.tree.RootDir())
-	// set output files from command line flags.
-	for _, output := range r.outputs {
-		rel, err := rootRel(r.filepath, output, cleanCWD, cleanRootDir)
-		if err != nil {
-			return nil, fmt.Errorf("output %s: %v", output, err)
-		}
-		if r.cmdConfig.GetCmdDescriptor().GetCross().GetWindowsCross() {
-			rel = winpath.ToPosix(rel)
-		}
-		command.OutputFiles = append(command.OutputFiles, rel)
-	}
-	sort.Strings(command.OutputFiles)
-
-	logger.Debugf("setup for output dirs: %v", r.outputDirs)
-	// set output dirs from command line flags.
-	for _, output := range r.outputDirs {
-		rel, err := rootRel(r.filepath, output, cleanCWD, cleanRootDir)
-		if err != nil {
-			return nil, fmt.Errorf("output dir %s: %v", output, err)
-		}
-		if r.cmdConfig.GetCmdDescriptor().GetCross().GetWindowsCross() {
-			rel = winpath.ToPosix(rel)
-		}
-		command.OutputDirectories = append(command.OutputDirectories, rel)
-	}
-	sort.Strings(command.OutputDirectories)
-
-	return command, nil
-}
-
-func (r *request) checkCache(ctx context.Context) (*rpb.ActionResult, bool) {
-	if r.err != nil {
-		// no need to ask to execute.
-		return nil, true
-	}
-	logger := log.FromContext(ctx)
-	if skipCacheLookup(r.gomaReq) {
-		logger.Infof("store_only; skip cache lookup")
-		return nil, false
-	}
-	resp, err := r.client.Cache().GetActionResult(ctx, &rpb.GetActionResultRequest{
-		InstanceName: r.instanceName(),
-		ActionDigest: r.actionDigest,
-	})
-	if err != nil {
-		switch status.Code(err) {
-		case codes.NotFound:
-			logger.Infof("no cached action %v: %v", r.actionDigest, err)
-		case codes.Unavailable, codes.Canceled, codes.Aborted:
-			logger.Warnf("get action result %v: %v", r.actionDigest, err)
-		default:
-			logger.Errorf("get action result %v: %v", r.actionDigest, err)
-		}
-		return nil, false
-	}
-	return resp, true
-}
-
-func (r *request) missingBlobs(ctx context.Context) ([]*rpb.Digest, error) {
-	if r.err != nil {
-		return nil, r.err
-	}
-	var blobs []*rpb.Digest
-	err := rpc.Retry{}.Do(ctx, func() error {
-		var err error
-		blobs, err = r.cas.Missing(ctx, r.instanceName(), r.digestStore.List())
-		return fixRBEInternalError(err)
-	})
-	if err != nil {
-		r.err = err
-		return nil, err
-	}
-	return blobs, nil
-}
-
-func inputForDigest(ds *digest.Store, d *rpb.Digest) (string, error) {
-	src, ok := ds.GetSource(d)
-	if !ok {
-		return "", fmt.Errorf("not found for %s", d)
-	}
-	idd, ok := src.(inputDigestData)
-	if !ok {
-		return "", fmt.Errorf("not input file for %s", d)
-	}
-	return idd.filename, nil
-}
-
-type byInputFilenames struct {
-	order map[string]int
-	resp  *gomapb.ExecResp
-}
-
-func (b byInputFilenames) Len() int { return len(b.resp.MissingInput) }
-func (b byInputFilenames) Swap(i, j int) {
-	b.resp.MissingInput[i], b.resp.MissingInput[j] = b.resp.MissingInput[j], b.resp.MissingInput[i]
-	b.resp.MissingReason[i], b.resp.MissingReason[j] = b.resp.MissingReason[j], b.resp.MissingReason[i]
-}
-
-func (b byInputFilenames) Less(i, j int) bool {
-	io := b.order[b.resp.MissingInput[i]]
-	jo := b.order[b.resp.MissingInput[j]]
-	return io < jo
-}
-
-func sortMissing(inputs []*gomapb.ExecReq_Input, resp *gomapb.ExecResp) {
-	m := make(map[string]int)
-	for i, input := range inputs {
-		m[input.GetFilename()] = i
-	}
-	sort.Sort(byInputFilenames{
-		order: m,
-		resp:  resp,
-	})
-}
-
-// thinOutMissing thins out missint inputs if it is more than limit.
-// Note: sortMissing should be called after this to preserve the file name order.
-func thinOutMissing(resp *gomapb.ExecResp, limit int) {
-	if limit == 0 || len(resp.MissingInput) < limit { // no need to thin out.
-		return
-	}
-	rand.Shuffle(len(resp.MissingInput), func(i, j int) {
-		resp.MissingInput[i], resp.MissingInput[j] = resp.MissingInput[j], resp.MissingInput[i]
-	})
-	resp.MissingInput = resp.MissingInput[:limit]
-}
-
-func logFileList(logger log.Logger, msg string, files []string) {
-	s := fmt.Sprintf("%q", files)
-	const logLineThreshold = 95 * 1024
-	if len(s) < logLineThreshold {
-		logger.Infof("%s %s", msg, s)
-		return
-	}
-	logger.Warnf("too many %s %d", msg, len(files))
-	var b strings.Builder
-	var i int
-	for len(files) > 0 {
-		if b.Len() > 0 {
-			fmt.Fprintf(&b, " ")
-		}
-		s, files = files[0], files[1:]
-		fmt.Fprintf(&b, "%q", s)
-		if b.Len() > logLineThreshold {
-			logger.Infof("%s %d: [%s]", msg, i, b.String())
-			i++
-			b.Reset()
-		}
-	}
-	if b.Len() > 0 {
-		logger.Infof("%s %d: [%s]", msg, i, b)
-	}
-}
-
-func (r *request) uploadBlobs(ctx context.Context, blobs []*rpb.Digest) (*gomapb.ExecResp, error) {
-	if r.err != nil {
-		return nil, r.err
-	}
-	err := r.cas.Upload(ctx, r.instanceName(), r.f.CASBlobLookupSema, blobs...)
-	if err != nil {
-		if missing, ok := err.(cas.MissingError); ok {
-			logger := log.FromContext(ctx)
-			logger.Infof("failed to upload blobs %s", missing.Blobs)
-			var missingInputs []string
-			var missingReason []string
-			for _, b := range missing.Blobs {
-				fname, err := inputForDigest(r.digestStore, b.Digest)
-				if err != nil {
-					logger.Warnf("unknown input for %s: %v", b.Digest, err)
-					continue
-				}
-				missingInputs = append(missingInputs, fname)
-				missingReason = append(missingReason, b.Err.Error())
-			}
-			if len(missingInputs) > 0 {
-				r.gomaResp.MissingInput = missingInputs
-				r.gomaResp.MissingReason = missingReason
-				thinOutMissing(r.gomaResp, r.f.MissingInputLimit)
-				sortMissing(r.gomaReq.Input, r.gomaResp)
-				logFileList(logger, "missing inputs", r.gomaResp.MissingInput)
-				return r.gomaResp, nil
-			}
-			// failed to upload non-input, so no need to report
-			// missing input to users.
-			// handle it as grpc error.
-		}
-		r.err = err
-	}
-	return nil, err
-}
-
-func (r *request) executeAction(ctx context.Context) (*rpb.ExecuteResponse, error) {
-	if r.err != nil {
-		return nil, r.Err()
-	}
-	_, resp, err := ExecuteAndWait(ctx, r.client, &rpb.ExecuteRequest{
-		InstanceName:    r.instanceName(),
-		SkipCacheLookup: skipCacheLookup(r.gomaReq),
-		ActionDigest:    r.actionDigest,
-		// ExecutionPolicy
-		// ResultsCachePolicy
-	})
-	if err != nil {
-		r.err = err
-		return nil, r.Err()
-	}
-	return resp, nil
-}
-
-func timestampSub(ctx context.Context, t1, t2 *tspb.Timestamp) time.Duration {
-	time1 := t1.AsTime()
-	time2 := t2.AsTime()
-	return time1.Sub(time2)
-}
-
-func (r *request) newResp(ctx context.Context, eresp *rpb.ExecuteResponse, cached bool) (*gomapb.ExecResp, error) {
-	logger := log.FromContext(ctx)
-	if r.err != nil {
-		return nil, r.Err()
-	}
-	logger.Debugf("response %v cached=%t", eresp, cached)
-	r.gomaResp.CacheKey = proto.String(r.actionDigest.String())
-	switch {
-	case eresp.CachedResult:
-		r.gomaResp.CacheHit = gomapb.ExecResp_STORAGE_CACHE.Enum()
-	case cached:
-		r.gomaResp.CacheHit = gomapb.ExecResp_MEM_CACHE.Enum()
-	default:
-		r.gomaResp.CacheHit = gomapb.ExecResp_NO_CACHE.Enum()
-	}
-	if st := eresp.GetStatus(); st.GetCode() != 0 {
-		logger.Errorf("execute status error: %v", st)
-		s := status.FromProto(st)
-		r.gomaResp.ErrorMessage = append(r.gomaResp.ErrorMessage, fmt.Sprintf("Execute error: %s", s.Code()))
-		logger.Errorf("resp %v", r.gomaResp)
-		return r.gomaResp, nil
-	}
-	if eresp.Result == nil {
-		r.gomaResp.ErrorMessage = append(r.gomaResp.ErrorMessage, "unexpected response message")
-		logger.Errorf("resp %v", r.gomaResp)
-		return r.gomaResp, nil
-	}
-	md := eresp.Result.GetExecutionMetadata()
-	queueTime := timestampSub(ctx, md.GetWorkerStartTimestamp(), md.GetQueuedTimestamp())
-	workerTime := timestampSub(ctx, md.GetWorkerCompletedTimestamp(), md.GetWorkerStartTimestamp())
-	inputTime := timestampSub(ctx, md.GetInputFetchCompletedTimestamp(), md.GetInputFetchStartTimestamp())
-	execTime := timestampSub(ctx, md.GetExecutionCompletedTimestamp(), md.GetExecutionStartTimestamp())
-	outputTime := timestampSub(ctx, md.GetOutputUploadCompletedTimestamp(), md.GetOutputUploadStartTimestamp())
-	osFamily := platformOSFamily(r.platform)
-	dockerRuntime := platformDockerRuntime(r.platform)
-	crossCompileType := crossCompileType(r.cmdConfig.GetCmdDescriptor().GetCross())
-	logger.Infof("exit=%d cache=%s : exec on %q[%s, %s, cross:%s, target=%s] queue=%s worker=%s input=%s exec=%s output=%s",
-		eresp.Result.GetExitCode(),
-		r.gomaResp.GetCacheHit(),
-		md.GetWorker(),
-		osFamily,
-		dockerRuntime,
-		crossCompileType,
-		r.crossTarget,
-		queueTime,
-		workerTime,
-		inputTime,
-		execTime,
-		outputTime)
-	tags := []tag.Mutator{
-		// exit_code=159 is seccomp violation.
-		tag.Upsert(rbeExitKey, fmt.Sprintf("%d", eresp.Result.GetExitCode())),
-		tag.Upsert(rbeCacheKey, r.gomaResp.GetCacheHit().String()),
-		tag.Upsert(rbePlatformOSFamilyKey, osFamily),
-		tag.Upsert(rbePlatformDockerRuntimeKey, dockerRuntime),
-		tag.Upsert(rbeCrossKey, crossCompileType),
-	}
-	stats.RecordWithTags(ctx, tags, rbeQueueTime.M(float64(queueTime.Nanoseconds())/1e6))
-	stats.RecordWithTags(ctx, tags, rbeWorkerTime.M(float64(workerTime.Nanoseconds())/1e6))
-	stats.RecordWithTags(ctx, tags, rbeInputTime.M(float64(inputTime.Nanoseconds())/1e6))
-	stats.RecordWithTags(ctx, tags, rbeExecTime.M(float64(execTime.Nanoseconds())/1e6))
-	stats.RecordWithTags(ctx, tags, rbeOutputTime.M(float64(outputTime.Nanoseconds())/1e6))
-
-	r.gomaResp.ExecutionStats = &gomapb.ExecutionStats{
-		ExecutionStartTimestamp:     md.GetExecutionStartTimestamp(),
-		ExecutionCompletedTimestamp: md.GetExecutionCompletedTimestamp(),
-	}
-	gout := gomaOutput{
-		gomaResp: r.gomaResp,
-		bs:       r.client.ByteStream(),
-		instance: r.instanceName(),
-		gomaFile: r.f.GomaFile,
-	}
-	// gomaOutput should return err for codes.Unauthenticated,
-	// instead of setting ErrorMessage in r.gomaResp,
-	// so it returns to caller (i.e. frontend), and retry with new
-	// refreshed oauth2 access token.
-	for _, f := range []func(context.Context, *rpb.ExecuteResponse) error{
-		gout.stdoutData,
-		gout.stderrData,
-	} {
-		err := f(ctx, eresp)
-		if status.Code(err) == codes.Unauthenticated && r.err == nil {
-			r.err = err
-			return r.gomaResp, r.Err()
-		}
-	}
-
-	if len(r.gomaResp.Result.StdoutBuffer) > 0 {
-		// docker failure would be error of goma server, not users.
-		// so make it internal error, rather than command execution error.
-		// http://b/80272874
-		const dockerErrorResponse = "docker: Error response from daemon: oci runtime error:"
-		if eresp.Result.ExitCode == 127 &&
-			bytes.Contains(r.gomaResp.Result.StdoutBuffer, []byte(dockerErrorResponse)) {
-			logger.Errorf("docker error response %s", shortLogMsg(r.gomaResp.Result.StdoutBuffer))
-			return r.gomaResp, status.Errorf(codes.Internal, "docker error: %s", string(r.gomaResp.Result.StdoutBuffer))
-		}
-
-		if eresp.Result.ExitCode != 0 {
-			logLLVMError(logger, "stdout", r.gomaResp.Result.StdoutBuffer)
-		}
-		logger.Infof("stdout %s", shortLogMsg(r.gomaResp.Result.StdoutBuffer))
-	}
-	if len(r.gomaResp.Result.StderrBuffer) > 0 {
-		if eresp.Result.ExitCode != 0 {
-			logLLVMError(logger, "stderr", r.gomaResp.Result.StderrBuffer)
-		}
-		logger.Infof("stderr %s", shortLogMsg(r.gomaResp.Result.StderrBuffer))
-	}
-
-	for _, output := range eresp.Result.OutputFiles {
-		if r.err != nil {
-			break
-		}
-		// output.Path should not be absolute, but relative to root dir.
-		// convert it to fname, which is cwd relative.
-		fname, err := r.filepath.Rel(r.gomaReq.GetCwd(), r.filepath.Join(r.tree.RootDir(), output.Path))
-		if err != nil {
-			r.gomaResp.ErrorMessage = append(r.gomaResp.ErrorMessage, fmt.Sprintf("output path %s: %v", output.Path, err))
-			continue
-		}
-		err = gout.outputFile(ctx, fname, output)
-		if err != nil && r.err == nil {
-			r.err = err
-			return r.gomaResp, r.Err()
-		}
-	}
-	for _, output := range eresp.Result.OutputDirectories {
-		if r.err != nil {
-			break
-		}
-		// output.Path should not be absolute, but relative to root dir.
-		// convert it to fname, which is cwd relative.
-		fname, err := r.filepath.Rel(r.gomaReq.GetCwd(), r.filepath.Join(r.tree.RootDir(), output.Path))
-		if err != nil {
-			r.gomaResp.ErrorMessage = append(r.gomaResp.ErrorMessage, fmt.Sprintf("output path %s: %v", output.Path, err))
-			continue
-		}
-		err = gout.outputDirectory(ctx, r.filepath, fname, output, r.f.OutputFileSema)
-		if err != nil && r.err == nil {
-			r.err = err
-			return r.gomaResp, r.Err()
-		}
-	}
-	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()
-}
-
-func platformOSFamily(p *rpb.Platform) string {
-	for _, p := range p.Properties {
-		if p.Name == "OSFamily" {
-			return p.Value
-		}
-	}
-	return "unspecified"
-}
-
-func platformDockerRuntime(p *rpb.Platform) string {
-	priv := false
-	runAsRoot := false
-	for _, p := range p.Properties {
-		switch p.Name {
-		case "dockerRuntime":
-			return p.Value
-		case "dockerPrivileged":
-			priv = p.Value == "true"
-		case "dockerRunAsRoot":
-			runAsRoot = p.Value == "true"
-		}
-	}
-	switch {
-	case priv && runAsRoot:
-		return "nsjail-chroot"
-	case priv:
-		return "nsjail"
-	}
-	return "default"
-}
-
-func crossCompileType(cross *cmdpb.CmdDescriptor_Cross) string {
-	switch {
-	case cross.GetWindowsCross():
-		return "win"
-	case cross.GetClangNeedTarget():
-		return "need-target"
-	}
-	return "no"
-}
-
-func shortLogMsg(msg []byte) string {
-	if len(msg) <= 1024 {
-		return string(msg)
-	}
-	var b strings.Builder
-	b.Write(msg[:512])
-	fmt.Fprint(&b, "...")
-	b.Write(msg[len(msg)-512:])
-	return b.String()
-}
-
-// logLLVMError records LLVM ERROR.
-// http://b/145177862
-func logLLVMError(logger log.Logger, id string, msg []byte) {
-	llvmErrorMsg, ok := extractLLVMError(msg)
-	if !ok {
-		return
-	}
-	logger.Errorf("%s: %s", id, llvmErrorMsg)
-}
-
-func extractLLVMError(msg []byte) ([]byte, bool) {
-	const llvmError = "LLVM ERROR:"
-	i := bytes.Index(msg, []byte(llvmError))
-	if i < 0 {
-		return nil, false
-	}
-	llvmErrorMsg := msg[i:]
-	i = bytes.IndexAny(llvmErrorMsg, "\r\n")
-	if i >= 0 {
-		llvmErrorMsg = llvmErrorMsg[:i]
-	}
-	return llvmErrorMsg, true
-}
diff --git a/remoteexec/exec_oss.go b/remoteexec/exec_oss.go
deleted file mode 100644
index 90d7c2c..0000000
--- a/remoteexec/exec_oss.go
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2019 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.
-
-
-
-package remoteexec
-
-import (
-	"context"
-	"errors"
-
-	gomapb "go.chromium.org/goma/server/proto/api"
-	"go.chromium.org/goma/server/remoteexec/digest"
-)
-
-// adjustExecReq adjust req if needed.
-func adjustExecReq(req *gomapb.ExecReq) {
-}
-
-// wrapperForWindows. not yet supported.
-func wrapperForWindows(ctx context.Context) (string, digest.Data, error) {
-	return "", nil, errors.New("not yet supported")
-}
diff --git a/remoteexec/exec_test.go b/remoteexec/exec_test.go
deleted file mode 100644
index a744876..0000000
--- a/remoteexec/exec_test.go
+++ /dev/null
@@ -1,579 +0,0 @@
-// Copyright 2019 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.
-
-package remoteexec
-
-import (
-	"bytes"
-	"context"
-	"errors"
-	"fmt"
-	"sort"
-	"sync"
-	"testing"
-
-	"github.com/google/go-cmp/cmp"
-	"google.golang.org/protobuf/proto"
-
-	"go.chromium.org/goma/server/command/descriptor/winpath"
-	"go.chromium.org/goma/server/hash"
-	"go.chromium.org/goma/server/log"
-	gomapb "go.chromium.org/goma/server/proto/api"
-	"go.chromium.org/goma/server/remoteexec/digest"
-	"go.chromium.org/goma/server/remoteexec/merkletree"
-)
-
-func TestDedupInputs(t *testing.T) {
-	toExecReq_Input := func(filenames []string) []*gomapb.ExecReq_Input {
-		var r []*gomapb.ExecReq_Input
-		for _, fname := range filenames {
-			r = append(r, &gomapb.ExecReq_Input{
-				Filename: proto.String(fname),
-			})
-		}
-		return r
-	}
-	fromExecReq_Input := func(inputs []*gomapb.ExecReq_Input) []string {
-		var r []string
-		for _, input := range inputs {
-			r = append(r, input.GetFilename())
-		}
-		return r
-	}
-
-	for _, tc := range []struct {
-		desc   string
-		cwd    string
-		inputs []string
-		want   []string
-	}{
-		{
-			desc: "no-dup",
-			cwd:  `c:\b\w`,
-			inputs: []string{
-				"foo",
-				"bar",
-			},
-			want: []string{
-				"foo",
-				"bar",
-			},
-		},
-		{
-			desc: "dup",
-			cwd:  `c:\b\w`,
-			inputs: []string{
-				"Foo",
-				"foo",
-			},
-			want: []string{
-				"Foo",
-			},
-		},
-		{
-			desc: "dup-pref",
-			cwd:  `c:\b\w`,
-			inputs: []string{
-				"foo",
-				"Foo",
-			},
-			want: []string{
-				"Foo",
-			},
-		},
-		{
-			desc: "short-path",
-			cwd:  `c:\b\w`,
-			inputs: []string{
-				"foo",
-				`c:\b\w\Foo`,
-			},
-			want: []string{
-				"foo",
-			},
-		},
-	} {
-		t.Run(tc.desc, func(t *testing.T) {
-			got := dedupInputs(winpath.FilePath{}, tc.cwd, toExecReq_Input(tc.inputs))
-			if diff := cmp.Diff(tc.want, fromExecReq_Input(got)); diff != "" {
-				t.Errorf("dedupInputs: diff -want +got:\n%s", diff)
-			}
-		})
-	}
-}
-
-func TestSortMissing(t *testing.T) {
-	inputs := []*gomapb.ExecReq_Input{
-		{
-			Filename: proto.String("../src/hello.cc"),
-			HashKey:  proto.String("hash-hello.cc"),
-		},
-		{
-			Filename: proto.String("../include/base.h"),
-			HashKey:  proto.String("hash-base.h"),
-		},
-		{
-			Filename: proto.String("../include/hello.h"),
-			HashKey:  proto.String("hash-hello.h"),
-		},
-	}
-
-	resp := &gomapb.ExecResp{
-		MissingInput: []string{
-			"../include/hello.h",
-			"../src/hello.cc",
-		},
-		MissingReason: []string{
-			"missing-hello.h",
-			"missing-hello.cc",
-		},
-	}
-
-	sortMissing(inputs, resp)
-	want := &gomapb.ExecResp{
-		MissingInput: []string{
-			"../src/hello.cc",
-			"../include/hello.h",
-		},
-		MissingReason: []string{
-			"missing-hello.cc",
-			"missing-hello.h",
-		},
-	}
-	if !proto.Equal(resp, want) {
-		t.Errorf("sortMissing: %s != %s", resp, want)
-	}
-
-	resp = proto.Clone(want).(*gomapb.ExecResp)
-	sortMissing(inputs, resp)
-	if !proto.Equal(resp, want) {
-		t.Errorf("sortMissing (stable): %s != %s", resp, want)
-	}
-}
-
-func TestExtractLLVMError(t *testing.T) {
-	// from fdf0c2b3d3d633787324c90ff55404cd0f45fa83cd48e033c332961c9e13e7b5-277800
-	// in http://b/145177862
-	msg := []byte(`Note: including file: ../..\content/renderer/render_frame_impl.h
-Note: including file:  ../../buildtools/third_party/libc++/trunk/include\stddef.h
-Note: including file:   ../../buildtools/third_party/libc++/trunk/include\__config
-Note: including file:  ../..\base/native_library.h
-Note: including file:  ../..\ppapi/c/ppb_core.h
-Note: including file:  ../..\ppapi/shared_impl/ppapi_permissions.h
-Note: including file: ../..\base/debug/invalid_access_win.h
-Note: including file: ../..\base/process/kill.h
-LLVM ERROR: out of memory
-Stack dump:
-0.	Program arguments: ..\..\third_party\llvm-build\Release+Asserts\bin\clang-cl.exe -cc1 -triple i386-pc-windows-msvc19.16.0 -emit-obj -disable-free -disable-llvm-verifier -discard-value-names -main-file-name render_frame_impl.cc -mrelocation-model static -mthread-model posix -fmerge-all-constants -mframe-pointer=all -relaxed-aliasing -fmath-errno -fno-rounding-math -masm-verbose -mconstructor-aliases -target-cpu pentium4 -mllvm -x86-asm-syntax=intel -D_MT -flto-visibility-public-std --dependent-lib=libcmt --dependent-lib=oldnames --show-includes -fno-rtti-data -stack-protector 2 -fms-volatile -fdiagnostics-format msvc -cfguard-no-checks -gcodeview -debug-info-kind=line-tables-only -ffunction-sections -fdata-sections -nostdsysteminc -resource-dir ..\..\third_party\llvm-build\Release+Asserts\lib\clang\10.0.0 -D USE_AURA=1 -D CR_CLANG_REVISION="n331734-e84b7a5f-1" -D _HAS_NODISCARD -D _LIBCPP_ABI_UNSTABLE -D _LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS -D _LIBCPP_ENABLE_NODISCARD -D _LIBCPP_NO_AUTO_LINK -D __STD_C -D _CRT_RAND_S -D _CRT_SECURE_NO_DEPRECATE -D _SCL_SECURE_NO_DEPRECATE -D _ATL_NO_OPENGL -D _WINDOWS -D CERT_CHAIN_PARA_HAS_EXTRA_FIELDS -D PSAPI_VERSION=2 -D WIN32 -D _SECURE_ATL -D _USING_V110_SDK71_ -D WINAPI_FAMILY=WINAPI_FAMILY_DESKTOP_APP -D WIN32_LEAN_AND_MEAN -D NOMINMAX -D _UNICODE -D UNICODE -D NTDDI_VERSION=NTDDI_WIN10_RS2 -D _WIN32_WINNT=0x0A00 -D WINVER=0x0A00 -D NDEBUG -D NVALGRIND -D DYNAMIC_ANNOTATIONS_ENABLED=0 -D CONTENT_IMPLEMENTATION -D ENABLE_IPC_FUZZER -D WEBP_EXTERN=extern -D USE_EGL -D _WTL_NO_AUTOMATIC_NAMESPACE -D U_USING_ICU_NAMESPACE=0 -D U_ENABLE_DYLOAD=0 -D USE_CHROMIUM_ICU=1 -D U_STATIC_IMPLEMENTATION -D ICU_UTIL_DATA_IMPL=ICU_UTIL_DATA_FILE -D UCHAR_TYPE=wchar_t -D GOOGLE_PROTOBUF_NO_RTTI -D GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER -D WEBRTC_NON_STATIC_TRACE_EVENT_HANDLERS=0 -D WEBRTC_CHROMIUM_BUILD -D WEBRTC_WIN -D ABSL_ALLOCATOR_NOTHROW=1 -D WEBRTC_USE_BUILTIN_ISAC_FIX=0 -D WEBRTC_USE_BUILTIN_ISAC_FLOAT=1 -D HAVE_SCTP -D NO_MAIN_THREAD_WRAPPING -D SK_HAS_PNG_LIBRARY -D SK_HAS_WEBP_LIBRARY -D SK_USER_CONFIG_HEADER="../../skia/config/SkUserConfig.h" -D SK_GL -D SK_HAS_JPEG_LIBRARY -D SK_HAS_WUFFS_LIBRARY -D SK_SUPPORT_GPU=1 -D SK_GPU_WORKAROUNDS_HEADER="gpu/config/gpu_driver_bug_workaround_autogen.h" -D GR_GL_FUNCTION_TYPE=__stdcall -D LEVELDB_PLATFORM_CHROMIUM=1 -D LEVELDB_PLATFORM_CHROMIUM=1 -D V8_USE_EXTERNAL_STARTUP_DATA -D V8_31BIT_SMIS_ON_64BIT_ARCH -D V8_DEPRECATION_WARNINGS -D SUPPORT_WEBGL2_COMPUTE_CONTEXT=1 -D WTF_USE_WEBAUDIO_PFFFT=1 -D V8_31BIT_SMIS_ON_64BIT_ARCH -D V8_DEPRECATION_WARNINGS -D AUDIO_PROCESSING_IN_AUDIO_SERVICE -D SQLITE_ENABLE_BATCH_ATOMIC_WRITE -D SQLITE_ENABLE_FTS3 -D SQLITE_DISABLE_FTS3_UNICODE -D SQLITE_DISABLE_FTS4_DEFERRED -D SQLITE_ENABLE_ICU -D SQLITE_SECURE_DELETE -D SQLITE_THREADSAFE=1 -D SQLITE_MAX_WORKER_THREADS=0 -D SQLITE_MAX_MMAP_SIZE=268435456 -D SQLITE_DEFAULT_FILE_PERMISSIONS=0600 -D SQLITE_DEFAULT_MEMSTATUS=1 -D SQLITE_DEFAULT_PAGE_SIZE=4096 -D SQLITE_DEFAULT_PCACHE_INITSZ=0 -D SQLITE_LIKE_DOESNT_MATCH_BLOBS -D SQLITE_OMIT_DEPRECATED -D SQLITE_OMIT_PROGRESS_CALLBACK -D SQLITE_OMIT_SHARED_CACHE -D SQLITE_USE_ALLOCA -D SQLITE_OMIT_ANALYZE -D SQLITE_OMIT_AUTOINIT -D SQLITE_OMIT_AUTORESET -D SQLITE_OMIT_COMPILEOPTION_DIAGS -D SQLITE_OMIT_COMPLETE -D SQLITE_OMIT_DECLTYPE -D SQLITE_OMIT_EXPLAIN -D SQLITE_OMIT_GET_TABLE -D SQLITE_OMIT_LOAD_EXTENSION -D SQLITE_DEFAULT_LOOKASIDE=0,0 -D SQLITE_OMIT_LOOKASIDE -D SQLITE_OMIT_TCL_VARIABLE -D SQLITE_OMIT_REINDEX -D SQLITE_OMIT_TRACE -D SQLITE_OMIT_UPSERT -D SQLITE_OMIT_WINDOWFUNC -D SQLITE_HAVE_ISNAN -D SQLITE_TEMP_STORE=3 -D SQLITE_ENABLE_LOCKING_STYLE=0 -I ../.. -I gen -I ../../third_party/libyuv/include -I ../../third_party/jsoncpp/source/include -I ../../third_party/jsoncpp/generated -I ../../third_party/libwebp/src -I ../../third_party/wtl/include -I ../../third_party/khronos -I ../../gpu -I gen/third_party/dawn/src/include -I ../../third_party/dawn/src/include -I ../../third_party/boringssl/src/include -I ../../third_party/ced/src -I ../../third_party/icu/source/common -I ../../third_party/icu/source/i18n -I ../../third_party/protobuf/src -I gen/protoc_out -I ../../third_party/protobuf/src -I ../../third_party/webrtc_overrides -I ../../third_party/webrtc -I gen/third_party/webrtc -I ../../third_party/abseil-cpp -I ../../third_party/skia -I ../../third_party/wuffs/src/release/c -I ../../third_party/libwebm/source -I ../../third_party/leveldatabase -I ../../third_party/leveldatabase/src -I ../../third_party/leveldatabase/src/include -I ../../v8/include -I gen/v8/include -I gen/third_party/metrics_proto -I ../../third_party/mesa_headers -I ../../v8/include -I gen/v8/include -I ../../third_party/perfetto/include -I gen/third_party/perfetto/build_config -I gen/third_party/perfetto -I gen/third_party/perfetto -I gen/third_party/perfetto -I gen/third_party/perfetto -I gen/third_party/perfetto -I gen/third_party/perfetto -I gen/third_party/perfetto -I gen/third_party/perfetto -I gen/third_party/perfetto -I gen/third_party/perfetto -I ../../third_party/libvpx/source/libvpx -I ../../third_party/opus/src/include -D __DATE__= -D __TIME__= -D __TIMESTAMP__= -I ../../buildtools/third_party/libc++/trunk/include -internal-isystem ..\..\third_party\llvm-build\Release+Asserts\lib\clang\10.0.0\include -internal-isystem ..\..\third_party\depot_tools\win_toolchain\vs_files\8f58c55897a3282ed617055775a77ec3db771b88\win_sdk\Include\10.0.18362.0\um -internal-isystem ..\..\third_party\depot_tools\win_toolchain\vs_files\8f58c55897a3282ed617055775a77ec3db771b88\win_sdk\Include\10.0.18362.0\shared -internal-isystem ..\..\third_party\depot_tools\win_toolchain\vs_files\8f58c55897a3282ed617055775a77ec3db771b88\win_sdk\Include\10.0.18362.0\winrt -internal-isystem ..\..\third_party\depot_tools\win_toolchain\vs_files\8f58c55897a3282ed617055775a77ec3db771b88\win_sdk\Include\10.0.18362.0\ucrt -internal-isystem ..\..\third_party\depot_tools\win_toolchain\vs_files\8f58c55897a3282ed617055775a77ec3db771b88\VC\Tools\MSVC\14.23.28105\include -internal-isystem ..\..\third_party\depot_tools\win_toolchain\vs_files\8f58c55897a3282ed617055775a77ec3db771b88\VC\Tools\MSVC\14.23.28105\atlmfc\include -Os -Wno-builtin-macro-redefined -WCL4 -Wimplicit-fallthrough -Wthread-safety -Wextra-semi -Werror -Wno-unused-parameter -Wno-deprecated-declarations -Wno-missing-field-initializers -Wno-unused-parameter -Wno-c++11-narrowing -Wno-unneeded-internal-declaration -Wno-undefined-var-template -Wno-nonportable-include-path -Wno-ignored-pragma-optimize -Wno-implicit-int-float-conversion -Wno-c99-designator -Wno-final-dtor-non-final-class -Wno-sizeof-array-div -Wno-bitwise-conditional-parentheses -Wno-builtin-assume-aligned-alignment -Wno-tautological-bitwise-compare -Wheader-hygiene -Wstring-conversion -Wtautological-overlap-compare -Wshadow -Wexit-time-destructors -Wno-deprecated-declarations -Wno-deprecated-declarations -fdeprecated-macro -fdebug-compilation-dir C:\botcode\w\out\Release -ferror-limit 19 -fmessage-length 0 -fno-use-cxa-atexit -fms-extensions -fms-compatibility -fms-compatibility-version=19.16 -std=c++14 -finline-functions -fobjc-runtime=gcc -fdiagnostics-show-option -fcolor-diagnostics -vectorize-loops -vectorize-slp -mllvm -instcombine-lower-dbg-declare=0 -fdebug-compilation-dir . -add-plugin find-bad-constructs -add-plugin blink-gc-plugin -plugin-arg-blink-gc-plugin no-gc-finalized -fcomplete-member-pointers -faddrsig -o obj/content/renderer/renderer/render_frame_impl.obj -x c++ ../../content/renderer/render_frame_impl.cc
-1.	<eof> parser at end of file
-2.	Per-file LLVM IR generation
-3.	../../buildtools/third_party/libc++/trunk/include\vector:353:14: Generating code for declaration 'std::__1::__vector_base<content::FaviconURL, std::__1::allocator<content::FaviconURL> >::__end_cap'
-0x00007FF633872FE6 (0x00007FF636C42730 0x00007FF635EB0D63 0x000000EB03B89D98 0x000000000000001A) <unknown module>
-0x00007FF635EB0C19 (0x00007FF635EB0D63 0x000000EB03B89D98 0x000000000000001A 0x00007FF636C42B30) <unknown module>
-0x00007FF636C42730 (0x000000EB03B89D98 0x000000000000001A 0x00007FF636C42B30 0x0000000000000012) <unknown module>
-0x00007FF635EB0D63 (0x000000000000001A 0x00007FF636C42B30 0x0000000000000012 0x0000000000000000) <unknown module>
-0x000000EB03B89D98 (0x00007FF636C42B30 0x0000000000000012 0x0000000000000000 0x00007FF635EB080D) <unknown module>
-0x000000000000001A (0x0000000000000012 0x0000000000000000 0x00007FF635EB080D 0x0000023A0CCA2868) <unknown module>
-0x00007FF636C42B30 (0x0000000000000000 0x00007FF635EB080D 0x0000023A0CCA2868 0x000000EB03B8A0C0) <unknown module>
-0x0000000000000012 (0x00007FF635EB080D 0x0000023A0CCA2868 0x000000EB03B8A0C0 0x0000000000000000) <unknown module>
-clang-cl: error: clang frontend command failed due to signal (use -v to see invocation)
-clang version 10.0.0 (https://github.com/llvm/llvm-project/ e84b7a5fe230e42b8e6fe451369874a773bf1867)
-Target: i386-pc-windows-msvc
-Thread model: posix
-InstalledDir: ..\..\third_party\llvm-build\Release+Asserts\bin
-clang-cl: note: diagnostic msg: PLEASE submit a bug report to https://crbug.com and run tools/clang/scripts/process_crashreports.py (only works inside Google) which will upload a report and include the crash backtrace, preprocessed source, and associated run script.
-clang-cl: note: diagnostic msg:
-********************
-
-PLEASE ATTACH THE FOLLOWING FILES TO THE BUG REPORT:
-Preprocessed source(s) and associated run script(s) are located at:
-clang-cl: note: diagnostic msg: ../../tools/clang/crashreports\render_frame_impl-673e3a.cpp
-clang-cl: note: diagnostic msg: ../../tools/clang/crashreports\render_frame_impl-673e3a.sh
-clang-cl: note: diagnostic msg:
-
-********************
-`)
-	llvmErrorMsg, ok := extractLLVMError(msg)
-	if got, want := llvmErrorMsg, []byte(`LLVM ERROR: out of memory`); !ok || !bytes.Equal(got, want) {
-		t.Errorf("extractLLVMError(msg)=%q, %t; want=%q; true",
-			string(got), ok, string(want))
-	}
-
-}
-
-func TestChangeSymlinkAbsToRel(t *testing.T) {
-	for _, tc := range []struct {
-		desc       string
-		name       string
-		target     string
-		wantTarget string
-		wantErr    bool
-	}{
-		{
-			desc:       "base",
-			name:       "/a/b.txt",
-			target:     "/c/d.txt",
-			wantTarget: "../c/d.txt",
-		},
-		{
-			desc:    "should be error because name is not absolute",
-			name:    "../a/b.txt",
-			target:  "/c/d.txt",
-			wantErr: true,
-		},
-		{
-			desc:       "should allow name is in root",
-			name:       "/a.txt",
-			target:     "/c/d.txt",
-			wantTarget: "c/d.txt",
-		},
-	} {
-		t.Run(tc.desc, func(t *testing.T) {
-			e := merkletree.Entry{
-				Name:   tc.name,
-				Target: tc.target,
-			}
-			actual, err := changeSymlinkAbsToRel(e)
-			if tc.wantErr && err == nil {
-				t.Errorf("changeSymlinkAbsToRel(%v) returned nil err; want err", e)
-			}
-			if !tc.wantErr && err != nil {
-				t.Errorf("changeSymlinkAbsToRel(%v) returned %v ; want nil", e, err)
-			}
-			if tc.wantTarget != actual.Target {
-				expected := merkletree.Entry{
-					Name:   e.Name,
-					Target: tc.wantTarget,
-				}
-				t.Errorf("changeSymlinkAbsToRel(%v) = %v; want %v", e, actual, expected)
-			}
-		})
-	}
-}
-
-type fakeGomaInput struct {
-	mu         sync.Mutex
-	digests    map[*gomapb.ExecReq_Input]digest.Data
-	hashes     map[*gomapb.FileBlob]string
-	uploaded   []string
-	numUploads int
-}
-
-func (f *fakeGomaInput) setInputs(inputs []*gomapb.ExecReq_Input) {
-	f.mu.Lock()
-	defer f.mu.Unlock()
-	if f.digests == nil {
-		f.digests = make(map[*gomapb.ExecReq_Input]digest.Data)
-	}
-	if f.hashes == nil {
-		f.hashes = make(map[*gomapb.FileBlob]string)
-	}
-	for _, input := range inputs {
-		f.digests[input] = digest.Bytes(input.GetFilename(), input.Content.GetContent())
-		f.hashes[input.Content] = input.GetHashKey()
-	}
-}
-
-func (f *fakeGomaInput) toDigest(ctx context.Context, in *gomapb.ExecReq_Input) (digest.Data, error) {
-	d, ok := f.digests[in]
-	if !ok {
-		return nil, errors.New("not found")
-	}
-	return d, nil
-}
-
-func (f *fakeGomaInput) upload(ctx context.Context, blobs []*gomapb.FileBlob) ([]string, error) {
-	f.mu.Lock()
-	defer f.mu.Unlock()
-	hashes := make([]string, 0, len(blobs))
-	for _, blob := range blobs {
-		h, ok := f.hashes[blob]
-		if !ok {
-			return nil, errors.New("upload error")
-		}
-		f.uploaded = append(f.uploaded, h)
-		f.numUploads++
-		hashes = append(hashes, h)
-	}
-	return hashes, nil
-}
-
-func (f *fakeGomaInput) Close() {}
-
-func makeInput(tb testing.TB, content, filename string) *gomapb.ExecReq_Input {
-	tb.Helper()
-	blob := &gomapb.FileBlob{
-		BlobType: gomapb.FileBlob_FILE.Enum(),
-		Content:  []byte(content),
-		FileSize: proto.Int64(int64(len(content))),
-	}
-	hashkey, err := hash.SHA256Proto(blob)
-	if err != nil {
-		tb.Fatal(err)
-	}
-	return &gomapb.ExecReq_Input{
-		HashKey:  proto.String(hashkey),
-		Filename: proto.String(filename),
-		Content:  blob,
-	}
-}
-
-func TestUploadInputFiles(t *testing.T) {
-	inputs := make([]*gomapb.ExecReq_Input, 6)
-	for i := range inputs {
-		inputs[i] = makeInput(t, fmt.Sprintf("content %d", i), fmt.Sprintf("input_%d", i))
-	}
-
-	manyInputs := make([]*gomapb.ExecReq_Input, 1000)
-	for i := range manyInputs {
-		manyInputs[i] = makeInput(t, fmt.Sprintf("content %d", i), fmt.Sprintf("input_%d", i))
-	}
-
-	makeHashKeys := func(inputs []*gomapb.ExecReq_Input) []string {
-		result := make([]string, len(inputs))
-		for i, input := range inputs {
-			result[i] = input.GetHashKey()
-		}
-		return result
-	}
-
-	for _, tc := range []struct {
-		desc           string
-		stored         []*gomapb.ExecReq_Input
-		inputs         []*gomapb.ExecReq_Input
-		wantUploaded   []string
-		wantNumUploads int
-		wantErr        bool
-	}{
-		{
-			desc:           "uploads",
-			stored:         inputs,
-			inputs:         inputs,
-			wantUploaded:   makeHashKeys(inputs),
-			wantNumUploads: len(inputs),
-		}, {
-			desc:           "many uploads",
-			stored:         manyInputs,
-			inputs:         manyInputs,
-			wantUploaded:   makeHashKeys(manyInputs),
-			wantNumUploads: len(manyInputs),
-		}, {
-			desc:    "all errors",
-			inputs:  inputs,
-			wantErr: true,
-		}, {
-			desc:           "partial errors",
-			stored:         inputs[:3],
-			inputs:         inputs,
-			wantUploaded:   makeHashKeys(manyInputs[:3]),
-			wantNumUploads: len(inputs[:3]),
-			wantErr:        true,
-		},
-	} {
-		t.Run(tc.desc, func(t *testing.T) {
-			gi := &fakeGomaInput{}
-			ctx := context.Background()
-
-			gi.setInputs(tc.stored)
-
-			err := uploadInputFiles(ctx, tc.inputs, gi)
-
-			sort.Strings(gi.uploaded)
-			sort.Strings(tc.wantUploaded)
-			if !cmp.Equal(gi.uploaded, tc.wantUploaded) {
-				t.Errorf("gi.uploaded -want +got: %s", cmp.Diff(tc.wantUploaded, gi.uploaded))
-			}
-			if gi.numUploads != tc.wantNumUploads {
-				t.Errorf("numUploads=%d; want %d", gi.numUploads, tc.wantNumUploads)
-			}
-			if err != nil && !tc.wantErr {
-				t.Errorf("err=%v; want nil", err)
-			}
-		})
-	}
-}
-
-func TestInputFiles(t *testing.T) {
-	// These are minimal function / map that are not being tested.
-	rootRel := func(filename string) (string, error) { return filename, nil }
-	executableInputs := map[string]bool{}
-
-	inputs := make([]*gomapb.ExecReq_Input, 6)
-	inputsNoContent := make([]*gomapb.ExecReq_Input, len(inputs))
-	for i := range inputs {
-		input := makeInput(t, fmt.Sprintf("content %d", i), fmt.Sprintf("input_%d", i))
-		inputs[i] = input
-		inputsNoContent[i] = &gomapb.ExecReq_Input{
-			HashKey:  input.HashKey,
-			Filename: input.Filename,
-		}
-	}
-
-	manyInputs := make([]*gomapb.ExecReq_Input, 1000)
-	for i := range manyInputs {
-		manyInputs[i] = makeInput(t, fmt.Sprintf("content %d", i), fmt.Sprintf("input_%d", i))
-	}
-
-	makeMissing := func(input *gomapb.ExecReq_Input) inputFileResult {
-		return inputFileResult{
-			missingInput:  input.GetFilename(),
-			missingReason: "input: not found",
-		}
-	}
-	makeFound := func(input *gomapb.ExecReq_Input) inputFileResult {
-		return inputFileResult{
-			file: merkletree.Entry{
-				Name: input.GetFilename(), // Because `rootRel()` is an identity function
-				Data: inputDigestData{
-					filename: input.GetFilename(),
-					Data:     digest.Bytes(input.GetFilename(), input.Content.GetContent()),
-				},
-			},
-			needUpload: input.Content != nil,
-		}
-	}
-
-	for _, tc := range []struct {
-		desc       string
-		stored     []*gomapb.ExecReq_Input
-		inputs     []*gomapb.ExecReq_Input
-		wantResult []inputFileResult
-	}{
-		{
-			desc:   "all missing",
-			inputs: inputsNoContent,
-			wantResult: []inputFileResult{
-				makeMissing(inputs[0]),
-				makeMissing(inputs[1]),
-				makeMissing(inputs[2]),
-				makeMissing(inputs[3]),
-				makeMissing(inputs[4]),
-				makeMissing(inputs[5]),
-			},
-		}, {
-			desc:   "new content but not stored",
-			inputs: inputs,
-			wantResult: []inputFileResult{
-				makeMissing(inputs[0]),
-				makeMissing(inputs[1]),
-				makeMissing(inputs[2]),
-				makeMissing(inputs[3]),
-				makeMissing(inputs[4]),
-				makeMissing(inputs[5]),
-			},
-		}, {
-			desc:   "all stored no content",
-			stored: inputsNoContent,
-			inputs: inputsNoContent,
-			wantResult: []inputFileResult{
-				makeFound(inputsNoContent[0]),
-				makeFound(inputsNoContent[1]),
-				makeFound(inputsNoContent[2]),
-				makeFound(inputsNoContent[3]),
-				makeFound(inputsNoContent[4]),
-				makeFound(inputsNoContent[5]),
-			},
-		}, {
-			desc:   "all uploaded",
-			stored: inputs,
-			inputs: inputs,
-			wantResult: []inputFileResult{
-				makeFound(inputs[0]),
-				makeFound(inputs[1]),
-				makeFound(inputs[2]),
-				makeFound(inputs[3]),
-				makeFound(inputs[4]),
-				makeFound(inputs[5]),
-			},
-		},
-		{
-			desc: "mixed",
-			stored: []*gomapb.ExecReq_Input{
-				inputs[0],
-				inputsNoContent[1],
-				inputs[3],
-				inputsNoContent[4],
-			},
-			inputs: []*gomapb.ExecReq_Input{
-				inputs[0],
-				inputsNoContent[1],
-				inputs[2],
-				inputs[3],
-				inputsNoContent[4],
-				inputsNoContent[5],
-			},
-			wantResult: []inputFileResult{
-				makeFound(inputs[0]),
-				makeFound(inputsNoContent[1]),
-				makeMissing(inputs[2]),
-				makeFound(inputs[3]),
-				makeFound(inputsNoContent[4]),
-				makeMissing(inputs[5]),
-			},
-		}, {
-			desc:   "many uploads",
-			stored: manyInputs,
-			inputs: manyInputs,
-			wantResult: func() []inputFileResult {
-				result := make([]inputFileResult, len(manyInputs))
-				for i, input := range manyInputs {
-					result[i] = makeFound(input)
-				}
-				return result
-			}(),
-		},
-	} {
-		t.Run(tc.desc, func(t *testing.T) {
-			gi := &fakeGomaInput{}
-			gi.setInputs(tc.stored)
-			ctx := context.Background()
-
-			results := inputFiles(ctx, tc.inputs, gi, rootRel, executableInputs)
-
-			digestDataComparer := cmp.Comparer(func(x, y digest.Data) bool {
-				if x == nil && y == nil {
-					return true
-				}
-				if x == nil || y == nil {
-					return false
-				}
-				return proto.Equal(x.Digest(), y.Digest()) && x.String() == y.String()
-			})
-
-			if !cmp.Equal(results, tc.wantResult, cmp.AllowUnexported(inputFileResult{}), cmp.AllowUnexported(inputDigestData{}), digestDataComparer) {
-				t.Errorf("results=%v; want %v", results, tc.wantResult)
-			}
-		})
-	}
-}
-
-type nopLogger struct{}
-
-func (nopLogger) Debug(args ...interface{})                {}
-func (nopLogger) Debugf(format string, arg ...interface{}) {}
-func (nopLogger) Info(args ...interface{})                 {}
-func (nopLogger) Infof(format string, arg ...interface{})  {}
-func (nopLogger) Warn(args ...interface{})                 {}
-func (nopLogger) Warnf(format string, arg ...interface{})  {}
-func (nopLogger) Error(args ...interface{})                {}
-func (nopLogger) Errorf(format string, arg ...interface{}) {}
-func (nopLogger) Fatal(args ...interface{})                {}
-func (nopLogger) Fatalf(format string, arg ...interface{}) {}
-func (nopLogger) Sync() error                              { return nil }
-
-func BenchmarkInputFiles(b *testing.B) {
-	var inputs []*gomapb.ExecReq_Input
-	for i := 0; i < 1000; i++ {
-		inputs = append(inputs, makeInput(b, fmt.Sprintf("content %d", i), fmt.Sprintf("input_%d", i)))
-	}
-	gi := &fakeGomaInput{}
-	gi.setInputs(inputs)
-	rootRel := func(filename string) (string, error) { return filename, nil }
-	executableInputs := map[string]bool{}
-	ctx := log.NewContext(context.Background(), nopLogger{})
-	b.ResetTimer()
-	for i := 0; i < b.N; i++ {
-		inputFiles(ctx, inputs, gi, rootRel, executableInputs)
-	}
-}
diff --git a/remoteexec/fake_cluster_for_test.go b/remoteexec/fake_cluster_for_test.go
deleted file mode 100644
index e80fb36..0000000
--- a/remoteexec/fake_cluster_for_test.go
+++ /dev/null
@@ -1,478 +0,0 @@
-// Copyright 2018 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.
-
-package remoteexec
-
-import (
-	"context"
-	"fmt"
-	"io"
-	"io/ioutil"
-	"math/rand"
-	"strings"
-	"sync"
-	"testing"
-	"time"
-
-	rpb "github.com/bazelbuild/remote-apis/build/bazel/remote/execution/v2"
-	"google.golang.org/grpc"
-	"google.golang.org/grpc/codes"
-	"google.golang.org/grpc/status"
-	"google.golang.org/protobuf/proto"
-
-	"go.chromium.org/goma/server/cache"
-	"go.chromium.org/goma/server/file"
-	"go.chromium.org/goma/server/hash"
-	"go.chromium.org/goma/server/log"
-	gomapb "go.chromium.org/goma/server/proto/api"
-	cachepb "go.chromium.org/goma/server/proto/cache"
-	cpb "go.chromium.org/goma/server/proto/command"
-	fpb "go.chromium.org/goma/server/proto/file"
-	"go.chromium.org/goma/server/remoteexec/digest"
-	"go.chromium.org/goma/server/rpc/grpctest"
-)
-
-// fakeCluster represents fake goma cluster.
-type fakeCluster struct {
-	rbe  *fakeRBE
-	srv  *grpc.Server
-	addr string
-	conn *grpc.ClientConn
-	stop func()
-
-	cache *cache.Cache
-	csrv  *grpc.Server
-	cconn *grpc.ClientConn
-	cstop func()
-
-	gomafile *file.Service
-	fsrv     *grpc.Server
-	fconn    *grpc.ClientConn
-	fstop    func()
-
-	cmdStorage fakeCmdStorage
-	redis      fakeRedis
-
-	adapter Adapter
-}
-
-// setup sets up fake goma cluster with fake RBE instance.
-func (f *fakeCluster) setup(ctx context.Context, instancePrefix string) error {
-	var err error
-	var defers []func()
-	defer func() {
-		for i := len(defers) - 1; i >= 0; i-- {
-			defers[i]()
-		}
-	}()
-
-	logger := log.FromContext(ctx)
-
-	// CAS BatchUpdateBlobs will accept at most max_batch_total_size_bytes.
-	// https://github.com/bazelbuild/remote-apis/blob/efd28d1832bd3ccddc3d2b29c341da1cce09c333/build/bazel/remote/execution/v2/remote_execution.proto#L1278
-	// TODO: set max msg size to match with max_batch_total_size_bytes
-	// in ServerCapabilities.CacheCapabitilies.
-	f.srv = grpc.NewServer()
-	registerFakeRBE(f.srv, f.rbe)
-	f.addr, f.stop, err = grpctest.StartServer(f.srv)
-	if err != nil {
-		return err
-	}
-	defers = append(defers, f.stop)
-	logger.Infof("f.conn = addr:%s", f.addr)
-	// TODO: make it secure and pass enduser credentials
-	f.conn, err = grpc.Dial(f.addr, grpc.WithInsecure())
-	if err != nil {
-		return err
-	}
-	defers = append(defers, func() { f.conn.Close() })
-
-	f.csrv = grpc.NewServer()
-	f.cache, err = cache.New(cache.Config{
-		MaxBytes: 1 * 1024 * 1024 * 1024,
-	})
-	if err != nil {
-		return err
-	}
-	cachepb.RegisterCacheServiceServer(f.csrv, f.cache)
-	var addr string
-	addr, f.cstop, err = grpctest.StartServer(f.csrv)
-	if err != nil {
-		return err
-	}
-	defers = append(defers, f.cstop)
-	logger.Infof("f.cconn = addr:%s", addr)
-	f.cconn, err = grpc.Dial(addr, grpc.WithInsecure())
-	if err != nil {
-		return err
-	}
-	defers = append(defers, func() { f.cconn.Close() })
-	f.fsrv = grpc.NewServer(grpc.MaxSendMsgSize(file.DefaultMaxMsgSize), grpc.MaxRecvMsgSize(file.DefaultMaxMsgSize))
-	f.gomafile = &file.Service{
-		Cache: cachepb.NewCacheServiceClient(f.cconn),
-	}
-	fpb.RegisterFileServiceServer(f.fsrv, f.gomafile)
-	addr, f.fstop, err = grpctest.StartServer(f.fsrv)
-	if err != nil {
-		return err
-	}
-	defers = append(defers, f.fstop)
-	logger.Infof("f.fconn = addr:%s", addr)
-	f.fconn, err = grpc.Dial(addr, grpc.WithInsecure())
-	if err != nil {
-		return err
-	}
-	defers = append(defers, func() { f.fconn.Close() })
-
-	f.adapter = Adapter{
-		InstancePrefix:    instancePrefix,
-		ExecTimeout:       10 * time.Second,
-		Client:            Client{ClientConn: f.conn},
-		GomaFile:          fpb.NewFileServiceClient(f.fconn),
-		DigestCache:       digest.NewCache(&f.redis, 1e6),
-		CmdStorage:        &f.cmdStorage,
-		ToolDetails:       &rpb.ToolDetails{},
-		FileLookupSema:    make(chan struct{}, 2),
-		CASBlobLookupSema: make(chan struct{}, 2),
-	}
-
-	defers = nil
-	return nil
-}
-
-// teardown cleans up fake cluster.
-func (f *fakeCluster) teardown() {
-	if f.fconn != nil {
-		f.fconn.Close()
-	}
-	if f.fstop != nil {
-		f.fstop()
-	}
-	if f.cconn != nil {
-		f.cconn.Close()
-	}
-	if f.cstop != nil {
-		f.cstop()
-	}
-}
-
-// fakeToolchain represents fake toolchain.
-type fakeToolchain struct {
-	descs              []*cpb.CmdDescriptor
-	RemoteexecPlatform *cpb.RemoteexecPlatform
-}
-
-// CommandSpec returns command spec for name and localPath.
-func (f *fakeToolchain) CommandSpec(name, localPath string) *gomapb.CommandSpec {
-	for _, desc := range f.descs {
-		if desc.Selector.Name == name {
-			return &gomapb.CommandSpec{
-				Name:              proto.String(desc.Selector.Name),
-				Version:           proto.String(desc.Selector.Version),
-				Target:            proto.String(desc.Selector.Target),
-				BinaryHash:        []byte(desc.Selector.BinaryHash),
-				LocalCompilerPath: proto.String(localPath),
-			}
-		}
-	}
-	return &gomapb.CommandSpec{}
-}
-
-// newFakeClang creates new fake toolchain for clang version+target.
-func newFakeClang(f *fakeCmdStorage, version, target string) *fakeToolchain {
-	clangFile := f.newFileSpec("bin/clang", true)
-	libFindBadConstructs := f.newFileSpec("lib/libFindBadConstructs.so", false)
-	return &fakeToolchain{
-		descs: []*cpb.CmdDescriptor{
-			{
-				Selector: &cpb.Selector{
-					Name:       "clang",
-					Version:    version,
-					Target:     target,
-					BinaryHash: clangFile.Hash,
-				},
-				Setup: &cpb.CmdDescriptor_Setup{
-					CmdFile:  clangFile,
-					PathType: cpb.CmdDescriptor_POSIX,
-				},
-			},
-			{
-				Selector: &cpb.Selector{
-					Name:       "clang++",
-					Version:    version,
-					Target:     target,
-					BinaryHash: clangFile.Hash,
-				},
-				Setup: &cpb.CmdDescriptor_Setup{
-					CmdFile:  clangFile,
-					PathType: cpb.CmdDescriptor_POSIX,
-				},
-			},
-			{
-				Selector: &cpb.Selector{
-					Name:       "libFindBadConstructs.so",
-					BinaryHash: libFindBadConstructs.Hash,
-				},
-				Setup: &cpb.CmdDescriptor_Setup{
-					CmdFile:  libFindBadConstructs,
-					PathType: cpb.CmdDescriptor_POSIX,
-				},
-			},
-		},
-		RemoteexecPlatform: &cpb.RemoteexecPlatform{
-			Properties: []*cpb.RemoteexecPlatform_Property{
-				{
-					Name:  "container-image",
-					Value: "docker://grpc.io/goma-dev/container-image@sha256:xxxx",
-				},
-			},
-		},
-	}
-}
-
-// newFakeClangCL creates new fake toolchain for clang-cl.
-func newFakeClangCL(f *fakeCmdStorage, version string) *fakeToolchain {
-	clangCLFile := f.newFileSpec("bin/clang-cl", true)
-	return &fakeToolchain{
-		descs: []*cpb.CmdDescriptor{
-			{
-				Selector: &cpb.Selector{
-					Name:       "clang-cl",
-					Version:    version,
-					Target:     "x86_64-windows-msvc",
-					BinaryHash: clangCLFile.Hash,
-				},
-				Setup: &cpb.CmdDescriptor_Setup{
-					CmdFile:  clangCLFile,
-					PathType: cpb.CmdDescriptor_POSIX,
-				},
-				Cross: &cpb.CmdDescriptor_Cross{
-					ClangNeedTarget: true,
-					WindowsCross:    true,
-				},
-			},
-		},
-		RemoteexecPlatform: &cpb.RemoteexecPlatform{
-			Properties: []*cpb.RemoteexecPlatform_Property{
-				{
-					Name:  "container-image",
-					Value: "docker://grpc.io/goma-dev/container-image@sha256:yyy",
-				},
-			},
-		},
-	}
-}
-
-// pushToolchains push fake toolchain in fake cluster.
-func (f *fakeCluster) pushToolchains(ctx context.Context, tc *fakeToolchain) error {
-	config := &cpb.ConfigResp{
-		VersionId: time.Now().String(),
-	}
-	for _, desc := range tc.descs {
-		config.Configs = append(config.Configs, &cpb.Config{
-			Target: &cpb.Target{
-				Addr: f.addr,
-			},
-			BuildInfo:          &cpb.BuildInfo{},
-			CmdDescriptor:      desc,
-			RemoteexecPlatform: tc.RemoteexecPlatform,
-		})
-	}
-	err := f.adapter.Inventory.Configure(ctx, config)
-	return err
-}
-
-// pushPlatform pushes a platform with dimensions.
-func (f *fakeCluster) pushPlatform(ctx context.Context, containerImage string, dimensions []string) error {
-	config := &cpb.ConfigResp{
-		VersionId: time.Now().String(),
-	}
-
-	config.Configs = []*cpb.Config{
-		{
-			RemoteexecPlatform: &cpb.RemoteexecPlatform{
-				Properties: []*cpb.RemoteexecPlatform_Property{
-					{
-						Name:  "container-image",
-						Value: containerImage,
-					},
-				},
-			},
-			Dimensions: dimensions,
-		},
-	}
-	err := f.adapter.Inventory.Configure(ctx, config)
-	return err
-}
-
-// fakeLocalFiles represents fake local files (in client side).
-type fakeLocalFiles struct {
-	m map[string]string // path -> content
-}
-
-func randomSize() uint64 {
-	s := rand.Uint64() % (2 * 1024 * 1024)
-	if s == 0 {
-		s++
-	}
-	return s
-}
-
-func randomBigSize() uint64 {
-	return 2*1024*1024 + randomSize()
-}
-
-// Add adds new fake file named fname.
-func (f *fakeLocalFiles) Add(fname string, size uint64) {
-	if f.m == nil {
-		f.m = make(map[string]string)
-	}
-	buf := make([]byte, size)
-	rand.Read(buf)
-	f.m[fname] = string(buf)
-}
-
-// Dup dups oldname as newname.
-func (f *fakeLocalFiles) Dup(oldname, newname string) {
-	f.m[newname] = f.m[oldname]
-}
-
-// Open opens fake file.
-func (f *fakeLocalFiles) Open(fname string) (io.Reader, error) {
-	data, ok := f.m[fname]
-	if !ok {
-		return nil, fmt.Errorf("%s not found", fname)
-	}
-	return strings.NewReader(data), nil
-}
-
-// mustFileHash returns SHA256 hash of file content.
-func (f *fakeLocalFiles) mustFileHash(ctx context.Context, t *testing.T, fname string) string {
-	t.Helper()
-	data, ok := f.m[fname]
-	if !ok {
-		t.Fatalf("%s not found", fname)
-	}
-
-	return hash.SHA256Content([]byte(data))
-}
-
-// mustDigest returns digest of file content.
-func (f *fakeLocalFiles) mustDigest(ctx context.Context, t *testing.T, fname string) *rpb.Digest {
-	data, ok := f.m[fname]
-	if !ok {
-		t.Fatalf("%s not found", fname)
-	}
-	return &rpb.Digest{
-		Hash:      hash.SHA256Content([]byte(data)),
-		SizeBytes: int64(len(data)),
-	}
-}
-
-// mustInput creates execreq input for fname (maybe relative) from fullpath.
-// if fc is nil, content won't be uploaded and not set in input (i.e. hash only).
-func (f *fakeLocalFiles) mustInput(ctx context.Context, t *testing.T, fc fpb.FileServiceClient, fullpath, fname string) *gomapb.ExecReq_Input {
-	data := f.m[fullpath]
-	input := &gomapb.ExecReq_Input{
-		Filename: proto.String(fname),
-		Content: &gomapb.FileBlob{
-			FileSize: proto.Int64(int64(len(data))),
-		},
-	}
-	err := file.FromReader(ctx, fc, strings.NewReader(data), input.Content)
-	if err != nil {
-		t.Fatalf("failed to read file %s: %v", fullpath, err)
-	}
-	hashKey, err := hash.SHA256Proto(input.Content)
-	if err != nil {
-		t.Fatalf("failed to compute sha256 %s: %v", fullpath, err)
-	}
-	input.HashKey = proto.String(hashKey)
-	if fc == nil {
-		input.Content = nil
-	}
-	return input
-}
-
-// fakeRedis represents cache client on fake memorystore.
-type fakeRedis struct {
-	mu sync.Mutex
-	m  map[string][]byte
-}
-
-func (f *fakeRedis) mustSet(ctx context.Context, t *testing.T, key string, d *rpb.Digest) {
-	f.mu.Lock()
-	defer f.mu.Unlock()
-	if f.m == nil {
-		f.m = make(map[string][]byte)
-	}
-	b, err := proto.Marshal(d)
-	if err != nil {
-		t.Fatal(err)
-	}
-	f.m[key] = b
-}
-
-func (f *fakeRedis) Get(ctx context.Context, req *cachepb.GetReq, opts ...grpc.CallOption) (*cachepb.GetResp, error) {
-	f.mu.Lock()
-	defer f.mu.Unlock()
-	b, ok := f.m[req.Key]
-	if !ok {
-		return nil, status.Errorf(codes.NotFound, "no digest cache for %s", req.Key)
-	}
-	return &cachepb.GetResp{
-		Kv: &cachepb.KV{
-			Key:   req.Key,
-			Value: b,
-		},
-	}, nil
-}
-
-func (f *fakeRedis) Put(ctx context.Context, req *cachepb.PutReq, opts ...grpc.CallOption) (*cachepb.PutResp, error) {
-	f.mu.Lock()
-	defer f.mu.Unlock()
-	if f.m == nil {
-		f.m = make(map[string][]byte)
-	}
-	f.m[req.Kv.Key] = req.Kv.Value
-	return &cachepb.PutResp{}, nil
-}
-
-// fakeCmdStorage represents fake cmdstorage bucket.
-type fakeCmdStorage struct {
-	m map[string]string // hash -> data
-}
-
-// Open opens cmd files identified by hash.
-func (f *fakeCmdStorage) Open(ctx context.Context, hash string) (io.ReadCloser, error) {
-	v, ok := f.m[hash]
-	if !ok {
-		return nil, status.Errorf(codes.NotFound, "%s not found", hash)
-	}
-	return ioutil.NopCloser(strings.NewReader(v)), nil
-}
-
-// newFile stores a new file, and returns hash of it.
-func (f *fakeCmdStorage) newFile(size int64) string {
-	buf := make([]byte, size)
-	rand.Read(buf)
-	h := hash.SHA256Content(buf)
-	if f.m == nil {
-		f.m = make(map[string]string)
-	}
-	f.m[h] = string(buf)
-	return h
-}
-
-// newFileSpec stores a new file and returns FileSpec of it.
-func (f *fakeCmdStorage) newFileSpec(name string, isExecutable bool) *cpb.FileSpec {
-	size := int64(rand.Uint64() % (8 * 1024 * 1024))
-	h := f.newFile(size)
-	return &cpb.FileSpec{
-		Path:         name,
-		Size:         size,
-		Hash:         h,
-		IsExecutable: isExecutable,
-	}
-}
diff --git a/remoteexec/fake_rbe_for_test.go b/remoteexec/fake_rbe_for_test.go
deleted file mode 100644
index d5c04de..0000000
--- a/remoteexec/fake_rbe_for_test.go
+++ /dev/null
@@ -1,615 +0,0 @@
-// Copyright 2018 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.
-
-package remoteexec
-
-import (
-	"bytes"
-	"context"
-	"fmt"
-	"io"
-	"io/ioutil"
-	"path/filepath"
-	"sort"
-	"strings"
-	"sync"
-
-	rpb "github.com/bazelbuild/remote-apis/build/bazel/remote/execution/v2"
-	sempb "github.com/bazelbuild/remote-apis/build/bazel/semver"
-	"github.com/google/uuid"
-	bpb "google.golang.org/genproto/googleapis/bytestream"
-	opb "google.golang.org/genproto/googleapis/longrunning"
-	spb "google.golang.org/genproto/googleapis/rpc/status"
-	"google.golang.org/grpc"
-	"google.golang.org/grpc/codes"
-	"google.golang.org/grpc/status"
-	"google.golang.org/protobuf/proto"
-	"google.golang.org/protobuf/types/known/anypb"
-
-	"go.chromium.org/goma/server/log"
-	"go.chromium.org/goma/server/remoteexec/cas"
-	"go.chromium.org/goma/server/remoteexec/digest"
-)
-
-type operations struct {
-	mu  sync.Mutex
-	ops map[string][]*opb.Operation
-}
-
-func (o *operations) Add(opname string, op *opb.Operation) {
-	o.mu.Lock()
-	defer o.mu.Unlock()
-	if o.ops == nil {
-		o.ops = make(map[string][]*opb.Operation)
-	}
-	o.ops[opname] = append(o.ops[opname], op)
-}
-
-func (o *operations) Get(opname string) []*opb.Operation {
-	o.mu.Lock()
-	defer o.mu.Unlock()
-	ops := o.ops[opname]
-	o.ops[opname] = nil
-	return ops
-}
-
-func execRespOp(opname string, resp *rpb.ExecuteResponse, err error) (*opb.Operation, error) {
-	op := &opb.Operation{
-		Name: opname,
-		Done: true,
-	}
-	if err != nil {
-		s, _ := status.FromError(err)
-		op.Result = &opb.Operation_Error{
-			Error: s.Proto(),
-		}
-		return op, nil
-	}
-	opresp := &opb.Operation_Response{}
-	op.Result = opresp
-	opresp.Response, err = anypb.New(resp)
-	return op, err
-}
-
-type actionCache struct {
-	mu sync.Mutex
-	m  map[string]*rpb.ActionResult
-}
-
-func (c *actionCache) Get(actionDigest *rpb.Digest) (*rpb.ActionResult, bool) {
-	c.mu.Lock()
-	defer c.mu.Unlock()
-	r, ok := c.m[cas.ResName("", actionDigest)]
-	return r, ok
-}
-
-func (c *actionCache) Set(actionDigest *rpb.Digest, resp *rpb.ActionResult) {
-	c.mu.Lock()
-	defer c.mu.Unlock()
-	if c.m == nil {
-		c.m = make(map[string]*rpb.ActionResult)
-	}
-	c.m[cas.ResName("", actionDigest)] = resp
-}
-
-type fakeRBE struct {
-	instancePrefix string
-
-	fakeExec func(ctx context.Context, req *rpb.ExecuteRequest) (*rpb.ExecuteResponse, error)
-
-	rpb.ExecutionServer
-	ops operations
-
-	rpb.ActionCacheServer
-	cache actionCache
-
-	rpb.ContentAddressableStorageServer
-	bpb.ByteStreamServer
-	cas *digest.Store
-
-	rpb.CapabilitiesServer
-	*rpb.ServerCapabilities
-
-	execResp          *rpb.ExecuteResponse
-	execErr           error
-	gotExecuteRequest *rpb.ExecuteRequest
-	gotAction         *rpb.Action
-	gotCommand        *rpb.Command
-}
-
-func registerFakeRBE(srv *grpc.Server, rbe *fakeRBE) {
-	rpb.RegisterExecutionServer(srv, rbe)
-	rpb.RegisterActionCacheServer(srv, rbe)
-	rpb.RegisterContentAddressableStorageServer(srv, rbe)
-	bpb.RegisterByteStreamServer(srv, rbe)
-	rpb.RegisterCapabilitiesServer(srv, rbe)
-}
-
-func defaultCapabilities() *rpb.ServerCapabilities {
-	// as of Sep 14, capabilities is
-	// cache_capabilities:<
-	//  digest_function:SHA256
-	//  action_cache_update_capabilities:<>
-	//  max_batch_total_size_bytes:4194304
-	//  symlink_absolute_path_strategy:DISALLOWED
-	// >
-	// execution_capabilities:<
-	//  digest_function:SHA256
-	//  exec_enabled:true
-	// >
-	// deprecated_api_version:<prerelease:"v1test" >
-	// low_api_version:<major:2 >
-	// high_api_version:<major:2 >
-	return &rpb.ServerCapabilities{
-		CacheCapabilities: &rpb.CacheCapabilities{
-			DigestFunctions: []rpb.DigestFunction_Value{
-				rpb.DigestFunction_SHA256,
-			},
-			ActionCacheUpdateCapabilities: &rpb.ActionCacheUpdateCapabilities{
-				UpdateEnabled: false,
-			},
-			// CachePriorityCapabilities:
-			MaxBatchTotalSizeBytes:      4 * 1024 * 1024,
-			SymlinkAbsolutePathStrategy: rpb.SymlinkAbsolutePathStrategy_DISALLOWED,
-		},
-		ExecutionCapabilities: &rpb.ExecutionCapabilities{
-			DigestFunction: rpb.DigestFunction_SHA256,
-			ExecEnabled:    true,
-			// ExecutionPriorityCapabilities:
-		},
-		LowApiVersion: &sempb.SemVer{
-			Major: 2,
-		},
-		HighApiVersion: &sempb.SemVer{
-			Major: 2,
-		},
-	}
-}
-
-const fakeInstancePrefix = "projects/goma-dev/instances"
-
-func newFakeRBE() *fakeRBE {
-	// TODO: validate capabilities.
-	// - digest_function: sha256 only
-	// - action cache update: not enabled
-	// - symlink absolute: disallow
-	// - exec enabled
-	rbe := &fakeRBE{
-		instancePrefix:     fakeInstancePrefix,
-		cas:                digest.NewStore(),
-		ServerCapabilities: defaultCapabilities(),
-
-		execResp: &rpb.ExecuteResponse{
-			Result: &rpb.ActionResult{
-				ExitCode: 0,
-			},
-		},
-	}
-	rbe.fakeExec = func(ctx context.Context, req *rpb.ExecuteRequest) (*rpb.ExecuteResponse, error) {
-		rbe.gotExecuteRequest = proto.Clone(req).(*rpb.ExecuteRequest)
-		rbe.gotAction = &rpb.Action{}
-		err := rbe.getProto(ctx, req.ActionDigest, rbe.gotAction)
-		if err != nil {
-			return nil, fmt.Errorf("no action in Execute Request %s: %v", req, err)
-		}
-		rbe.gotCommand = &rpb.Command{}
-		err = rbe.getProto(ctx, rbe.gotAction.CommandDigest, rbe.gotCommand)
-		if err != nil {
-			return nil, fmt.Errorf("no command in Execute.Action %s: %v", rbe.gotAction, err)
-		}
-		return rbe.execResp, rbe.execErr
-	}
-	return rbe
-}
-
-func (f *fakeRBE) getProto(ctx context.Context, d *rpb.Digest, m proto.Message) error {
-	if d == nil {
-		return status.Errorf(codes.InvalidArgument, "no digest")
-	}
-	data, ok := f.cas.Get(d)
-	if !ok {
-		return status.Errorf(codes.NotFound, "no data for %s", d)
-	}
-	r, err := data.Open(ctx)
-	if err != nil {
-		return err
-	}
-	defer r.Close()
-	buf, err := ioutil.ReadAll(r)
-	if err != nil {
-		return err
-	}
-	return proto.Unmarshal(buf, m)
-}
-
-func (f *fakeRBE) setProto(ctx context.Context, m proto.Message) (*rpb.Digest, error) {
-	d, err := digest.Proto(m)
-	if err != nil {
-		return nil, err
-	}
-	f.cas.Set(d)
-	return d.Digest(), nil
-}
-
-func (f *fakeRBE) isValidInstance(instance string) bool {
-	return strings.HasPrefix(instance, f.instancePrefix+"/")
-}
-
-func (f *fakeRBE) verifyExecuteRequest(ctx context.Context, req *rpb.ExecuteRequest) error {
-	if !f.isValidInstance(req.InstanceName) {
-		return status.Errorf(codes.PermissionDenied, "unexpected instance name %q", req.InstanceName)
-	}
-	if req.ActionDigest == nil {
-		return status.Errorf(codes.InvalidArgument, "no action digest")
-	}
-	action := &rpb.Action{}
-	err := f.getProto(ctx, req.ActionDigest, action)
-	if err != nil {
-		return status.Errorf(codes.InvalidArgument, "action_digest: %v", err)
-	}
-	command := &rpb.Command{}
-	err = f.getProto(ctx, action.CommandDigest, command)
-	if err != nil {
-		return status.Errorf(codes.InvalidArgument, "action.command_digest: %v", err)
-	}
-	if len(command.Arguments) == 0 {
-		return status.Errorf(codes.InvalidArgument, "command.arguments empty")
-	}
-	if !sort.SliceIsSorted(command.EnvironmentVariables, func(i, j int) bool {
-		return command.EnvironmentVariables[i].Name < command.EnvironmentVariables[j].Name
-	}) {
-		return status.Errorf(codes.InvalidArgument, "command.environment_variables: not sorted: %q", command.EnvironmentVariables)
-	}
-
-	if !sort.StringsAreSorted(command.OutputFiles) {
-		return status.Errorf(codes.InvalidArgument, "command.output_files is not sorted: %q", command.OutputFiles)
-	}
-	if !sort.StringsAreSorted(command.OutputDirectories) {
-		return status.Errorf(codes.InvalidArgument, "command.output_directories is not sorted: %q", command.OutputDirectories)
-	}
-
-	var prev string
-	for _, fname := range append(append([]string{}, command.OutputFiles...), command.OutputDirectories...) {
-		if strings.HasPrefix(fname, "/") || strings.HasSuffix(fname, "/") {
-			return status.Errorf(codes.InvalidArgument, "command.output_* %q: leading slash or trailing slash", fname)
-		}
-		if fname == prev {
-			return status.Errorf(codes.InvalidArgument, "command.output_* %q: duplicate", fname)
-		}
-		if prev != "" && strings.HasPrefix(fname, prev+"/") {
-			return status.Errorf(codes.InvalidArgument, "command.output_* %q child of %q?", fname, prev)
-		}
-		prev = fname
-	}
-
-	if command.Platform == nil {
-		return status.Errorf(codes.InvalidArgument, "command.platform: missing")
-	}
-	if !sort.SliceIsSorted(command.Platform.Properties, func(i, j int) bool {
-		return command.Platform.Properties[i].Name < command.Platform.Properties[j].Name
-	}) {
-		return status.Errorf(codes.InvalidArgument, "command.platform.properties: not sorted")
-	}
-
-	err = f.verifyDirectory(ctx, action.InputRootDigest, ".")
-	if err != nil {
-		return status.Errorf(codes.InvalidArgument, "action.input_root_digest: %v", err)
-	}
-	return nil
-}
-
-func (f *fakeRBE) verifyDirectory(ctx context.Context, d *rpb.Digest, name string) error {
-	dir := &rpb.Directory{}
-	err := f.getProto(ctx, d, dir)
-	if err != nil {
-		return err
-	}
-	if !sort.SliceIsSorted(dir.Files, func(i, j int) bool {
-		return dir.Files[i].Name < dir.Files[j].Name
-	}) {
-		return fmt.Errorf("files not sorted at %s", name)
-	}
-	if !sort.SliceIsSorted(dir.Directories, func(i, j int) bool {
-		return dir.Directories[i].Name < dir.Directories[j].Name
-	}) {
-		return fmt.Errorf("directories not sorted at %s", name)
-	}
-	if !sort.SliceIsSorted(dir.Symlinks, func(i, j int) bool {
-		return dir.Symlinks[i].Name < dir.Symlinks[j].Name
-	}) {
-		return fmt.Errorf("symlinks not sorted at %s", name)
-	}
-
-	seen := make(map[string]interface{})
-	for _, file := range dir.Files {
-		if file.Name == "" || strings.Contains(file.Name, "/") {
-			return fmt.Errorf("bad name %q for %s in %s", file.Name, file.Digest, name)
-		}
-		if seen[file.Name] != nil {
-			return fmt.Errorf("duplicate %q in %s: %v", file.Name, name, dir)
-		}
-		seen[file.Name] = file
-		_, ok := f.cas.Get(file.Digest)
-		if !ok {
-			return fmt.Errorf("%s %s not found", filepath.Join(name, file.Name), file.Digest)
-		}
-	}
-	for _, d := range dir.Directories {
-		if d.Name == "" || strings.Contains(d.Name, "/") {
-			return fmt.Errorf("bad name %q for %s in %s", d.Name, d.Digest, name)
-		}
-		if seen[d.Name] != nil {
-			return fmt.Errorf("duplicate %q in %s: %v", d.Name, name, dir)
-		}
-		seen[d.Name] = d
-		err = f.verifyDirectory(ctx, d.Digest, filepath.Join(name, d.Name))
-		if err != nil {
-			return err
-		}
-	}
-	for _, sym := range dir.Symlinks {
-		if sym.Name == "" || strings.Contains(sym.Name, "/") {
-			return fmt.Errorf("bad name %q to %s in %s", sym.Name, sym.Target, name)
-		}
-		if seen[sym.Name] != nil {
-			return fmt.Errorf("duplicate %q in %s: %v", sym.Name, name, dir)
-		}
-		seen[sym.Name] = sym
-		if strings.Contains(sym.Target, "/./") || strings.Contains(sym.Target, "//") {
-			return fmt.Errorf("bad target %q: %s in %s", sym.Target, sym.Name, name)
-		}
-	}
-	return nil
-}
-
-func (f *fakeRBE) Execute(req *rpb.ExecuteRequest, s rpb.Execution_ExecuteServer) error {
-	ctx := s.Context()
-	err := f.verifyExecuteRequest(ctx, req)
-	if err != nil {
-		return err
-	}
-	opname := uuid.New().String()
-	if !req.SkipCacheLookup {
-		aresp, err := f.GetActionResult(ctx, &rpb.GetActionResultRequest{
-			InstanceName: req.InstanceName,
-			ActionDigest: req.ActionDigest,
-		})
-		if err == nil {
-			resp := &rpb.ExecuteResponse{
-				Result:       proto.Clone(aresp).(*rpb.ActionResult),
-				CachedResult: true,
-				Status: &spb.Status{
-					Code: int32(codes.OK),
-				},
-			}
-			op, err := execRespOp(opname, resp, nil)
-			if err != nil {
-				return status.Errorf(codes.Internal, "execRespOp: %v", err)
-			}
-			return s.Send(op)
-		}
-	}
-	var resp *rpb.ExecuteResponse
-	if f.fakeExec == nil {
-		err = status.Errorf(codes.Unavailable, "exec service unavailable")
-	} else {
-		resp, err = f.fakeExec(ctx, req)
-	}
-	op, err := execRespOp(opname, resp, err)
-	if err != nil {
-		return status.Errorf(codes.Internal, "execRespOp: %v", err)
-	}
-	f.ops.Add(opname, op)
-
-	err = status.FromProto(resp.GetStatus()).Err()
-	if err == nil && resp != nil && resp.Result != nil && resp.Result.ExitCode == 0 {
-		f.cache.Set(req.ActionDigest, proto.Clone(resp.Result).(*rpb.ActionResult))
-	}
-	ops := f.ops.Get(opname)
-	for _, op := range ops {
-		err = s.Send(op)
-		if err != nil {
-			return err
-		}
-	}
-	return nil
-}
-
-func (f *fakeRBE) WaitExecution(req *rpb.WaitExecutionRequest, s rpb.Execution_WaitExecutionServer) error {
-	ctx := s.Context()
-	logger := log.FromContext(ctx)
-	name := req.Name
-	logger.Infof("wait %q", name)
-	for {
-		select {
-		case <-ctx.Done():
-			return ctx.Err()
-		default:
-		}
-		ops := f.ops.Get(name)
-		if len(ops) == 0 {
-			return status.Errorf(codes.NotFound, "no operation for %q", name)
-		}
-		logger.Infof("operation for %q: %d", name, len(ops))
-		for _, op := range ops {
-			err := s.Send(op)
-			if err != nil {
-				return err
-			}
-		}
-	}
-}
-
-func (f *fakeRBE) GetActionResult(ctx context.Context, req *rpb.GetActionResultRequest) (*rpb.ActionResult, error) {
-	if !f.isValidInstance(req.InstanceName) {
-		return nil, status.Errorf(codes.PermissionDenied, "unexpected instance name %q", req.InstanceName)
-	}
-	resp, ok := f.cache.Get(req.ActionDigest)
-	if !ok {
-		return nil, status.Errorf(codes.NotFound, "no action cache")
-	}
-	return resp, nil
-}
-
-// TODO: UpdateActionResult
-
-func (f *fakeRBE) FindMissingBlobs(ctx context.Context, req *rpb.FindMissingBlobsRequest) (*rpb.FindMissingBlobsResponse, error) {
-	if !f.isValidInstance(req.InstanceName) {
-		return nil, status.Errorf(codes.PermissionDenied, "unexpected instance name %q", req.InstanceName)
-	}
-	resp := &rpb.FindMissingBlobsResponse{}
-	for _, d := range req.BlobDigests {
-		_, ok := f.cas.Get(d)
-		if !ok {
-			resp.MissingBlobDigests = append(resp.MissingBlobDigests, d)
-		}
-	}
-	return resp, nil
-}
-
-func (f *fakeRBE) BatchUpdateBlobs(ctx context.Context, req *rpb.BatchUpdateBlobsRequest) (*rpb.BatchUpdateBlobsResponse, error) {
-	if !f.isValidInstance(req.InstanceName) {
-		return nil, status.Errorf(codes.PermissionDenied, "unexpected instance name %q", req.InstanceName)
-	}
-	var totalSize int64
-	resp := &rpb.BatchUpdateBlobsResponse{}
-	for _, req := range req.Requests {
-		d := req.Digest
-		bresp := &rpb.BatchUpdateBlobsResponse_Response{
-			Digest: d,
-		}
-		totalSize += int64(len(req.Data))
-		resp.Responses = append(resp.Responses, bresp)
-		v := digest.Bytes(cas.ResName("", d), req.Data)
-		if !proto.Equal(d, v.Digest()) {
-			bresp.Status = &spb.Status{
-				Code:    int32(codes.InvalidArgument),
-				Message: fmt.Sprintf("digest mismatch %s != %s", d, v.Digest()),
-			}
-			continue
-		}
-		f.cas.Set(v)
-		bresp.Status = &spb.Status{
-			Code: int32(codes.OK),
-		}
-	}
-	if totalSize > f.ServerCapabilities.CacheCapabilities.MaxBatchTotalSizeBytes {
-		return nil, status.Errorf(codes.InvalidArgument, "exceed server capabilities: %d > %d", totalSize, f.ServerCapabilities.CacheCapabilities.MaxBatchTotalSizeBytes)
-	}
-	return resp, nil
-}
-
-// TODO: GetTree?
-
-func (f *fakeRBE) Read(req *bpb.ReadRequest, s bpb.ByteStream_ReadServer) error {
-	if !f.isValidInstance(req.ResourceName) {
-		return status.Errorf(codes.PermissionDenied, "unexpected instance name %q", req.ResourceName)
-	}
-	d, err := cas.ParseResName(strings.TrimPrefix(req.ResourceName, f.instancePrefix+"/"))
-	if err != nil {
-		return status.Errorf(codes.InvalidArgument, "bad resource name %q: %v", req.ResourceName, err)
-	}
-	v, ok := f.cas.Get(d)
-	if !ok {
-		return status.Errorf(codes.NotFound, "%q not found", req.ResourceName)
-	}
-	ctx := s.Context()
-	rd, err := v.Open(ctx)
-	if err != nil {
-		return status.Errorf(codes.Internal, "open %q: %v", req.ResourceName, err)
-	}
-	if req.ReadOffset < 0 {
-		return status.Errorf(codes.OutOfRange, "read offset negative %d", req.ReadOffset)
-	}
-	if req.ReadOffset > 0 {
-		_, err = io.CopyN(ioutil.Discard, rd, req.ReadOffset)
-		if err != nil {
-			return status.Errorf(codes.OutOfRange, "read offset %d: %v", req.ReadOffset, err)
-		}
-	}
-	if req.ReadLimit < 0 {
-		return status.Errorf(codes.OutOfRange, "read limit negative %d", req.ReadLimit)
-	}
-	if req.ReadLimit > 0 {
-		rd = ioutil.NopCloser(&io.LimitedReader{
-			R: rd,
-			N: req.ReadLimit,
-		})
-	}
-	const maxChunkSize = 2 * 1024 * 1024
-	buf := make([]byte, maxChunkSize)
-	for {
-		n, err := rd.Read(buf)
-		if err == io.EOF {
-			break
-		}
-		if err != nil {
-			return err
-		}
-		err = s.Send(&bpb.ReadResponse{
-			Data: buf[:n],
-		})
-		if err != nil {
-			return err
-		}
-	}
-	// TODO: check digest hash?
-	return nil
-}
-
-func (f *fakeRBE) Write(s bpb.ByteStream_WriteServer) error {
-	var resourceName string
-	var d *rpb.Digest
-	var offset int64
-	var buf bytes.Buffer
-	for {
-		req, err := s.Recv()
-		if err != nil {
-			return err
-		}
-		if req.ResourceName != "" {
-			if resourceName == "" {
-				resourceName = req.ResourceName
-				if !f.isValidInstance(req.ResourceName) {
-					return status.Errorf(codes.PermissionDenied, "unexpected instance name %q", req.ResourceName)
-				}
-				d, err = cas.ParseResName(strings.TrimPrefix(req.ResourceName, f.instancePrefix+"/"))
-				if err != nil {
-					return status.Errorf(codes.InvalidArgument, "bad resource name %q: %v", req.ResourceName, err)
-				}
-				// TODO: allow non zero offset?
-			} else if req.ResourceName != resourceName {
-				return status.Errorf(codes.InvalidArgument, "resource name mismatch %q => %q", resourceName, req.ResourceName)
-			}
-		}
-		if resourceName == "" {
-			return status.Errorf(codes.InvalidArgument, "no resource name")
-		}
-		if req.WriteOffset != offset {
-			return status.Errorf(codes.InvalidArgument, "invalid offset %d; want=%d", req.WriteOffset, offset)
-		}
-		buf.Write(req.Data)
-		offset += int64(len(req.Data))
-		if req.FinishWrite {
-			break
-		}
-	}
-	data := digest.Bytes(cas.ResName("", d), buf.Bytes())
-	if !proto.Equal(d, data.Digest()) {
-		return status.Errorf(codes.InvalidArgument, "digest mismatch %s != %s", d, data.Digest())
-	}
-	f.cas.Set(data)
-	return s.SendAndClose(&bpb.WriteResponse{
-		CommittedSize: offset,
-	})
-}
-
-// TODO: QueryWriteStatus?
-
-func (f *fakeRBE) GetCapabilities(ctx context.Context, req *rpb.GetCapabilitiesRequest) (*rpb.ServerCapabilities, error) {
-	if !f.isValidInstance(req.InstanceName) {
-		return nil, status.Errorf(codes.PermissionDenied, "unexpected instance name %q", req.InstanceName)
-	}
-	return f.ServerCapabilities, nil
-}
diff --git a/remoteexec/fake_rbe_test.go b/remoteexec/fake_rbe_test.go
deleted file mode 100644
index a0460b3..0000000
--- a/remoteexec/fake_rbe_test.go
+++ /dev/null
@@ -1,451 +0,0 @@
-// Copyright 2018 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.
-
-package remoteexec
-
-import (
-	"bytes"
-	"context"
-	"fmt"
-	"io"
-	"io/ioutil"
-	"math/rand"
-	"path"
-	"testing"
-	"time"
-
-	rpb "github.com/bazelbuild/remote-apis/build/bazel/remote/execution/v2"
-
-	"github.com/google/go-cmp/cmp"
-	bpb "google.golang.org/genproto/googleapis/bytestream"
-	"google.golang.org/grpc"
-	"google.golang.org/grpc/codes"
-	"google.golang.org/grpc/status"
-	"google.golang.org/protobuf/proto"
-
-	"go.chromium.org/goma/server/bytestreamio"
-	"go.chromium.org/goma/server/remoteexec/cas"
-	"go.chromium.org/goma/server/remoteexec/digest"
-	"go.chromium.org/goma/server/rpc/grpctest"
-)
-
-func setup(rbe *fakeRBE) (client Client, stop func(), err error) {
-	srv := grpc.NewServer()
-	registerFakeRBE(srv, rbe)
-	addr, serverStop, err := grpctest.StartServer(srv)
-	if err != nil {
-		return Client{}, func() {}, err
-	}
-	conn, err := grpc.Dial(addr, grpc.WithInsecure())
-	if err != nil {
-		serverStop()
-		return Client{}, func() {}, err
-	}
-	return Client{
-			ClientConn: conn,
-		}, func() {
-			conn.Close()
-			serverStop()
-		}, nil
-}
-
-func TestFakeActionCache(t *testing.T) {
-	rbe := newFakeRBE()
-	client, stop, err := setup(rbe)
-	if err != nil {
-		t.Fatal(err)
-	}
-	defer stop()
-	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
-	defer cancel()
-
-	actionDigest := digest.Bytes("dummy-action-digest", []byte("dummy-action-digest"))
-	req := &rpb.GetActionResultRequest{
-		ActionDigest: actionDigest.Digest(),
-	}
-
-	req.InstanceName = "bad-instance"
-	t.Logf(`GetActionResult(instance_name=%q)`, req.InstanceName)
-	resp, err := client.GetActionResult(ctx, req)
-	if got, want := status.Convert(err).Code(), codes.PermissionDenied; got != want {
-		t.Errorf(`GetActionResult(ctx, req)=%v, %v; want %v`, resp, err, want)
-	}
-
-	req.InstanceName = path.Join(rbe.instancePrefix, "default_instance")
-	t.Logf(`GetActionResult(instance_name=%q, no action digest)`, req.InstanceName)
-	resp, err = client.GetActionResult(ctx, req)
-	if got, want := status.Convert(err).Code(), codes.NotFound; got != want {
-		t.Errorf(`GetActionResult(ctx, req)=%v, %v; want %v`, resp, err, want)
-	}
-
-	result := &rpb.ActionResult{
-		StdoutDigest: digest.Bytes("stdout", []byte("ok")).Digest(),
-	}
-	rbe.cache.Set(actionDigest.Digest(), result)
-
-	t.Logf(`GetActionResult(instance_name=%q, %s)`, req.InstanceName, cas.ResName("", actionDigest.Digest()))
-	resp, err = client.GetActionResult(ctx, req)
-	if err != nil || !proto.Equal(resp, result) {
-		t.Errorf(`GetActionResult(ct, req)=%v, %v; want %v, nil`, resp, err, result)
-	}
-}
-
-func TestFakeCAS(t *testing.T) {
-	rbe := newFakeRBE()
-	client, stop, err := setup(rbe)
-	if err != nil {
-		t.Fatal(err)
-	}
-	defer stop()
-	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
-	defer cancel()
-
-	largeFile := make([]byte, 6*1024*1024)
-	rand.Read(largeFile)
-	data := []digest.Data{
-		digest.Bytes("data0", []byte("data0")),
-		digest.Bytes("data1", []byte("data1")),
-		digest.Bytes("data2", []byte("data2")),
-		digest.Bytes("large-data", largeFile),
-	}
-	var digests []*rpb.Digest
-	for _, d := range data {
-		digests = append(digests, d.Digest())
-	}
-
-	fmbReq := &rpb.FindMissingBlobsRequest{
-		BlobDigests: digests,
-	}
-
-	t.Logf(`FindMissingBlobs(instance_name="bad-instance")`)
-	fmbReq.InstanceName = "bad-instance"
-	fmbResp, err := client.FindMissingBlobs(ctx, fmbReq)
-	if got, want := status.Convert(err).Code(), codes.PermissionDenied; got != want {
-		t.Errorf(`FindMissingBlobs(ctx, req)=%v, %v; want %v`, fmbResp, err, want)
-	}
-
-	fmbReq.InstanceName = path.Join(rbe.instancePrefix, "default_instance")
-	t.Logf(`FindMissingBlobs(instance_name=%q)`, fmbReq.InstanceName)
-	fmbResp, err = client.FindMissingBlobs(ctx, fmbReq)
-	if err != nil || !proto.Equal(fmbResp, &rpb.FindMissingBlobsResponse{
-		MissingBlobDigests: digests,
-	}) {
-		t.Errorf(`FindMissingBlobs(ctx, req)=%v, %v; want %v, nil`, fmbResp, err, digests)
-	}
-
-	bubReq := &rpb.BatchUpdateBlobsRequest{}
-	var updates []*rpb.Digest
-	var noUpdates []*rpb.Digest
-	for i, d := range data {
-		if i%2 == 1 {
-			noUpdates = append(noUpdates, d.Digest())
-			continue
-		}
-		rd, err := d.Open(ctx)
-		if err != nil {
-			t.Fatalf("data[%d].Open()=%v, %v", i, rd, err)
-		}
-		buf, err := ioutil.ReadAll(rd)
-		rd.Close()
-		req := &rpb.BatchUpdateBlobsRequest_Request{
-			Digest: d.Digest(),
-			Data:   buf,
-		}
-		bubReq.Requests = append(bubReq.Requests, req)
-		updates = append(updates, d.Digest())
-	}
-	updateResult := func(resp *rpb.BatchUpdateBlobsResponse) (stored, failed []*rpb.Digest) {
-		for _, r := range resp.Responses {
-			err := status.FromProto(r.Status).Err()
-			if err != nil {
-				failed = append(failed, r.Digest)
-				continue
-			}
-			stored = append(stored, r.Digest)
-		}
-		return stored, failed
-	}
-
-	t.Logf(`BatchUpdateBlobs(instance_name="bad-instance")`)
-	bubReq.InstanceName = "bad-instance"
-	bubResp, err := client.BatchUpdateBlobs(ctx, bubReq)
-	if got, want := status.Convert(err).Code(), codes.PermissionDenied; got != want {
-		t.Errorf(`BatchUpdateBlobs(ctx, req)=%v, %v; want %v`, bubResp, err, want)
-	}
-
-	instanceName := path.Join(rbe.instancePrefix, "default_instance")
-	t.Logf(`BatchUpdateBlobs(instance_name=%q, %v)`, instanceName, bubReq)
-	bubReq.InstanceName = instanceName
-	bubResp, err = client.BatchUpdateBlobs(ctx, bubReq)
-	stored, failed := updateResult(bubResp)
-	if err != nil || !cmp.Equal(stored, updates, cmp.Comparer(proto.Equal)) || len(failed) > 0 {
-		t.Errorf(`BatchUpdateBlobs(ctx, req)=%v, %v; want=%v, nil`, bubResp, err, updates)
-	}
-
-	t.Logf(`FindMissingBlobs(instance_name=%q) after update`, instanceName)
-	fmbResp, err = client.FindMissingBlobs(ctx, fmbReq)
-	if err != nil || !proto.Equal(fmbResp, &rpb.FindMissingBlobsResponse{
-		MissingBlobDigests: noUpdates,
-	}) {
-		t.Errorf(`FindMissingBlobs(ctx, req)=%v, %v; want %v, nil`, fmbResp, err, digests)
-	}
-
-	t.Logf(`bytestream check`)
-	resname := "bad-resource-name"
-	err = bytestreamDownloadCheck(ctx, client, resname, data[0])
-	if got, want := status.Convert(err).Code(), codes.PermissionDenied; got != want {
-		t.Errorf(`bytestreamDownloadCheck(ctx, client, %q, data[0])=%v; want %v`, resname, err, want)
-	}
-
-	resname = path.Join(instanceName, "blobs/bad/num")
-	err = bytestreamDownloadCheck(ctx, client, resname, data[0])
-	if got, want := status.Convert(err).Code(), codes.InvalidArgument; got != want {
-		t.Errorf(`bytestreamDownloadCheck(ctx, client, %q, data[0])=%v; want %v`, resname, err, want)
-	}
-
-	resname = cas.ResName(instanceName, data[0].Digest())
-	err = bytestreamDownloadCheck(ctx, client, resname, data[0])
-	if err != nil {
-		t.Errorf(`bytestreamDownloadCheck(ctx, client, %q, data[0])=%v; want %v`, resname, err, nil)
-	}
-
-	resname = cas.ResName(instanceName, data[1].Digest())
-	err = bytestreamDownloadCheck(ctx, client, resname, data[1])
-	if got, want := status.Convert(err).Code(), codes.NotFound; got != want {
-		t.Errorf(`bytestreamDownloadCheck(ctx, client, %q, data[1])=%v; want %v`, resname, err, want)
-	}
-
-	resname = cas.ResName(instanceName, data[2].Digest())
-	err = bytestreamDownloadCheck(ctx, client, resname, data[2])
-	if err != nil {
-		t.Errorf(`bytestreamDownloadCheck(ctx, client, %q, data[2])=%v; want %v`, resname, err, nil)
-	}
-
-	resname = cas.ResName(instanceName, data[3].Digest())
-	err = bytestreamDownloadCheck(ctx, client, resname, data[3])
-	if got, want := status.Convert(err).Code(), codes.NotFound; got != want {
-		t.Errorf(`bytestreamDownloadCheck(ctx, client, %q, data[3])=%v; want %v`, resname, err, want)
-	}
-
-	resname = "bad-resource-name"
-	t.Logf(`bytestreamio.Create %q`, resname)
-	err = bytestreamUpload(ctx, client.ByteStream(), resname, data[1])
-	if got, want := status.Convert(err).Code(), codes.PermissionDenied; got != want {
-		t.Errorf(`bytestreamUpload(ctx, client, %q, %v)=%v; want %v`, resname, data[1], err, want)
-	}
-
-	resname = path.Join(instanceName, "blobs/bad/num")
-	t.Logf(`bytestreamio.Create %q`, resname)
-	err = bytestreamUpload(ctx, client.ByteStream(), resname, data[1])
-	if got, want := status.Convert(err).Code(), codes.InvalidArgument; got != want {
-		t.Errorf(`bytestreamUpload(ctx, client, %q, %v)=%v; want %v`, resname, data[1], err, want)
-	}
-
-	resname = cas.UploadResName(instanceName, data[1].Digest())
-	t.Logf(`bytestreamUpload %q`, resname)
-	err = bytestreamUpload(ctx, client.ByteStream(), resname, data[1])
-	if err != nil {
-		t.Errorf(`bytestreamUpload(ctx, client, %q, %v)=%v; want nil error`, resname, data[1], err)
-	}
-
-	t.Logf(`bytestreamUpload %q again`, resname)
-	err = bytestreamUpload(ctx, client.ByteStream(), resname, data[1])
-	if err != nil {
-		t.Errorf(`2nd bytestreamUpload(ctx, client, %q, %v)=%v; want nil error`, resname, data[1], err)
-	}
-
-	resname = cas.UploadResName(instanceName, data[1].Digest())
-	t.Logf(`bytestreamUpload %q`, resname)
-	err = bytestreamUpload(ctx, client.ByteStream(), resname, data[1])
-	if err != nil {
-		t.Errorf(`bytestreamUpload(ctx, client, %q, %v)=%v; want nil error`, resname, data[1], err)
-	}
-
-	resname = cas.UploadResName(instanceName, data[3].Digest())
-	t.Logf(`bytestreamUpload %q`, resname)
-	err = bytestreamUpload(ctx, client.ByteStream(), resname, data[3])
-	if err != nil {
-		t.Errorf(`bytestreamUpload(ctx, client, %q, %v)=%v; want nil error`, resname, data[3], err)
-	}
-
-	resname = cas.ResName(instanceName, data[1].Digest())
-	err = bytestreamDownloadCheck(ctx, client, resname, data[1])
-	if err != nil {
-		t.Errorf(`bytestreamDownloadCheck(ctx, client, %q, data[1])=%v; want %v`, resname, err, nil)
-	}
-
-	resname = cas.ResName(instanceName, data[3].Digest())
-	err = bytestreamDownloadCheck(ctx, client, resname, data[3])
-	if err != nil {
-		t.Errorf(`bytestreamDownloadCheck(ctx, client, %q, data[3])=%v; want %v`, resname, err, nil)
-	}
-
-	t.Logf(`FindMissingBlobs(instance_name=%q)`, instanceName)
-	fmbReq.InstanceName = instanceName
-	fmbResp, err = client.FindMissingBlobs(ctx, fmbReq)
-	if err != nil || !proto.Equal(fmbResp, &rpb.FindMissingBlobsResponse{}) {
-		t.Errorf(`FindMissingBlobs(ctx, req)=%v, %v; want %v, nil`, fmbResp, err, digests)
-	}
-}
-
-func bytestreamDownloadCheck(ctx context.Context, client bpb.ByteStreamClient, resname string, data digest.Data) error {
-	r, err := data.Open(ctx)
-	if err != nil {
-		return err
-	}
-	defer r.Close()
-	var dbuf bytes.Buffer
-	_, err = io.Copy(&dbuf, r)
-	if err != nil {
-		return err
-	}
-
-	rd, err := bytestreamio.Open(ctx, client, resname)
-	if err != nil {
-		return err
-	}
-	var buf bytes.Buffer
-	_, err = io.Copy(&buf, rd)
-	if err != nil {
-		return err
-	}
-
-	if !bytes.Equal(dbuf.Bytes(), buf.Bytes()) {
-		return fmt.Errorf("digest content mismatch for %s", resname)
-	}
-	return nil
-}
-
-func bytestreamUpload(ctx context.Context, client bpb.ByteStreamClient, resname string, data digest.Data) error {
-	rd, err := data.Open(ctx)
-	if err != nil {
-		return err
-	}
-	defer rd.Close()
-	wr, err := bytestreamio.Create(ctx, client, resname)
-	if err != nil {
-		return err
-	}
-	// limit 2MB buffer.
-	buf := make([]byte, 2*1024*1024)
-	// drop WriteTo method in rd.
-	type ioReader struct{ io.Reader }
-	_, err = io.CopyBuffer(wr, ioReader{rd}, buf)
-	if err != nil {
-		wr.Close()
-		return err
-	}
-	return wr.Close()
-}
-
-func mustDigestProto(t *testing.T, m proto.Message) digest.Data {
-	t.Helper()
-	data, err := digest.Proto(m)
-	if err != nil {
-		t.Fatal(err)
-	}
-	return data
-}
-
-func TestFakeExecute(t *testing.T) {
-	rbe := newFakeRBE()
-	wantResp := proto.Clone(rbe.execResp).(*rpb.ExecuteResponse)
-
-	client, stop, err := setup(rbe)
-	if err != nil {
-		t.Fatal(err)
-	}
-	defer stop()
-	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
-	defer cancel()
-
-	commandData := mustDigestProto(t, &rpb.Command{
-		Arguments: []string{"echo", "hello, world"},
-		Platform:  &rpb.Platform{},
-	})
-	helloData := digest.Bytes("hello.txt", []byte("hello"))
-	rbe.cas.Set(helloData)
-	inputRootData := mustDigestProto(t, &rpb.Directory{
-		Files: []*rpb.FileNode{
-			{
-				Name:   "hello.txt",
-				Digest: helloData.Digest(),
-			},
-		},
-	})
-
-	actionData := mustDigestProto(t, &rpb.Action{})
-	rbe.cas.Set(actionData)
-
-	req := &rpb.ExecuteRequest{
-		ActionDigest: actionData.Digest(),
-	}
-
-	req.InstanceName = "bad-instance"
-	t.Logf(`Execute(instance_name=%q)`, req.InstanceName)
-	opname, resp, err := ExecuteAndWait(ctx, client, req)
-	if got, want := status.Convert(err).Code(), codes.PermissionDenied; got != want {
-		t.Errorf(`ExecuteAndWait(ctx, client, req)=%q, %v, %v; want %v`, opname, resp, err, want)
-	}
-
-	req.InstanceName = path.Join(rbe.instancePrefix, "default_instance")
-	t.Logf(`Execute(instance_name=%q)`, req.InstanceName)
-	opname, resp, err = ExecuteAndWait(ctx, client, req)
-	if got, want := status.Convert(err).Code(), codes.InvalidArgument; got != want {
-		t.Errorf(`ExecuteAndWait(ctx, client, req)=%q, %v, %v; want %v`, opname, resp, err, want)
-	}
-
-	t.Logf(`set command digest`)
-	actionData = mustDigestProto(t, &rpb.Action{
-		CommandDigest: commandData.Digest(),
-	})
-	rbe.cas.Set(actionData)
-	req.ActionDigest = actionData.Digest()
-	opname, resp, err = ExecuteAndWait(ctx, client, req)
-	if got, want := status.Convert(err).Code(), codes.InvalidArgument; got != want {
-		t.Errorf(`ExecuteAndWait(ctx, client, req)=%q, %v, %v; want %v`, opname, resp, err, want)
-	}
-
-	t.Logf(`store command data`)
-	rbe.cas.Set(commandData)
-	opname, resp, err = ExecuteAndWait(ctx, client, req)
-	if got, want := status.Convert(err).Code(), codes.InvalidArgument; got != want {
-		t.Errorf(`ExecuteAndWait(ctx, client, req)=%q, %v, %v; want %v`, opname, resp, err, want)
-	}
-
-	t.Logf(`set input root digest`)
-	actionData = mustDigestProto(t, &rpb.Action{
-		CommandDigest:   commandData.Digest(),
-		InputRootDigest: inputRootData.Digest(),
-	})
-	rbe.cas.Set(actionData)
-	req.ActionDigest = actionData.Digest()
-	opname, resp, err = ExecuteAndWait(ctx, client, req)
-	if got, want := status.Convert(err).Code(), codes.InvalidArgument; got != want {
-		t.Errorf(`ExecuteAndWait(ctx, client, req)=%q, %v, %v; want %v`, opname, resp, err, want)
-	}
-
-	t.Logf(`store input_root data`)
-	rbe.cas.Set(inputRootData)
-	opname, resp, err = ExecuteAndWait(ctx, client, req)
-	if err != nil || !proto.Equal(resp, wantResp) {
-		t.Errorf(`ExecuteAndWait(ctx, client, req)=%q, %v, %v; want %v, nil error`, opname, resp, err, wantResp)
-	}
-
-	t.Logf(`Execute(instance_name=%q) cached`, req.InstanceName)
-	opname, resp, err = ExecuteAndWait(ctx, client, req)
-	if err != nil || !proto.Equal(resp.Result, wantResp.Result) {
-		t.Errorf(`ExecuteAndWait(ctx, client, req)=%q, %v, %v; want %v, nil error`, opname, resp, err, wantResp)
-	}
-	if !resp.CachedResult {
-		t.Errorf(`resp.CachedResult=%t; want=true`, resp.CachedResult)
-	}
-
-	req.SkipCacheLookup = true
-	t.Logf(`Execute(instance_name=%q) skip cache`, req.InstanceName)
-	opname, resp, err = ExecuteAndWait(ctx, client, req)
-	if err != nil || !proto.Equal(resp.Result, wantResp.Result) {
-		t.Errorf(`ExecuteAndWait(ctx, client, req)=%q, %v, %v; want %v, nil error`, opname, resp, err, wantResp)
-	}
-}
diff --git a/remoteexec/gcc.go b/remoteexec/gcc.go
deleted file mode 100644
index daf659f..0000000
--- a/remoteexec/gcc.go
+++ /dev/null
@@ -1,407 +0,0 @@
-// Copyright 2018 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.
-
-package remoteexec
-
-import (
-	"errors"
-	"fmt"
-	"strings"
-)
-
-// longest first
-var pathFlags = []string{
-	"-fcoverage-compilation-dir=",
-	"-fcrash-diagnostics-dir=",
-	"-fdebug-compilation-dir=",
-	"-ffile-compilation-dir=",
-	"-fprofile-sample-use=",
-	"-fsanitize-blacklist=",
-	"-fprofile-instr-use=",
-	"-fprofile-list=",
-	"-resource-dir=",
-	"--include=",
-	"--sysroot=",
-	"--include",
-	"-include=",
-	"-include",
-	"-isystem",
-	"-o",
-	"-B",
-	"-F",
-	"-I",
-}
-
-// TODO: share exec/gcc.go ?
-
-// 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).
-//
-// The request will be relocatable if path used in arg is cwd relative.
-//
-// 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
-//
-// 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)
-//	-I<path>
-//	    *.d file output will not be cwd-agnostic.
-//	    directory table in debug info (debug build)
-//	-B<path>
-//	-isystem<path> --sysroot=<path>
-//	...
-//	TODO: these could be normalized to cwd relative?
-//
-// ref:
-// https://docs.google.com/spreadsheets/d/1_-ZJhqy7WhSFYuZU2QkmQ4Ed9182bWfKg09EfBAkVf8/edit#gid=759603323
-//
-// 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 {
-		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)
-				}
-				if fp == "-fdebug-compilation-dir=" || fp == "-ffile-compilation-dir=" {
-					switch subCmd {
-					case "clang":
-						subArgs[subCmd] = append(subArgs[subCmd], arg)
-						fallthrough
-					case "":
-						debugCompilationDir = true
-						continue Loop
-					}
-					return errors.New("-fdebug-compilation-dir= and -ffile-compilation-dir= not supported for " + subCmd)
-				}
-				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)
-			subCmd = ""
-
-		case strings.HasPrefix(arg, "-g"):
-			if arg == "-g0" {
-				debugFlags = nil
-				continue
-			}
-			debugFlags = append(debugFlags, arg)
-		case arg == "--pnacl-allow-translate": // crbug.com/685461
-			return errors.New("pnacl-allow-translate")
-
-		case strings.HasPrefix(arg, "-Wa,"): // assembler arg
-			subArgs["as"] = append(subArgs["as"], strings.Split(arg[len("-Wa,"):], ",")...)
-		case strings.HasPrefix(arg, "-Wl,"): // linker arg
-			subArgs["ld"] = append(subArgs["ld"], strings.Split(arg[len("-Wl,"):], ",")...)
-		case strings.HasPrefix(arg, "-Wp,"): // preproc arg
-			subArgs["cpp"] = append(subArgs["cpp"], strings.Split(arg[len("-Wp,"):], ",")...)
-		case arg == "-Xclang":
-			subCmd = "clang"
-
-		case arg == "-mllvm":
-			// -mllvm <value>  Additional arguments to forward to LLVM's option processing
-			subCmd = "llvm"
-
-		case strings.HasPrefix(arg, "-w"): // inhibit all warnings
-		case strings.HasPrefix(arg, "-W"): // warning
-		case strings.HasPrefix(arg, "-D"): // define
-		case strings.HasPrefix(arg, "-U"): // undefine
-		case strings.HasPrefix(arg, "-O"): // optimize
-		case strings.HasPrefix(arg, "-f"): // feature
-		case strings.HasPrefix(arg, "-m"):
-			// -m64, -march=x86-64
-		case arg == "-arch":
-		case arg == "-target":
-		case strings.HasPrefix(arg, "--target="):
-
-		case strings.HasPrefix(arg, "-no"):
-			// -no-canonical-prefixes, -nostdinc++
-		case arg == "-integrated-as":
-		case arg == "-pedantic":
-		case arg == "-pipe":
-		case arg == "-pie":
-		case arg == "-pthread":
-		case arg == "-c":
-		case strings.HasPrefix(arg, "-std"):
-		case strings.HasPrefix(arg, "--param="):
-		case arg == "-MMD" || arg == "-MD" || arg == "-M":
-		case arg == "-Qunused-arguments":
-		case arg == "-static-libgcc":
-		case strings.HasPrefix(arg, "--rtlib="):
-		case strings.HasPrefix(arg, "--unwindlib="):
-			continue
-
-		case arg == "-o":
-			pathFlag = true
-		case arg == "-I" || arg == "-B" || arg == "-F" || arg == "-isystem" || arg == "-include" || arg == "-iframework":
-			pathFlag = true
-		case arg == "-MF":
-			pathFlag = true
-		case arg == "-isysroot":
-			pathFlag = true
-		case arg == "--sysroot":
-			pathFlag = true
-		case arg == "-idirafter":
-			pathFlag = true
-
-		case strings.HasPrefix(arg, "-"): // unknown flag?
-			return unknownFlagError{arg: arg}
-
-		default: // input file?
-			if filepath.IsAbs(arg) {
-				return fmt.Errorf("abs path: %s", arg)
-			}
-		}
-	}
-
-	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 := clangArgRelocatable(filepath, args)
-				if err != nil {
-					return err
-				}
-			case "llvm":
-				err := llvmArgRelocatable(filepath, args)
-				if err != nil {
-					return err
-				}
-			case "as":
-				err := asArgRelocatable(filepath, args)
-				if err != nil {
-					return err
-				}
-			default:
-				return unknownFlagError{arg: fmt.Sprintf("unsupported subcommand %s: %s", cmd, args)}
-			}
-		}
-	}
-
-	for _, env := range envs {
-		e := strings.SplitN(env, "=", 2)
-		if len(e) != 2 {
-			return fmt.Errorf("bad environment variable: %s", env)
-		}
-		if e[0] == "PWD" {
-			continue
-		}
-		if filepath.IsAbs(e[1]) {
-			return fmt.Errorf("abs path in env %s=%s", e[0], e[1])
-		}
-	}
-	return nil
-}
-
-func clangArgRelocatable(filepath clientFilePath, args []string) error {
-	pathFlag := false
-	skipFlag := false
-	for _, arg := range args {
-		switch {
-		case pathFlag:
-			if filepath.IsAbs(arg) {
-				return fmt.Errorf("clang abs path: %s", arg)
-			}
-			pathFlag = false
-		case skipFlag:
-			skipFlag = false
-
-		case arg == "-mllvm", arg == "-add-plugin", arg == "-fdebug-compilation-dir", arg == "-target-feature":
-			// TODO: pass llvmArgRelocatable for -mllvm?
-			skipFlag = true
-		case strings.HasPrefix(arg, "-plugin-arg-"):
-			skipFlag = true
-		case arg == "-load":
-			pathFlag = true
-		case strings.HasPrefix(arg, "-f"): // feature
-		case strings.HasPrefix(arg, "-debug-info-kind="):
-		case arg == "-no-opaque-pointers":
-		default:
-			return unknownFlagError{arg: fmt.Sprintf("clang: %s", arg)}
-		}
-	}
-	return nil
-}
-
-func llvmArgRelocatable(filepath clientFilePath, args []string) error {
-	for _, arg := range args {
-		switch {
-		case strings.HasPrefix(arg, "-asan-"):
-			// https://b/issues/141210713#comment3
-			// -mllvm -asan-globals=0
-			// https://github.com/llvm-mirror/llvm/blob/ef512ca8e66e2d6abee71b9729b2887cb094cb6e/lib/Transforms/Instrumentation/AddressSanitizer.cpp
-			// -asan-* has no path related options
-
-		case strings.HasPrefix(arg, "-regalloc="):
-			// https://b/issues/141210713#comment4
-			// -mllvm -regalloc=pbqp
-			// https://github.com/llvm-mirror/llvm/blob/be9f44f943df228dbca68139efef55f2c7666563/lib/CodeGen/TargetPassConfig.cpp
-			// -regalloc= doesn't take path related value,
-			// "basic", "fast", "greedy", "pbqp", etc.
-
-		case strings.HasPrefix(arg, "-pbqp-"):
-			// https://b/issues/141210713#comment4
-			// -mllvm -pbqp-coalescing
-			// https://github.com/llvm-mirror/llvm/blob/114087caa6f95b526861c3af94b3093d9444c57b/lib/CodeGen/RegAllocPBQP.cpp
-
-		case strings.HasPrefix(arg, "-instcombine-"):
-			// https://b/issues/161304121
-			// -mllvm -instcombine-lower-dbg-declare=0
-			// -instcombine-* defined in
-			// https://github.com/llvm/llvm-project/blob/8e9a505139fbef7d2e6e9d0adfe1efc87326f9ef/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
-
-		case strings.HasPrefix(arg, "-basic-aa"):
-			// -mllvm -basic-aa-recphi=0
-			// https://github.com/llvm/llvm-project/blob/694ded37b9d70e385addfc482d298b054073ebe1/llvm/lib/Analysis/BasicAliasAnalysis.cpp
-
-		case strings.HasPrefix(arg, "-sanitizer-coverage-"):
-			// -mllvm -sanitizer-coverage-prune-blocks=1
-			// https://github.com/llvm/llvm-project/blob/93ec6cd684265161623b4ea67836f022cd18c224/llvm/lib/Transforms/Instrumentation/SanitizerCoverage.cpp
-
-		case strings.HasPrefix(arg, "-enable-dse-memoryssa="):
-			// https://crbug.com/1127713
-			// -mllvm -enable-dse-memoryssa={true,false}
-			// doesn't take a path related value.
-
-		case strings.HasPrefix(arg, "-enable-ml-inliner="):
-			// --mllvm -enable-ml-inliner={development,release}
-			// does't take a path related value.
-
-		case strings.HasPrefix(arg, "-training-log="):
-			if filepath.IsAbs(arg[len("-training-log="):]) {
-				return fmt.Errorf("abs path: %s", arg)
-			}
-
-		case strings.HasPrefix(arg, "-ml-inliner-model-under-training="):
-			if filepath.IsAbs(arg[len("-ml-inliner-model-under-training="):]) {
-				return fmt.Errorf("abs path: %s", arg)
-			}
-
-		case strings.HasPrefix(arg, "-limited-coverage-experimental="):
-			// b/229939600 -limited-coverage-experimental=true
-			// doesn't take a path related value.
-
-		default:
-			return unknownFlagError{arg: fmt.Sprintf("llvm: %s", arg)}
-		}
-	}
-	return nil
-}
-
-func asArgRelocatable(filepath clientFilePath, args []string) error {
-	for _, arg := range args {
-		switch {
-		case arg == "--fatal-warnings":
-			// b/173641495
-			// https://github.com/llvm/llvm-project/blob/ffc5d98d2c0df5f72ce67e5dcb724b64f03f639b/llvm/lib/MC/MCTargetOptionsCommandFlags.cpp
-		default:
-			return unknownFlagError{arg: fmt.Sprintf("as: %s", arg)}
-		}
-	}
-	return nil
-}
-
-// gccOutputs returns output files from gcc command line.
-// TODO: implicit obj output (without -o, but -c).
-// TODO: -MD / -MMD without -MF case.
-func gccOutputs(args []string) []string {
-	var outputs []string
-	var objout string
-	outputArg := false
-	splitDwarf := false
-	mfArg := false
-
-	for _, arg := range args {
-		switch {
-		case arg == "-o":
-			outputArg = true
-		case outputArg:
-			objout = arg
-			outputArg = false
-		case strings.HasPrefix(arg, "-o"):
-			objout = arg[2:]
-
-		case arg == "-gsplit-dwarf":
-			splitDwarf = true
-
-		case arg == "-MF":
-			mfArg = true
-		case mfArg:
-			outputs = append(outputs, arg)
-			mfArg = false
-		case strings.HasPrefix(arg, "-MF"):
-			outputs = append(outputs, arg[3:])
-
-		}
-	}
-	if objout != "" {
-		outputs = append(outputs, objout)
-		if splitDwarf {
-			outputs = append(outputs, strings.TrimSuffix(objout, ".o")+".dwo")
-		}
-	}
-	return outputs
-}
diff --git a/remoteexec/gcc_test.go b/remoteexec/gcc_test.go
deleted file mode 100644
index 773880a..0000000
--- a/remoteexec/gcc_test.go
+++ /dev/null
@@ -1,706 +0,0 @@
-// Copyright 2018 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.
-
-package remoteexec
-
-import (
-	"reflect"
-	"strings"
-	"testing"
-
-	"go.chromium.org/goma/server/command/descriptor/posixpath"
-)
-
-func TestGccPathFlags(t *testing.T) {
-	lastP := pathFlags[0]
-	for _, p := range pathFlags[1:] {
-		if len(lastP) < len(p) {
-			t.Errorf("%q is longer than %q", p, lastP)
-		}
-		lastP = p
-	}
-}
-
-func TestGccRelocatableReq(t *testing.T) {
-
-	baseReleaseArgs := []string{
-		"../../third_party/llvm-build/Release+Asserts/bin/clang++",
-		"-MMD",
-		"-MF",
-		"obj/base/base/time.o.d",
-		"-DUSE_SYMBOLIZE",
-		"-I../..",
-		"-fno-strict-aliasing",
-		"--param=ssp-buffer-size=4",
-		"-fPIC",
-		"-pipe",
-		"-B../../third_party/binutils/Linux_x64/Release/bin",
-		"-pthread",
-		"-Xclang",
-		"-mllvm",
-		"-Xclang",
-		"-instcombine-lower-dbg-declare=0",
-		"-no-canonical-prefixes",
-		"-m64",
-		"-march=x86-64",
-		"-Wall",
-		"-g0",
-		"-Xclang",
-		"-load",
-		"-Xclang",
-		"../../third_party/llvm-build/Release+Asserts/lib/libFindBadCustructs.so",
-		"-Xclang",
-		"-add-plugin",
-		"-Xclang",
-		"find-bad-constructs",
-		"-Xclang",
-		"-plugin-arg-find-bad-constructs",
-		"-Xclang",
-		"check-enum-max-value",
-		"-isystem../../build/linux/debian_sid_amd64-sysroot/usr/include/glib-2.0",
-		"-O2",
-		"--sysroot=../../build/linux/debian_sid_amd64-sysroot",
-		"-c",
-		"../../base/time/time.cc",
-		"-o",
-		"obj/base/base/time.o",
-	}
-
-	baseDebugArgs := []string{
-		"../../third_party/llvm-build/Release+Asserts/bin/clang++",
-		"-MMD",
-		"-MF",
-		"obj/base/base/time.o.d",
-		"-DUSE_SYMBOLIZE",
-		"-I../..",
-		"-fno-strict-aliasing",
-		"--param=ssp-buffer-size=4",
-		"-fPIC",
-		"-pipe",
-		"-B../../third_party/binutils/Linux_x64/Release/bin",
-		"-pthread",
-		"-Xclang",
-		"-mllvm",
-		"-Xclang",
-		"-instcombine-lower-dbg-declare=0",
-		"-no-canonical-prefixes",
-		"-m64",
-		"-march=x86-64",
-		"-Wall",
-		"-g2",
-		"-gsplit-dwarf",
-		"-Xclang",
-		"-load",
-		"-Xclang",
-		"../../third_party/llvm-build/Release+Asserts/lib/libFindBadCustructs.so",
-		"-Xclang",
-		"-add-plugin",
-		"-Xclang",
-		"find-bad-constructs",
-		"-Xclang",
-		"-plugin-arg-find-bad-constructs",
-		"-Xclang",
-		"check-enum-max-value",
-		"-isystem../../build/linux/debian_sid_amd64-sysroot/usr/include/glib-2.0",
-		"-O2",
-		"--sysroot=../../build/linux/debian_sid_amd64-sysroot",
-		"-c",
-		"../../base/time/time.cc",
-		"-o",
-		"obj/base/base/time.o",
-	}
-
-	modifyArgs := func(args []string, prefix, replace string) []string {
-		var ret []string
-		found := false
-		for _, arg := range args {
-			if strings.HasPrefix(arg, prefix) {
-				ret = append(ret, replace)
-				found = true
-				continue
-			}
-			ret = append(ret, arg)
-		}
-		if !found {
-			ret = append(ret, replace)
-		}
-		return ret
-	}
-
-	for _, tc := range []struct {
-		desc        string
-		args        []string
-		envs        []string
-		relocatable bool
-	}{
-		{
-			desc: "chromium base release",
-			args: baseReleaseArgs,
-			envs: []string{
-				"PWD=/b/c/b/linux/src/out/Release",
-			},
-			relocatable: true,
-		},
-		{
-			desc: "chromium base debug",
-			args: baseDebugArgs,
-			envs: []string{
-				"PWD=/b/c/b/linux/src/out/Debug",
-			},
-			relocatable: false,
-		},
-		{
-			desc: "resource-dir relative",
-			args: append(append([]string{}, baseReleaseArgs...),
-				"-resource-dir=../../third_party/llvm-build-release+Asserts/bin/clang/10.0.0"),
-			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"),
-			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"),
-			relocatable: false,
-		},
-		{
-			desc: "include absolute",
-			args: append(append([]string{}, baseReleaseArgs...),
-				"-include",
-				"/b/c/b/linux/header.h"),
-			relocatable: false,
-		},
-		{
-			desc: "include relative",
-			args: append(append([]string{}, baseReleaseArgs...),
-				"-include",
-				"../b/c/b/linux/header.h"),
-			relocatable: true,
-		},
-		{
-			desc: "include= absolute",
-			args: append(append([]string{}, baseReleaseArgs...),
-				"-include=/b/c/b/linux/header.h"),
-			relocatable: false,
-		},
-		{
-			desc: "include= relative",
-			args: append(append([]string{}, baseReleaseArgs...),
-				"-include=../b/c/b/linux/header.h"),
-			relocatable: true,
-		},
-		{
-			desc: "sysroot absolute",
-			args: modifyArgs(baseReleaseArgs,
-				"--sysroot",
-				"--sysroot=/b/c/b/linux/build/linux/debian_sid_amd64-sysroot"),
-			relocatable: false,
-		},
-		{
-			desc: "sysroot separate relative",
-			args: append(modifyArgs(baseReleaseArgs,
-				"--sysroot",
-				"--sysroot"),
-				"../../build/linux/debian_sid_amd64-sysroot"),
-			relocatable: true,
-		},
-		{
-			desc: "sysroot separate absolute",
-			args: append(modifyArgs(baseReleaseArgs,
-				"--sysroot",
-				"--sysroot"),
-				"/b/c/b/linux/build/linux/debian_sid_amd64-sysroot"),
-			relocatable: false,
-		},
-		{
-			desc: "llvm -asan option",
-			// https://b/issues/141210713#comment3
-			args: append(append([]string{}, baseReleaseArgs...),
-				"-mllvm",
-				"-asan-globals=0"),
-			relocatable: true,
-		},
-		{
-			desc: "llvm -regalloc option",
-			// https://b/issues/141210713#comment4
-			args: append(append([]string{}, baseReleaseArgs...),
-				"-mllvm",
-				"-regalloc=pbqp",
-				"-mllvm",
-				"-pbqp-coalescing"),
-			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...),
-				"-Qunused-arguments"),
-			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,
-		},
-		{
-			desc: "static-libgcc",
-			args: append(append([]string{}, baseReleaseArgs...),
-				"-static-libgcc"),
-			relocatable: true,
-		},
-		{
-			desc: "idirafter relative",
-			args: append(append([]string{}, baseReleaseArgs...),
-				"-idirafter", "../../system/ulib/include"),
-			relocatable: true,
-		},
-		{
-			desc: "idirafter abs",
-			args: append(append([]string{}, baseReleaseArgs...),
-				"-idirafter", "/b/c/system/ulib/include"),
-			relocatable: false,
-		},
-		{
-			desc: "-mllvm -instcombine-lower-dbg-declare=0",
-			args: append(append([]string{}, baseReleaseArgs...),
-				"-mllvm", "-instcombine-lower-dbg-declare=0"),
-			relocatable: true,
-		},
-		{
-			desc: "-mllvm ml inliner relocatable",
-			args: append(append([]string{}, baseReleaseArgs...),
-				"-mllvm", "-enable-ml-inliner=development", "-mllvm", "-training-log=./train.log", "-mllvm", "-ml-inliner-model-under-training=./tf.policy"),
-			relocatable: true,
-		},
-		{
-			desc: "-mllvm ml inliner non-relocatable train log",
-			args: append(append([]string{}, baseReleaseArgs...),
-				"-mllvm", "-enable-ml-inliner=development", "-mllvm", "-training-log=/abs/train.log", "-mllvm", "-ml-inliner-model-under-training=./tf.policy"),
-			relocatable: false,
-		},
-		{
-			desc: "-mllvm ml inliner non-relocatable policy file",
-			args: append(append([]string{}, baseReleaseArgs...),
-				"-mllvm", "-enable-ml-inliner=development", "-mllvm", "-training-log=./train.log", "-mllvm", "-ml-inliner-model-under-training=/abs/tf.policy"),
-			relocatable: false,
-		},
-		{
-			desc: "-includeBuildConfig.h",
-			args: append(append([]string{}, baseReleaseArgs...),
-				"-includeBuildConfig.h"),
-			relocatable: true,
-		},
-		{
-			desc: "-include/usr/include/BuildConfig.h",
-			args: append(append([]string{}, baseReleaseArgs...),
-				"-include/usr/include/BuildConfig.h"),
-			relocatable: false,
-		},
-		{
-			desc: "--includeBuildConfig.h",
-			args: append(append([]string{}, baseReleaseArgs...),
-				"--includeBuildConfig.h"),
-			relocatable: true,
-		},
-		{
-			desc: "--include/usr/include/BuildConfig.h",
-			args: append(append([]string{}, baseReleaseArgs...),
-				"--include/usr/include/BuildConfig.h"),
-			relocatable: false,
-		},
-		{
-			desc: "--include=BuildConfig.h",
-			args: append(append([]string{}, baseReleaseArgs...),
-				"--include=BuildConfig.h"),
-			relocatable: true,
-		},
-		{
-			desc: "--include=/usr/include/BuildConfig.h",
-			args: append(append([]string{}, baseReleaseArgs...),
-				"--include=/usr/include/BuildConfig.h"),
-			relocatable: false,
-		},
-		{
-			desc: "-mllvm -basic-aa-recphi=0",
-			args: append(append([]string{}, baseReleaseArgs...),
-				"-mllvm", "-basic-aa-recphi=0"),
-			relocatable: true,
-		},
-		{
-			desc: "-pie",
-			args: append(append([]string{}, baseReleaseArgs...),
-				"-pie"),
-			relocatable: true,
-		},
-		{
-			desc: "-Xclang -debug-info-kind=constructor",
-			args: append(append([]string{}, baseReleaseArgs...),
-				"-Xclang", "-debug-info-kind=constructor"),
-			relocatable: true,
-		},
-		{
-			desc: "-Xclang -fno-experimental-new-pass-manager",
-			args: append(append([]string{}, baseReleaseArgs...),
-				"-Xclang", "-fno-experimental-new-pass-manager"),
-			relocatable: true,
-		},
-		{
-			desc: "-Xclang -no-opaque-pointers",
-			args: append(append([]string{}, baseReleaseArgs...),
-				"-Xclang", "-no-opaque-pointers"),
-			relocatable: true,
-		},
-		{
-			desc: "-mllvm -sanitizer-coverage-prune-blocks=1",
-			args: append(append([]string{}, baseReleaseArgs...),
-				"-mllvm", "-sanitizer-coverage-prune-blocks=1"),
-			relocatable: true,
-		},
-		{
-			desc: "framework include search path -F.",
-			args: append(append([]string{}, baseReleaseArgs...),
-				"-F."),
-			relocatable: true,
-		},
-		{
-			desc: "framework include search path -F/App/Framework",
-			args: append(append([]string{}, baseReleaseArgs...),
-				"-F/App/Framework"),
-			relocatable: false,
-		},
-		{
-			desc: "-target",
-			args: append(append([]string{}, baseReleaseArgs...),
-				"-target", "x86_64-linux-gnu"),
-			relocatable: true,
-		},
-		{
-			desc: "-mllvm -enable-dse-memoryssa=true",
-			args: append(append([]string{}, baseReleaseArgs...),
-				"-mllvm", "-enable-dse-memoryssa=true"),
-			relocatable: true,
-		},
-		{
-			desc: "-mllvm -enable-dse-memoryssa=false",
-			args: append(append([]string{}, baseReleaseArgs...),
-				"-mllvm", "-enable-dse-memoryssa=false"),
-			relocatable: true,
-		},
-		{
-			desc: "-mllvm -limited-coverage-experimental=true",
-			args: append(append([]string{}, baseReleaseArgs...),
-				"-mllvm", "-limited-coverage-experimental=true"),
-			relocatable: true,
-		},
-		{
-			desc: "-Wa,--fatal-warnings",
-			args: append(append([]string{}, baseReleaseArgs...),
-				"-Wa,--fatal-warnings"),
-			relocatable: true,
-		},
-		{
-			desc: "-fcoverage-compilation-dir= unrelocatable",
-			args: append(append([]string{}, baseReleaseArgs...),
-				"-fcoverage-compilation-dir=/b/c/b/linux/src/out/data/coverage-compilation-dir"),
-			relocatable: false,
-		},
-		{
-			desc: "-fcoverage-compilation-dir= relocatable",
-			args: append(append([]string{}, baseReleaseArgs...),
-				"-fcoverage-compilation-dir=./out/data/coverage-compilation-dir"),
-			relocatable: true,
-		},
-		{
-			desc: "-ffile-compilation-dir= unrelocatable with debug build",
-			args: append(append([]string{}, baseDebugArgs...),
-				"-ffile-compilation-dir=/b/c/b/linux/src/out/data/file-compilation-dir"),
-			relocatable: false,
-		},
-		{
-			desc: "-ffile-compilation-dir= relocatable with debug build",
-			args: append(append([]string{}, baseDebugArgs...),
-				"-ffile-compilation-dir=./out/data/file-compilation-dir"),
-			relocatable: true,
-		},
-		{
-			desc: "-ffile-compilation-dir= unrelocatable with release build",
-			args: append(append([]string{}, baseReleaseArgs...),
-				"-ffile-compilation-dir=/b/c/b/linux/src/out/data/file-compilation-dir"),
-			relocatable: false,
-		},
-		{
-			desc: "-ffile-compilation-dir= relocatable with release build",
-			args: append(append([]string{}, baseReleaseArgs...),
-				"-ffile-compilation-dir=./out/data/file-compilation-dir"),
-			relocatable: true,
-		},
-		{
-			desc: "-fprofile-list= unrelocatable",
-			args: append(append([]string{}, baseReleaseArgs...),
-				"-fprofile-list=/b/c/b/linux/src/out/data/profile-list"),
-			relocatable: false,
-		},
-		{
-			desc: "-fprofile-list= relocatable",
-			args: append(append([]string{}, baseReleaseArgs...),
-				"-fprofile-list=./out/data/profile-list"),
-			relocatable: true,
-		},
-		{
-			desc: "clang -target-feature",
-			args: append(append([]string{}, baseReleaseArgs...),
-				"-Xclang", "-target-feature",
-				"-Xclang", "+crc",
-				"-Xclang", "-target-feature",
-				"-Xclang", "+crypto"),
-			relocatable: true,
-		},
-		{
-			desc: "clang --rtlib=",
-			args: append(append([]string{}, baseReleaseArgs...),
-				"--rtlib=libgcc"),
-			relocatable: true,
-		},
-		{
-			desc: "clang --unwindlib=",
-			args: append(append([]string{}, baseReleaseArgs...),
-				"--unwindlib=libunwind"),
-			relocatable: true,
-		},
-	} {
-		t.Run(tc.desc, func(t *testing.T) {
-			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 TestGccRelocatableReqForDebugCompilationDir(t *testing.T) {
-	// Tests for supporting "-fdebug-compilation-dir", see b/135719929.
-	// We could have merged the cases here into TestGccRelocatableReq, but decided
-	// to separate them for clarity.
-
-	// Do not set "-g*" options in baseReleaseArgs!
-	baseReleaseArgs := []string{
-		"../../third_party/llvm-build/Release+Asserts/bin/clang++",
-		"../../base/time/time.cc",
-	}
-
-	// Since "-fdebug-compilation-dir" has been moved to clang driver flags in
-	// https://reviews.llvm.org/D63387, we set cases both with and w/o "-Xclang"
-	for _, tc := range []struct {
-		desc        string
-		args        []string
-		envs        []string
-		relocatable bool
-	}{
-		{
-			desc: "basic",
-			args: append(append([]string{}, baseReleaseArgs...),
-				"-fdebug-compilation-dir",
-				"."),
-			relocatable: true,
-		},
-		{
-			desc: "-Xclang",
-			args: append(append([]string{}, baseReleaseArgs...),
-				"-Xclang",
-				"-fdebug-compilation-dir",
-				"-Xclang",
-				"."),
-			relocatable: true,
-		},
-		{
-			desc: "With -g* DBG options",
-			args: append(append([]string{}, baseReleaseArgs...),
-				"-g2",
-				"-gsplit-dwarf",
-				"-fdebug-compilation-dir",
-				"."),
-			relocatable: true,
-		},
-		{
-			desc: "-Xclang with -g* DBG option",
-			args: append(append([]string{}, baseReleaseArgs...),
-				"-g2",
-				"-gsplit-dwarf",
-				"-Xclang",
-				"-fdebug-compilation-dir",
-				"-Xclang",
-				"."),
-			relocatable: true,
-		},
-		{
-			// Make sure the CWD agnostic still returns false if
-			// "-fdebug-compilation-dir" is not specified.
-			desc: "Only -g* DBG options",
-			args: append(append([]string{}, baseReleaseArgs...),
-				"-g2",
-				"-gsplit-dwarf"),
-			relocatable: false,
-		},
-		{
-			// "-fdebug-compilation-dir" is not supported as LLVM flags.
-			desc: "No LLVM",
-			args: append(append([]string{}, baseReleaseArgs...),
-				"-mllvm",
-				"-fdebug-compilation-dir",
-				"-mllvm",
-				"."),
-			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,
-		},
-		{
-			desc: "-fdebug-compilation-dir= unrelocatable",
-			args: append(append([]string{},
-				baseReleaseArgs...),
-				"-fdebug-compilation-dir=/b/c/b/linux/src/out/data/debug_compilation_dir"),
-			relocatable: false,
-		},
-		{
-			desc: "-fdebug-compilation-dir= relocatable",
-			args: append(append([]string{},
-				baseReleaseArgs...),
-				"-fdebug-compilation-dir=./debug_compilation_dir"),
-			relocatable: true,
-		},
-	} {
-		t.Run(tc.desc, func(t *testing.T) {
-			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 TestGccOutputs(t *testing.T) {
-	for _, tc := range []struct {
-		desc string
-		args []string
-		want []string
-	}{
-		{
-			desc: "basic",
-			args: []string{
-				"gcc", "-c", "A/test.c",
-				"-I", "A/B/C",
-				"-ID/E/F",
-				"-o", "A/test.o",
-				"-MF", "test.d",
-			},
-			want: []string{"test.d", "A/test.o"},
-		},
-		{
-			desc: "prefix",
-			args: []string{
-				"gcc", "-c", "A/test.c",
-				"-I", "A/B/C",
-				"-ID/E/F",
-				"-oA/test.o",
-				"-MFtest.d",
-			},
-			want: []string{"test.d", "A/test.o"},
-		},
-		{
-			desc: "with dwo",
-			args: []string{
-				"gcc", "-c", "A/test.c",
-				"-I", "A/B/C",
-				"-ID/E/F",
-				"-gsplit-dwarf",
-				"-o", "A/test.o",
-				"-MF", "test.d",
-			},
-			want: []string{"test.d", "A/test.o", "A/test.dwo"},
-		},
-		{
-			desc: "prefix with dwo",
-			args: []string{
-				"gcc", "-c", "A/test.c",
-				"-I", "A/B/C",
-				"-ID/E/F",
-				"-gsplit-dwarf",
-				"-oA/test.o",
-				"-MFtest.d",
-			},
-			want: []string{"test.d", "A/test.o", "A/test.dwo"},
-		},
-	} {
-		t.Run(tc.desc, func(t *testing.T) {
-			if got := gccOutputs(tc.args); !reflect.DeepEqual(got, tc.want) {
-				t.Errorf("gccOutputs(%q)=%q; want %q", tc.args, got, tc.want)
-			}
-		})
-	}
-}
diff --git a/remoteexec/gomainput.go b/remoteexec/gomainput.go
deleted file mode 100644
index 572f771..0000000
--- a/remoteexec/gomainput.go
+++ /dev/null
@@ -1,324 +0,0 @@
-// Copyright 2018 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.
-
-package remoteexec
-
-import (
-	"bytes"
-	"context"
-	"fmt"
-	"io"
-	"io/ioutil"
-	"sync"
-	"time"
-
-	"go.opencensus.io/stats"
-	"go.opencensus.io/tag"
-	"google.golang.org/grpc"
-	"google.golang.org/grpc/codes"
-	"google.golang.org/grpc/status"
-
-	"go.chromium.org/goma/server/file"
-	"go.chromium.org/goma/server/hash"
-	"go.chromium.org/goma/server/log"
-	gomapb "go.chromium.org/goma/server/proto/api"
-	fpb "go.chromium.org/goma/server/proto/file"
-	"go.chromium.org/goma/server/remoteexec/digest"
-	"go.chromium.org/goma/server/rpc"
-)
-
-// TODO: test for FILE_META input.
-
-type lookupClient interface {
-	LookupFile(context.Context, *gomapb.LookupFileReq, ...grpc.CallOption) (*gomapb.LookupFileResp, error)
-}
-
-// gomaInput handles goma input files.
-type gomaInput struct {
-	gomaFile fpb.FileServiceClient
-	sema     chan struct{}
-
-	// key: goma file hash -> value: digest.Data
-	digestCache DigestCache
-
-	mu   sync.Mutex
-	srcs []*gomaInputSource
-}
-
-func (gi *gomaInput) Close() {
-	gi.mu.Lock()
-	defer gi.mu.Unlock()
-	for _, src := range gi.srcs {
-		src.resetBlob()
-	}
-}
-
-// gomaInput converts goma input file to remoteexec digest.
-func (gi *gomaInput) toDigest(ctx context.Context, input *gomapb.ExecReq_Input) (digest.Data, error) {
-	hashKey := input.GetHashKey()
-	// TODO: if input has size bytes, use it as digest.
-	// if it has inlined content, put it in digest.Data.
-
-	// client usually sets hashKey, but compute hashKey if not set.
-	if hashKey == "" && input.GetContent() != nil {
-		var err error
-		hashKey, err = hash.SHA256Proto(input.GetContent())
-		if err != nil {
-			return nil, err
-		}
-	}
-	src := &gomaInputSource{
-		lookupClient: gi.gomaFile,
-		sema:         gi.sema,
-		hashKey:      hashKey,
-		filename:     input.GetFilename(),
-		blob:         input.GetContent(),
-	}
-	gi.mu.Lock()
-	gi.srcs = append(gi.srcs, src)
-	gi.mu.Unlock()
-
-	return gi.digestCache.Get(ctx, hashKey, src)
-}
-
-func (gi *gomaInput) upload(ctx context.Context, content []*gomapb.FileBlob) ([]string, error) {
-	if len(content) == 0 {
-		return nil, status.Error(codes.FailedPrecondition, "upload: contents must not be empty.")
-	}
-	for _, c := range content {
-		if c == nil {
-			return nil, status.Error(codes.FailedPrecondition, "upload: contents must not be nil.")
-		}
-	}
-	// need semaphore here?
-	resp, err := gi.gomaFile.StoreFile(ctx, &gomapb.StoreFileReq{
-		Blob: content,
-	})
-	if err != nil {
-		return nil, err
-	}
-	if len(resp.HashKey) < len(content) {
-		return nil, status.Errorf(codes.Internal, "file.StoreFile: failed to set content: %d hashes returned, expected %d", len(resp.HashKey), len(content))
-	}
-	for _, hk := range resp.HashKey {
-		if hk == "" {
-			return nil, status.Errorf(codes.Internal, "file.StoreFile: failed to set content")
-		}
-	}
-	return resp.HashKey, nil
-}
-
-type gomaInputSource struct {
-	lookupClient lookupClient
-	sema         chan struct{}
-	hashKey      string
-	filename     string
-
-	mu   sync.Mutex
-	blob *gomapb.FileBlob
-}
-
-func (g *gomaInputSource) String() string {
-	g.mu.Lock()
-	blob := g.blob
-	g.mu.Unlock()
-	return fmt.Sprintf("goma-input:%s %s %p", g.hashKey, g.filename, blob)
-}
-
-func (g *gomaInputSource) Filename() string {
-	return g.filename
-}
-
-func (g *gomaInputSource) lookup(ctx context.Context, 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 {
-		select {
-		case g.sema <- struct{}{}:
-			resp, err = g.lookupClient.LookupFile(ctx, req)
-			<-g.sema
-			return err
-		case <-ctx.Done():
-			logger := log.FromContext(ctx)
-			logger.Errorf("lookup failed to get semaphore: %v", ctx.Err())
-			return ctx.Err()
-		}
-	})
-	if err != nil {
-		return nil, err
-	}
-	if len(resp.Blob) == 0 {
-		return nil, status.Errorf(codes.NotFound, "no blob for %s", hashKeys)
-	}
-	if len(resp.Blob) != len(hashKeys) {
-		return nil, status.Errorf(codes.Internal, "request %d (%q), got %d", len(hashKeys), hashKeys, len(resp.Blob))
-	}
-	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
-}
-
-func (g *gomaInputSource) getBlob(ctx context.Context) (*gomapb.FileBlob, error) {
-	g.mu.Lock()
-	blob := g.blob
-	g.mu.Unlock()
-	if blob == nil {
-		blobs, err := g.lookup(ctx, []string{g.hashKey})
-		if err != nil {
-			return nil, err
-		}
-		blob = blobs[0]
-	}
-	return blob, nil
-}
-
-func (g *gomaInputSource) resetBlob() {
-	g.mu.Lock()
-	defer g.mu.Unlock()
-	g.blob = nil
-}
-
-func (g *gomaInputSource) Open(ctx context.Context) (io.ReadCloser, error) {
-	blob, err := g.getBlob(ctx)
-	if err != nil {
-		return nil, err
-	}
-	switch blob.GetBlobType() {
-	case gomapb.FileBlob_FILE:
-		r := bytes.NewReader(blob.Content)
-		return ioutil.NopCloser(r), nil
-
-	case gomapb.FileBlob_FILE_META:
-		return &gomaInputReader{
-			ctx:  ctx,
-			src:  g,
-			meta: blob,
-		}, nil
-
-	case gomapb.FileBlob_FILE_UNSPECIFIED:
-		return nil, status.Errorf(codes.NotFound, "missing blob for %s", g.hashKey)
-	}
-	return nil, status.Errorf(codes.Internal, "bad file_blob type: %s: %v", g.hashKey, blob.GetBlobType())
-}
-
-const gomaInputBatchSize = 5
-
-// one allocation at most 10MiB (5 * 2MB)
-// limit at most 1GB (5 * 2MiB * 100) for input buffers.
-const maxConcurrentInputBuffers = 100
-
-type gomaInputBufferPool struct {
-	sema chan bool
-	// use sync.Pool?
-}
-
-var inputBufferPool = &gomaInputBufferPool{
-	sema: make(chan bool, maxConcurrentInputBuffers),
-}
-
-func (p *gomaInputBufferPool) allocate(ctx context.Context, size int64) ([]byte, error) {
-	if size > gomaInputBatchSize*file.FileChunkSize {
-		size = gomaInputBatchSize * file.FileChunkSize
-	}
-	ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
-	defer cancel()
-	start := time.Now()
-	select {
-	case <-ctx.Done():
-		logger := log.FromContext(ctx)
-		logger.Errorf("goma input allocate buffer timed-out size=%d, %s", size, time.Since(start))
-		stats.RecordWithTags(ctx, []tag.Mutator{
-			tag.Upsert(allocStatusKey, "fail"),
-		}, inputBufferAllocSize.M(size))
-		return nil, ctx.Err()
-	case p.sema <- true:
-		stats.RecordWithTags(ctx, []tag.Mutator{
-			tag.Upsert(allocStatusKey, "ok"),
-		}, inputBufferAllocSize.M(size))
-		return make([]byte, size), nil
-	}
-}
-
-func (p *gomaInputBufferPool) release(buf []byte) {
-	<-p.sema
-}
-
-type gomaInputReader struct {
-	ctx       context.Context
-	src       *gomaInputSource
-	meta      *gomapb.FileBlob
-	i         int    // next index of hash key in meta.
-	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) {
-	if len(r.buf) == 0 {
-		if len(r.meta.HashKey) == r.i {
-			// release buffer once all contents has been processed.
-			inputBufferPool.release(r.allocated)
-			r.buf = nil
-			r.allocated = nil
-			return 0, io.EOF
-		}
-		j := r.i + gomaInputBatchSize
-		if j > len(r.meta.HashKey) {
-			j = len(r.meta.HashKey)
-		}
-		blobs, err := r.src.lookup(r.ctx, r.meta.HashKey[r.i:j])
-		if err != nil {
-			return 0, status.Errorf(status.Code(err), "lookup chunk in FILE_META %s %d:%d %s: %v", r.src.hashKey, r.i, j, r.meta.HashKey[r.i:j], err)
-		}
-		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.src.hashKey, r.i+i, r.meta.HashKey[r.i+i], blob.GetBlobType())
-			}
-		}
-		if len(r.allocated) == 0 {
-			r.allocated, err = inputBufferPool.allocate(r.ctx, r.meta.GetFileSize())
-			if err != nil {
-				return 0, status.Errorf(codes.ResourceExhausted, "allocate buffer for FILE_META %s size=%d: %v", r.src.hashKey, r.meta.GetFileSize(), err)
-			}
-		}
-		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.src.hashKey, i0+i, r.meta.HashKey[i0+i])
-			}
-			pos += n
-		}
-		r.buf = b[:pos]
-	}
-	n := copy(buf, r.buf)
-	r.buf = r.buf[n:]
-	return n, nil
-}
-
-func (r *gomaInputReader) Close() error {
-	// release buffer if it has not yet been released.
-	if len(r.allocated) > 0 {
-		inputBufferPool.release(r.allocated)
-		r.buf = nil
-		r.allocated = nil
-	}
-	return nil
-}
diff --git a/remoteexec/gomainput_test.go b/remoteexec/gomainput_test.go
deleted file mode 100644
index 48e02bc..0000000
--- a/remoteexec/gomainput_test.go
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright 2019 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.
-
-package remoteexec
-
-import (
-	"context"
-	"testing"
-	"time"
-
-	gomapb "go.chromium.org/goma/server/proto/api"
-)
-
-func TestUploadWork(t *testing.T) {
-	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
-	defer cancel()
-
-	cluster := &fakeCluster{
-		rbe: newFakeRBE(),
-	}
-	err := cluster.setup(ctx, cluster.rbe.instancePrefix)
-	if err != nil {
-		t.Fatal(err)
-	}
-	defer cluster.teardown()
-
-	gi := gomaInput{
-		gomaFile:    cluster.adapter.GomaFile,
-		sema:        make(chan struct{}, 1),
-		digestCache: cluster.adapter.DigestCache,
-	}
-
-	hks, err := gi.upload(ctx, []*gomapb.FileBlob{
-		{
-			BlobType: gomapb.FileBlob_FILE.Enum(),
-			Content:  []byte("dummy"),
-		},
-	})
-	if err != nil {
-		t.Errorf("gi.upload err=%v; want nil", err)
-	}
-	for _, hk := range hks {
-		if hk == "" {
-			t.Errorf("gi.upload returns hk=empty string; want non empty")
-		}
-	}
-}
-
-func TestUploadShouldReturnErrorOnNilContent(t *testing.T) {
-	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
-	defer cancel()
-
-	cluster := &fakeCluster{
-		rbe: newFakeRBE(),
-	}
-	err := cluster.setup(ctx, cluster.rbe.instancePrefix)
-	if err != nil {
-		t.Fatal(err)
-	}
-	defer cluster.teardown()
-
-	gi := gomaInput{
-		gomaFile:    cluster.adapter.GomaFile,
-		sema:        make(chan struct{}, 1),
-		digestCache: cluster.adapter.DigestCache,
-	}
-
-	_, err = gi.upload(ctx, nil)
-	if err == nil {
-		t.Errorf("gi.upload err=nil; want error")
-	}
-}
diff --git a/remoteexec/gomaoutput.go b/remoteexec/gomaoutput.go
deleted file mode 100644
index a6addc8..0000000
--- a/remoteexec/gomaoutput.go
+++ /dev/null
@@ -1,458 +0,0 @@
-// Copyright 2018 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.
-
-package remoteexec
-
-import (
-	"bufio"
-	"bytes"
-	"context"
-	"fmt"
-	"io"
-	"os"
-	"sync"
-	"time"
-
-	rpb "github.com/bazelbuild/remote-apis/build/bazel/remote/execution/v2"
-
-	"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"
-	"google.golang.org/protobuf/proto"
-
-	"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"
-	"go.chromium.org/goma/server/remoteexec/cas"
-	"go.chromium.org/goma/server/remoteexec/datasource"
-	"go.chromium.org/goma/server/remoteexec/digest"
-	"go.chromium.org/goma/server/rpc"
-)
-
-// gomaOutput handles goma output files.
-type gomaOutput struct {
-	gomaResp *gomapb.ExecResp
-	bs       bpb.ByteStreamClient
-	instance string
-	gomaFile fpb.FileServiceClient
-}
-
-func outputTimeout(size int64) time.Duration {
-	// assume at least 4MB/s
-	t := time.Duration(int64((float64(size) / (4 * 1024 * 1024)) * 1e9))
-	if t < 3*time.Second {
-		return 3 * time.Second
-	}
-	return t
-}
-
-func retryCAS(ctx context.Context, timeout time.Duration, f func(ctx context.Context) error) error {
-	n := 0
-	return rpc.Retry{}.Do(ctx, func() error {
-		n++
-		timeout *= time.Duration(n)
-		ctx, cancel := context.WithTimeout(ctx, timeout)
-		defer cancel()
-		err := f(ctx)
-		return fixRBEInternalError(err)
-	})
-}
-
-func (g gomaOutput) stdoutData(ctx context.Context, eresp *rpb.ExecuteResponse) error {
-	if len(eresp.Result.StdoutRaw) > 0 {
-		g.gomaResp.Result.StdoutBuffer = eresp.Result.StdoutRaw
-		return nil
-	}
-	if eresp.Result.StdoutDigest == nil {
-		return nil
-	}
-	var buf bytes.Buffer
-	err := retryCAS(ctx, outputTimeout(eresp.Result.StdoutDigest.SizeBytes), func(ctx context.Context) error {
-		buf.Reset()
-		return cas.DownloadDigest(ctx, g.bs, &buf, g.instance, eresp.Result.StdoutDigest)
-	})
-	if err != nil {
-		logger := log.FromContext(ctx)
-		logger.Errorf("failed to fetch stdout %v: %v", eresp.Result.StdoutDigest, err)
-		if status.Code(err) == codes.Unauthenticated {
-			return err
-		}
-		g.gomaResp.ErrorMessage = append(g.gomaResp.ErrorMessage, fmt.Sprintf("failed to fetch stdout %v: %s", eresp.Result.StdoutDigest, status.Code(err)))
-		return nil
-	}
-	g.gomaResp.Result.StdoutBuffer = buf.Bytes()
-	return nil
-}
-
-func (g gomaOutput) stderrData(ctx context.Context, eresp *rpb.ExecuteResponse) error {
-	if len(eresp.Result.StderrRaw) > 0 {
-		g.gomaResp.Result.StderrBuffer = eresp.Result.StderrRaw
-		return nil
-	}
-	if eresp.Result.StderrDigest == nil {
-		return nil
-	}
-	var buf bytes.Buffer
-	err := retryCAS(ctx, outputTimeout(eresp.Result.StderrDigest.SizeBytes), func(ctx context.Context) error {
-		buf.Reset()
-		return cas.DownloadDigest(ctx, g.bs, &buf, g.instance, eresp.Result.StderrDigest)
-	})
-	if err != nil {
-		logger := log.FromContext(ctx)
-		logger.Errorf("failed to fetch stderr %v: %v", eresp.Result.StdoutDigest, err)
-		if status.Code(err) == codes.Unauthenticated {
-			return err
-		}
-		g.gomaResp.ErrorMessage = append(g.gomaResp.ErrorMessage, fmt.Sprintf("failed to fetch stderr %v: %s", eresp.Result.StderrDigest, status.Code(err)))
-		return nil
-	}
-	g.gomaResp.Result.StderrBuffer = buf.Bytes()
-	return nil
-}
-
-func (g gomaOutput) outputFileHelper(ctx context.Context, fname string, output *rpb.OutputFile) (*gomapb.ExecResult_Output, error) {
-	var blob *gomapb.FileBlob
-	err := retryCAS(ctx, outputTimeout(output.GetDigest().GetSizeBytes()), func(ctx context.Context) error {
-		var err error
-		blob, err = g.toFileBlob(ctx, output)
-		return err
-	})
-	if err != nil {
-		logger := log.FromContext(ctx)
-		switch status.Code(err) {
-		case codes.Unavailable, codes.Canceled, codes.Aborted:
-			logger.Warnf("goma blob for %s: %v", output.Path, err)
-		default:
-			logger.Errorf("goma blob for %s: %v", output.Path, err)
-		}
-		return nil, status.Errorf(status.Code(err), "goma blob for %s: %v", output.Path, status.Code(err))
-	}
-	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) error {
-	result, err := g.outputFileHelper(ctx, fname, output)
-	if err != nil {
-		if status.Code(err) == codes.Unauthenticated {
-			return err
-		}
-		g.gomaResp.ErrorMessage = append(g.gomaResp.ErrorMessage, err.Error())
-		return nil
-	}
-	g.gomaResp.Result.Output = append(g.gomaResp.Result.Output, result)
-	return nil
-}
-
-func (g gomaOutput) outputFilesConcurrent(ctx context.Context, outputs []*rpb.OutputFile, sema chan struct{}) error {
-	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)
-		}
-	}
-	var rerr error
-	for _, err := range errs {
-		if err != nil {
-			if status.Code(err) == codes.Unauthenticated {
-				rerr = err
-			}
-			g.gomaResp.ErrorMessage = append(g.gomaResp.ErrorMessage, err.Error())
-		}
-	}
-	return rerr
-}
-
-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, status.Errorf(status.Code(err), "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, status.Errorf(status.Code(err), "could not confirm EOF: %v", err)
-	}
-	return blob, nil
-}
-
-func (g gomaOutput) toFileBlob(ctx context.Context, output *rpb.OutputFile) (*gomapb.FileBlob, error) {
-	ctx, span := trace.StartSpan(ctx, "go.chromium.org/goma/server/remoteexec.gomaOutput.toFileBlob")
-	defer span.End()
-
-	logger := log.FromContext(ctx)
-
-	if output.Digest.SizeBytes <= file.LargeFileThreshold {
-		// for single FileBlob.
-		var buf bytes.Buffer
-		err := cas.DownloadDigest(ctx, g.bs, &buf, g.instance, output.Digest)
-		if err != nil {
-			return nil, err
-		}
-		return &gomapb.FileBlob{
-			BlobType: gomapb.FileBlob_FILE.Enum(),
-			Content:  buf.Bytes(),
-			FileSize: proto.Int64(output.Digest.SizeBytes),
-		}, nil
-	}
-
-	casErrCh := make(chan error, 1)
-	rd, wr, err := os.Pipe()
-	if err != nil {
-		return nil, err
-	}
-	defer rd.Close()
-	go func() {
-		err := cas.DownloadDigest(ctx, g.bs, wr, g.instance, output.Digest)
-		if err != nil {
-			switch status.Code(err) {
-			case codes.Unavailable, codes.Canceled, codes.Aborted:
-				logger.Warnf("cas download error %s: %v", output.Digest, err)
-			default:
-				logger.Errorf("cas download error %s: %v", output.Digest, err)
-			}
-		}
-		wr.Close()
-		casErrCh <- err
-	}()
-
-	blob, err := toChunkedFileBlob(ctx, rd, output.Digest.SizeBytes, g.gomaFile)
-	// prefer cas err for Unauthenticated error.
-	// http://b/181914314
-	if casErr := <-casErrCh; casErr != nil {
-		return nil, casErr
-	}
-	if err != nil {
-		return nil, status.Errorf(status.Code(err), "failed to convert output:{%v} to chunked FileBlob: %v", output, err)
-	}
-	return blob, nil
-}
-
-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{}) error {
-	logger := log.FromContext(ctx)
-	if output.TreeDigest == nil {
-		logger.Warnf("no tree digest in %s", dname)
-		return nil
-	}
-	var buf bytes.Buffer
-	err := retryCAS(ctx, outputTimeout(output.TreeDigest.SizeBytes), func(ctx context.Context) error {
-		return cas.DownloadDigest(ctx, g.bs, &buf, g.instance, output.TreeDigest)
-	})
-	if err != nil {
-		logger.Errorf("failed to download tree %s: %v", dname, err)
-		if status.Code(err) == codes.Unauthenticated {
-			return err
-		}
-		g.gomaResp.ErrorMessage = append(g.gomaResp.ErrorMessage, fmt.Sprintf("failed to download tree %s: %s", dname, status.Code(err)))
-		return nil
-	}
-	tree := &rpb.Tree{}
-	err = proto.Unmarshal(buf.Bytes(), tree)
-	if err != nil {
-		logger.Errorf("failed to unmarshal tree data %s: %v", dname, err)
-		g.gomaResp.ErrorMessage = append(g.gomaResp.ErrorMessage, fmt.Sprintf("failed to unmarshal tree data %s: %v", dname, err))
-		return nil
-	}
-
-	ds := digest.NewStore()
-	for _, c := range tree.Children {
-		d, err := digest.Proto(c)
-		if err != nil {
-			logger.Errorf("digest for children %s: %v", c, err)
-			continue
-		}
-		ds.Set(d)
-	}
-	outputFiles := traverseTree(ctx, filepath, dname, tree.Root, ds)
-	return g.outputFilesConcurrent(ctx, outputFiles, sema)
-}
-
-// 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, status.Errorf(status.Code(err), "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
-		})
-	}
-
-	if err := eg.Wait(); err != nil {
-		return err
-	}
-
-	// 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
deleted file mode 100644
index e8c1578..0000000
--- a/remoteexec/gomaoutput_test.go
+++ /dev/null
@@ -1,666 +0,0 @@
-// Copyright 2019 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.
-
-package remoteexec
-
-import (
-	"context"
-	"fmt"
-	"path"
-	"strings"
-	"testing"
-
-	rpb "github.com/bazelbuild/remote-apis/build/bazel/remote/execution/v2"
-
-	"github.com/google/go-cmp/cmp"
-	"google.golang.org/protobuf/proto"
-
-	"go.chromium.org/goma/server/command/descriptor/posixpath"
-	gomapb "go.chromium.org/goma/server/proto/api"
-	"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()
-
-	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),
-		},
-	}
-
-	td, err := cluster.rbe.setProto(ctx, &rpb.Tree{
-		Root: &rpb.Directory{
-			Files: []*rpb.FileNode{f1.node},
-			Directories: []*rpb.DirectoryNode{
-				makeDirNode(t, "dir1", dir1),
-			},
-		},
-		Children: []*rpb.Directory{
-			dir1,
-			dir2,
-		},
-	})
-	if err != nil {
-		t.Fatalf("tree: %v", err)
-	}
-	var filepath posixpath.FilePath
-
-	gout := gomaOutput{
-		gomaResp: &gomapb.ExecResp{
-			Result: &gomapb.ExecResult{},
-		},
-		bs:       cluster.adapter.Client,
-		instance: path.Join(cluster.rbe.instancePrefix, "default_instance"),
-		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:         makeFileBlob("file1"),
-			IsExecutable: proto.Bool(false),
-		},
-		{
-			Filename:     proto.String("out/dir1/file2"),
-			Blob:         makeFileBlob("file2"),
-			IsExecutable: proto.Bool(false),
-		},
-		{
-			Filename:     proto.String("out/dir1/dir2/file3"),
-			Blob:         makeFileBlob("file3"),
-			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 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)
-				}
-			}
-		})
-	}
-}
diff --git a/remoteexec/inputroot.go b/remoteexec/inputroot.go
deleted file mode 100644
index 98223cd..0000000
--- a/remoteexec/inputroot.go
+++ /dev/null
@@ -1,318 +0,0 @@
-// Copyright 2018 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.
-
-package remoteexec
-
-import (
-	"errors"
-	"fmt"
-	"strings"
-
-	"go.chromium.org/goma/server/command/descriptor/posixpath"
-	"go.chromium.org/goma/server/command/descriptor/winpath"
-	gomapb "go.chromium.org/goma/server/proto/api"
-)
-
-func samePathElement(filepath clientFilePath, a, b []string) []string {
-	for i, p := range a {
-		if i >= len(b) {
-			return a[:i]
-		}
-		switch filepath.(type) {
-		case winpath.FilePath:
-			if strings.ToLower(p) != strings.ToLower(b[i]) {
-				return a[:i]
-			}
-		default:
-			if p != b[i] {
-				return a[:i]
-			}
-		}
-	}
-	return a
-}
-
-func commonDir(filepath clientFilePath, paths []string) string {
-	if len(paths) == 0 {
-		return ""
-	}
-	path := filepath.SplitElem(paths[0])
-	for _, p := range paths[1:] {
-		path = samePathElement(filepath, path, filepath.SplitElem(p))
-	}
-	return filepath.Join(path...)
-}
-
-// argv0InInputRoot checks argv0 should be in input root or not.
-// when argv0 is /usr/bin/*, it might use
-// the command in platform container image, so not
-// considered it as part of input root.
-// TODO: non-linux platforms?
-func argv0InInputRoot(argv0 string) bool {
-	for _, dir := range []string{"/bin/", "/sbin/", "/usr/bin/", "/usr/sbin/"} {
-		if strings.HasPrefix(argv0, dir) {
-			return false
-		}
-	}
-	return true
-}
-
-func sysrootDir(args []string) (string, bool) {
-	// https://github.com/llvm/llvm-project/blob/1d99472875100b230bac2d9ea70b5cd4b45e788b/clang/include/clang/Driver/Options.td
-	//  def isysroot : JoinedOrSeparate<["-"], "isysroot">, Group<clang_i_Group>, Flags<[CC1Option]>,
-	//     HelpText<"Set the system root directory (usually /)">, MetaVarName<"<dir>">;
-	//  def _sysroot_EQ : Joined<["--"], "sysroot=">;
-	//  def _sysroot : Separate<["--"], "sysroot">, Alias<_sysroot_EQ>;
-	var sysrootDir, isysrootDir string
-	var sysrootFlag, isysrootFlag bool
-	// https://releases.llvm.org/10.0.0/tools/clang/docs/DiagnosticsReference.html#wmissing-sysroot
-	// enabled by default
-	need := true
-	for _, arg := range args {
-		if sysrootFlag {
-			// separated, next arg of --sysroot
-			sysrootDir = arg
-			sysrootFlag = false
-			continue
-		}
-		if isysrootFlag {
-			// separated, next arg of -isysroot
-			isysrootDir = arg
-			isysrootFlag = false
-			continue
-		}
-		switch arg {
-		case "-Wall", "-Wmissing-sysroot":
-			need = true
-		case "-Wno-missing-sysroot":
-			need = false
-		case "-isysroot":
-			isysrootFlag = true
-		case "--sysroot":
-			sysrootFlag = true
-		default:
-			// handled joined.
-			switch {
-			case strings.HasPrefix(arg, "-isysroot"):
-				isysrootDir = strings.TrimPrefix(arg, "-isysroot")
-			case strings.HasPrefix(arg, "--sysroot="):
-				sysrootDir = strings.TrimPrefix(arg, "--sysroot=")
-			}
-		}
-	}
-	// https://gcc.gnu.org/onlinedocs/gcc/Directory-Options.html
-	// If you use both this option and the -isysroot option, then
-	// the --sysroot option applies to libraries, but the
-	// -isysroot option applies to header files.
-	if isysrootDir != "" {
-		return isysrootDir, need
-	}
-	return sysrootDir, need
-}
-
-func execPaths(filepath clientFilePath, req *gomapb.ExecReq, argv0 string) ([]string, error) {
-	cwd := filepath.Clean(req.GetCwd())
-	if !filepath.IsAbs(cwd) {
-		return nil, fmt.Errorf("cwd is not abs path: %s", cwd)
-	}
-	paths := []string{cwd}
-	for _, input := range req.Input {
-		filename := input.GetFilename()
-		if !filepath.IsAbs(filename) {
-			filename = filepath.Join(cwd, filename)
-		}
-		paths = append(paths, filepath.Clean(filename))
-	}
-	if argv0InInputRoot(argv0) {
-		if !filepath.IsAbs(argv0) {
-			argv0 = filepath.Join(cwd, argv0)
-		}
-		paths = append(paths, filepath.Clean(argv0))
-	}
-	sysrootDir, need := sysrootDir(req.Arg)
-	// if missing sysroot is allowed, no need to ensure it
-	if need && sysrootDir != "" {
-		// if request not allows missing sysroot, we
-		// need to create the directory even if no input files
-		// in dir are used. b/150421328
-
-		// allow relative path only.
-		// if dir is absolute, request would require
-		// InputRootAbsolutePath.
-		// TODO: cwdAgnosticReq should check input file path too.
-		// http://b/167342635
-		// http://crbug.com/1123938
-		if !filepath.IsAbs(sysrootDir) {
-			dir := filepath.Join(cwd, sysrootDir)
-			paths = append(paths, filepath.Clean(dir))
-		}
-	}
-	for _, output := range req.ExpectedOutputFiles {
-		if !filepath.IsAbs(output) {
-			output = filepath.Join(cwd, output)
-		}
-		paths = append(paths, filepath.Clean(output))
-	}
-	for _, output := range req.ExpectedOutputDirs {
-		if !filepath.IsAbs(output) {
-			output = filepath.Join(cwd, output)
-		}
-		paths = append(paths, filepath.Clean(output))
-	}
-	return paths, nil
-}
-
-// validCommonDir takes a path `dir` generated by commonDir() and returns
-// true if it represents a valid common path.
-func validCommonDir(filepath clientFilePath, dir string) bool {
-	if dir == "" {
-		return false
-	}
-	switch filepath.(type) {
-	case posixpath.FilePath:
-		if dir == "/" {
-			return false
-		}
-	case winpath.FilePath:
-		// The drive should be considered as root on Win.
-		// e.g. c:\ will return false.
-		if len(dir) == 3 && dir[1:] == `:\` {
-			return false
-		}
-
-	default:
-		// unknown filepath?
-		return true
-	}
-
-	return true
-}
-
-// needChroot returns true if chroot is needed to run the task with
-// given common `dir`
-func needChroot(filepath clientFilePath, dir string) bool {
-	return !validCommonDir(filepath, dir)
-}
-
-// getPathsWithNoCommonDir returns two or more paths from the input paths that
-// don't have a common dir above the root directory. If there is a common dir,
-// then returns an empty array. Does not guarantee any kind of order.
-func getPathsWithNoCommonDir(filepath clientFilePath, paths []string) []string {
-	if len(paths) < 2 {
-		return nil
-	}
-
-	p0 := paths[0]
-	// If there are any pairs of paths in `paths` that have no common directories,
-	// then there should be a mismatch between paths[0] and paths[i] for some i > 0.
-	for _, p1 := range paths[1:] {
-		pair := []string{p0, p1}
-		dir := commonDir(filepath, pair)
-		if !validCommonDir(filepath, dir) {
-			return pair
-		}
-	}
-	return nil
-}
-
-func checkExecRoot(filepath clientFilePath, dir string) error {
-	switch filepath.(type) {
-	case posixpath.FilePath:
-		// if dir covers these paths, command (e.g. clang) won't
-		// work because required *.so etc would not be accessible.
-		for _, p := range []string{
-			"/lib/x86_64-linux-gnu/",
-			"/usr/lib/x86_64-linux-gnu/",
-			"/lib64/",
-		} {
-			if strings.HasPrefix(p, dir+"/") {
-				return fmt.Errorf("bad input root: %s", dir)
-			}
-		}
-		return nil
-
-	case winpath.FilePath:
-		// TODO: check for win path.
-		return nil
-
-	default:
-		// unknown filepath?
-		return nil
-	}
-}
-
-// deriveExecRoot returns common root of paths.
-// if execRootDir is not empty, use it as root of paths.
-// If second return value is true, chroot must be used.  It become true only
-// if `allowChroot` is true and common input root is "/".
-func deriveExecRoot(filepath clientFilePath, paths []string, allowChroot bool, execRootDir string) (string, bool, error) {
-	if execRootDir != "" {
-		return execRootDir, execRootDir == "/" && allowChroot, nil
-	}
-	root := commonDir(filepath, paths)
-	if needChroot(filepath, root) && allowChroot {
-		switch filepath.(type) {
-		// TODO: support non-posix platform
-		case posixpath.FilePath:
-			return "/", true, nil
-		}
-	}
-	if !validCommonDir(filepath, root) {
-		pair := getPathsWithNoCommonDir(filepath, paths)
-		return "", false, fmt.Errorf("no common paths in inputs: %v", pair)
-	}
-	err := checkExecRoot(filepath, root)
-	if err != nil {
-		return "", false, err
-	}
-	return root, false, nil
-}
-
-var errOutOfRoot = errors.New("out of root")
-
-// rootRel returns relative path from rootDir for fname,
-// which is relative path from cwd, or absolute path.
-// cwd and rootDir should be clean path.
-func rootRel(filepath clientFilePath, fname, cwd, rootDir string) (string, error) {
-	if filepath == nil {
-		return "", errors.New("rootRel: client filepath unknown")
-	}
-	if !filepath.IsAbs(fname) {
-		fname = filepath.Join(cwd, fname)
-	}
-	// filepath.Rel cleans paths, so we can't use it here.
-	// suppose rootdir and cwd are clean path, and
-	// cwd is under rootdir.
-	rootElems := filepath.SplitElem(rootDir)
-	fileElems := filepath.SplitElem(fname)
-
-	if len(fileElems) < len(rootElems) {
-		return "", errOutOfRoot
-	}
-	commonElems := samePathElement(filepath, rootElems, fileElems)
-	if len(commonElems) != len(rootElems) {
-		return "", errOutOfRoot
-	}
-	fileElems = fileElems[len(rootElems):]
-	relname := filepath.Join(fileElems...)
-	fileElems = filepath.SplitElem(filepath.Clean(relname))
-	if len(fileElems) > 0 && fileElems[0] == ".." {
-		return "", errOutOfRoot
-	}
-	return relname, nil
-}
-
-// hasPrefixDir returns true if p has prefix as a directory name.
-// Note: it is case sensitive.
-// TODO: support non-posix platforms.
-func hasPrefixDir(p, prefix string) bool {
-	prefix = strings.TrimRight(prefix, "/")
-	if !strings.HasPrefix(p, prefix) {
-		return false
-	}
-	if len(p) == len(prefix) {
-		return true
-	}
-	return p[len(prefix)] == '/'
-}
diff --git a/remoteexec/inputroot_test.go b/remoteexec/inputroot_test.go
deleted file mode 100644
index e53cf9d..0000000
--- a/remoteexec/inputroot_test.go
+++ /dev/null
@@ -1,607 +0,0 @@
-// Copyright 2018 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.
-
-package remoteexec
-
-import (
-	"testing"
-
-	"github.com/google/go-cmp/cmp"
-	"google.golang.org/protobuf/proto"
-
-	"go.chromium.org/goma/server/command/descriptor/posixpath"
-	"go.chromium.org/goma/server/command/descriptor/winpath"
-	gomapb "go.chromium.org/goma/server/proto/api"
-)
-
-func TestGetPathsWithNoCommonDirPosix(t *testing.T) {
-	for _, tc := range []struct {
-		desc  string
-		paths []string
-		want  []string
-	}{
-		{
-			desc: "empty",
-			// paths: nil,
-			// want:  nil,
-		},
-		{
-			desc:  "single",
-			paths: []string{"/foo"},
-			// want:  nil,
-		},
-		{
-			desc: "has common dir",
-			paths: []string{
-				"/foo/bar1",
-				"/foo/local/bar2",
-				"/foo/bar3",
-			},
-			// want: nil,
-		},
-		{
-			desc: "no common dir #0",
-			paths: []string{
-				"/foo",
-				"/goo/local/baz",
-				"/goo/",
-				"/foo/local/bar2",
-				"/foo/bar3",
-			},
-			want: []string{
-				"/foo",
-				"/goo/local/baz",
-			},
-		},
-		{
-			desc: "no common dir #1",
-			paths: []string{
-				"/foo/local/bar2",
-				"/foo",
-				"/goo/",
-				"/goo/local/baz",
-				"/foo/bar3",
-			},
-			want: []string{
-				"/foo/local/bar2",
-				"/goo/",
-			},
-		},
-		{
-			desc: "no common dir #2",
-			paths: []string{
-				"/goo",
-				"/bar",
-				"/baz/",
-				"/foo",
-			},
-			want: []string{
-				"/goo",
-				"/bar",
-			},
-		},
-	} {
-		got := getPathsWithNoCommonDir(posixpath.FilePath{}, tc.paths)
-		if !cmp.Equal(got, tc.want) {
-			t.Errorf("test case %s: getPathsWithNoCommonDirPosix(paths=%v)=%v; want %v", tc.desc, tc.paths, got, tc.want)
-		}
-	}
-}
-
-func TestGetPathsWithNoCommonDirWin(t *testing.T) {
-	for _, tc := range []struct {
-		desc  string
-		paths []string
-		want  []string
-	}{
-		{
-			desc: `empty`,
-			// paths: nil,
-			// want:  nil,
-		},
-		{
-			desc:  `single`,
-			paths: []string{`/foo`},
-			// want:  nil,
-		},
-		{
-			desc: `has common dir`,
-			paths: []string{
-				`c:\foo\bar1`,
-				`c:\foo\local\bar2`,
-				`c:\foo\bar3`,
-			},
-			// want: nil,
-		},
-		{
-			desc: `has common dir with differnt pathsep`,
-			paths: []string{
-				`c:/foo/bar1`,
-				`c:\foo\local/bar2`,
-				`c:\foo\bar3`,
-			},
-			// want: nil,
-		},
-		{
-			desc: `has common dir with different case`,
-			paths: []string{
-				`C:\FOO\BAR1`,
-				`C:\Foo\Local\Bar2`,
-				`c:\foo\bar3`,
-			},
-			// want: nil,
-		},
-		{
-			desc: `no common dir`,
-			paths: []string{
-				`c:\foo`,
-				`c:\goo\local\baz`,
-				`c:\goo\`,
-				`c:\foo\local\bar2`,
-				`c:\foo\bar3`,
-			},
-			want: []string{
-				`c:\foo`,
-				`c:\goo\local\baz`,
-			},
-		},
-	} {
-		got := getPathsWithNoCommonDir(winpath.FilePath{}, tc.paths)
-		if !cmp.Equal(got, tc.want) {
-			t.Errorf(`test case %s: getPathsWithNoCommonDirWin(paths=%v)=%v; want %v`, tc.desc, tc.paths, got, tc.want)
-		}
-	}
-}
-
-func TestInputRootDir(t *testing.T) {
-	for _, tc := range []struct {
-		desc        string
-		req         *gomapb.ExecReq
-		argv0       string
-		allowChroot bool
-		execRoot    string
-		want        string
-		wantChroot  bool
-		wantPathErr bool
-		wantRootErr bool
-	}{
-		{
-			desc: "basic",
-			req: &gomapb.ExecReq{
-				Cwd: proto.String("/b/c/b/linux/src/out/Release"),
-				Input: []*gomapb.ExecReq_Input{
-					{
-						Filename: proto.String("../../base/logging.h"),
-					},
-					{
-						Filename: proto.String("../../build/linux/debian_sid_amd64-sysroot/usr/include/stdio.h"),
-					},
-					{
-						Filename: proto.String("gen/chrome/common/buildflags.h"),
-					},
-				},
-			},
-			argv0: "../../third_party/llvm-build/Release+Asserts/bin/clang++",
-			want:  "/b/c/b/linux/src",
-		},
-		{
-			desc: "execRoot",
-			req: &gomapb.ExecReq{
-				Cwd: proto.String("/b/c/b/linux/src/out/Release"),
-				Input: []*gomapb.ExecReq_Input{
-					{
-						Filename: proto.String("../../base/logging.h"),
-					},
-					{
-						Filename: proto.String("../../build/linux/debian_sid_amd64-sysroot/usr/include/stdio.h"),
-					},
-					{
-						Filename: proto.String("gen/chrome/common/buildflags.h"),
-					},
-					{
-						Filename: proto.String("/usr/include/stdio.h"),
-					},
-				},
-			},
-			argv0:    "../../third_party/llvm-build/Release+Asserts/bin/clang++",
-			execRoot: "/b/c/b/linux/src",
-			want:     "/b/c/b/linux/src",
-		},
-		{
-			desc: "abs input path",
-			req: &gomapb.ExecReq{
-				Cwd: proto.String("/b/c/b/linux/src/out/Release"),
-				Input: []*gomapb.ExecReq_Input{
-					{
-						Filename: proto.String("/b/c/b/linux/src/out/Release/../../base/logging.h"),
-					},
-					{
-						Filename: proto.String("/b/c/b/linux/src/out/Release/../../build/linux/debian_sid_amd64-sysroot/usr/include/stdio.h"),
-					},
-					{
-						Filename: proto.String("/b/c/b/linux/src/out/Release/gen/chrome/common/buildflags.h"),
-					},
-				},
-			},
-			argv0: "../../third_party/llvm-build/Release+Asserts/bin/clang++",
-			want:  "/b/c/b/linux/src",
-		},
-		{
-			desc: "abs resolved input path",
-			req: &gomapb.ExecReq{
-				Cwd: proto.String("/b/c/b/linux/src/out/Release"),
-				Input: []*gomapb.ExecReq_Input{
-					{
-						Filename: proto.String("/b/c/b/linux/src/base/logging.h"),
-					},
-					{
-						Filename: proto.String("/b/c/b/linux/src/build/linux/debian_sid_amd64-sysroot/usr/include/stdio.h"),
-					},
-					{
-						Filename: proto.String("/b/c/b/linux/src/out/Release/gen/chrome/common/buildflags.h"),
-					},
-				},
-			},
-			argv0: "../../third_party/llvm-build/Release+Asserts/bin/clang++",
-			want:  "/b/c/b/linux/src",
-		},
-		{
-			desc: "no common path",
-			req: &gomapb.ExecReq{
-				Cwd: proto.String("/b/c/b/linux/src/out/Release"),
-				Input: []*gomapb.ExecReq_Input{
-					{
-						Filename: proto.String("../../base/logging.h"),
-					},
-					{
-						Filename: proto.String("../../build/linux/debian_sid_amd64-sysroot/usr/include/stdio.h"),
-					},
-					{
-						Filename: proto.String("gen/chrome/common/buildflags.h"),
-					},
-					{
-						Filename: proto.String("/usr/local/include/foo.h"),
-					},
-				},
-			},
-			argv0:       "../../third_party/llvm-build/Release+Asserts/bin/clang++",
-			wantRootErr: true,
-		},
-		{
-			desc: "non-abs cwd",
-			req: &gomapb.ExecReq{
-				Cwd: proto.String("b/c/b/linux/src/out/Release"),
-				Input: []*gomapb.ExecReq_Input{
-					{
-						Filename: proto.String("../../base/logging.h"),
-					},
-					{
-						Filename: proto.String("../../build/linux/debian_sid_amd64-sysroot/usr/include/stdio.h"),
-					},
-					{
-						Filename: proto.String("gen/chrome/common/buildflags.h"),
-					},
-				},
-			},
-			argv0:       "../../third_party/llvm-build/Release+Asserts/bin/clang++",
-			wantPathErr: true,
-		},
-		{
-			desc: "gen files only",
-			req: &gomapb.ExecReq{
-				Cwd: proto.String("/b/c/b/linux/src/out/Release"),
-				Input: []*gomapb.ExecReq_Input{
-					{
-						Filename: proto.String("gen/chrome/common/buildflags.h"),
-					},
-					{
-						Filename: proto.String("gen/chrome/common/buildflags.cc"),
-					},
-				},
-			},
-			argv0: "../../third_party/llvm-build/Release+Asserts/bin/clang++",
-			want:  "/b/c/b/linux/src",
-		},
-		{
-			desc: "ignore /usr/bin/gcc",
-			req: &gomapb.ExecReq{
-				Cwd: proto.String("/b/c/b/linux/src/out/Release"),
-				Input: []*gomapb.ExecReq_Input{
-					{
-						Filename: proto.String("gen/chrome/common/buildflags.h"),
-					},
-					{
-						Filename: proto.String("gen/chrome/common/buildflags.cc"),
-					},
-				},
-			},
-			argv0: "/usr/bin/gcc",
-			want:  "/b/c/b/linux/src/out/Release",
-		},
-		{
-			desc: "cover system dirs (/usr)",
-			req: &gomapb.ExecReq{
-				Cwd: proto.String("/usr/home/foo/src/out/Release"),
-				Input: []*gomapb.ExecReq_Input{
-					{
-						Filename: proto.String("../../base/logging.h"),
-					},
-					{
-						Filename: proto.String("../../build/linux/debian_sid_amd64-sysroot/usr/include/stdio.h"),
-					},
-					{
-						Filename: proto.String("gen/chrome/common/buildflags.h"),
-					},
-					{
-						Filename: proto.String("/usr/lib/gcc/x86_64-linux-gnu/7/crtbegin.o"),
-					},
-				},
-			},
-			argv0:       "../../third_party/llvm-build/Release+Asserts/bin/clang++",
-			wantRootErr: true,
-		},
-		{
-			desc: "wantChroot for /usr",
-			req: &gomapb.ExecReq{
-				Cwd: proto.String("/home/foo/src/out/Release"),
-				Input: []*gomapb.ExecReq_Input{
-					{
-						Filename: proto.String("/usr/include/config.h"),
-					},
-				},
-			},
-			argv0:       "../../third_party/llvm-build/Release+Asserts/bin/clang++",
-			want:        "/",
-			allowChroot: true,
-			wantChroot:  true,
-		},
-		{
-			desc: "--sysroot=, but ignore system include paths",
-			req: &gomapb.ExecReq{
-				Arg: []string{
-					"../../third_party/llvm-build/Release+Asserts/bin/clang++",
-					"-Wall",
-					"-Werror",
-					"--sysroot=../../build/linux/debian_sid_amd64-sysroot",
-				},
-				Cwd: proto.String("/b/c/b/chromeos/src/out/Release"),
-				Input: []*gomapb.ExecReq_Input{
-					{
-						Filename: proto.String("../../base/logging.h"),
-					},
-					{
-						Filename: proto.String("gen/chrome/common/buildflags.h"),
-					},
-				},
-				CommandSpec: &gomapb.CommandSpec{
-					SystemIncludePath: []string{
-						"../../build/linux/debian_sid_amd64-sysroot/usr/include",
-						"/usr/include",
-					},
-				},
-			},
-			argv0: "../../third_party/llvm-build/Release+Asserts/bin/clang++",
-			want:  "/b/c/b/chromeos/src",
-		},
-	} {
-		t.Logf("test case: %s", tc.desc)
-		paths, err := execPaths(posixpath.FilePath{}, tc.req, tc.argv0)
-		if tc.wantPathErr {
-			if err == nil {
-				t.Errorf("execPaths(req, %q)=%v", tc.argv0, paths)
-			}
-			continue
-		}
-		t.Logf("paths=%q", paths)
-		if err != nil {
-			t.Errorf("execPaths(req, %q)=%v, %v; want nil error", tc.argv0, paths, err)
-		}
-		got, needChroot, err := deriveExecRoot(posixpath.FilePath{}, paths, tc.allowChroot, tc.execRoot)
-		if tc.wantRootErr {
-			if err == nil {
-				t.Errorf("deriveExecRoot(files)=%v, %t, nil; want err", got, needChroot)
-			}
-			continue
-		}
-		if err != nil || got != tc.want || needChroot != tc.wantChroot {
-			t.Errorf("deriveExecRoot(files)=%v, %t, %v; want %v, %t, nil", got, needChroot, err, tc.want, tc.wantChroot)
-		}
-	}
-}
-
-func TestRootRelPosix(t *testing.T) {
-	for _, tc := range []struct {
-		fname, cwd, rootDir string
-		want                string
-		wantErr             bool
-	}{
-		{
-			fname:   "../../base/foo.cc",
-			cwd:     "/home/foo/src/chromium/src/out/Release",
-			rootDir: "/home/foo/src/chromium/src",
-			want:    "out/Release/../../base/foo.cc",
-		},
-		{
-			fname:   "gen/foo.cc",
-			cwd:     "/home/foo/src/chromium/src/out/Release",
-			rootDir: "/home/foo/src/chromium/src",
-			want:    "out/Release/gen/foo.cc",
-		},
-		{
-			fname:   "/home/foo/src/chromium/src/third_party/depot_tools/win_toolchain/vs_files/hash/win_sdk/bin/../../VC/Tols/MSVC/14.x.x/include/limit.h",
-			cwd:     "/home/foo/src/chromium/src/out/Release",
-			rootDir: "/home/foo/src/chromium/src",
-			want:    "third_party/depot_tools/win_toolchain/vs_files/hash/win_sdk/bin/../../VC/Tols/MSVC/14.x.x/include/limit.h",
-		},
-		{
-			fname:   "../../../base/out-of-root.h",
-			cwd:     "/home/foo/src/chromium/src/out/Release",
-			rootDir: "/home/foo/src/chromium/src",
-			wantErr: true,
-		},
-		{
-			fname:   "../../base/../../other/out-of-root.h",
-			cwd:     "/home/foo/src/chromium/src/out/Release",
-			rootDir: "/home/foo/src/chromium/src",
-			wantErr: true,
-		},
-		{
-			fname:   "/usr/bin/objcopy",
-			cwd:     "/home/foo/src/chromium/src/out/Release",
-			rootDir: "/home/foo/src/chromium/src",
-			wantErr: true,
-		},
-		{
-			fname:   "../../base/foo.h",
-			cwd:     "/home/bar/../foo/src/chromium/src/out/Release",
-			rootDir: "/home/foo/src/chromium/src",
-			want:    "out/Release/../../base/foo.h",
-		},
-		{
-			fname:   "../../base/../a/../b/foo.h",
-			cwd:     "/home/bar/../foo/src/chromium/src/out/Release",
-			rootDir: "/home/foo/src/chromium/src",
-			want:    "out/Release/../../base/../a/../b/foo.h",
-		},
-		{
-			fname:   "../../base/../../a/b/../c/foo.h",
-			cwd:     "/home/bar/../foo/src/chromium/src/out/Release",
-			rootDir: "/home/foo/src/chromium/src",
-			wantErr: true,
-		},
-	} {
-		var filepath posixpath.FilePath
-		got, err := rootRel(filepath, tc.fname, filepath.Clean(tc.cwd), filepath.Clean(tc.rootDir))
-		if tc.wantErr {
-			if err == nil {
-				t.Errorf("rootRel(posixpath.FilePath, %q, %q, %q)=%v, nil; want error", tc.fname, tc.cwd, tc.rootDir, got)
-			}
-			continue
-		}
-		if err != nil || got != tc.want {
-			t.Errorf("rootRel(posixpath.FilePath, %q, %q, %q)=%q, %v; want %q, nil", tc.fname, tc.cwd, tc.rootDir, got, err, tc.want)
-		}
-	}
-}
-
-func TestRootRelWin(t *testing.T) {
-	for _, tc := range []struct {
-		fname, cwd, rootDir string
-		want                string
-		wantErr             bool
-	}{
-		{
-			fname:   `..\..\base\foo.cc`,
-			cwd:     `c:\Users\foo\src\chromium\src\out\Release`,
-			rootDir: `c:\Users\foo\src\chromium\src`,
-			want:    `out\Release\..\..\base\foo.cc`,
-		},
-		{ // should ignore case to get relative path but preserve original case.
-			fname:   `..\..\Base\Foo.cc`,
-			cwd:     `C:\Users\Foo\Src\Chromium\Src\Out\Release`,
-			rootDir: `c:\users\foo\src\chromium\src`,
-			want:    `Out\Release\..\..\Base\Foo.cc`,
-		},
-		{
-			fname:   `c:\Users\foo\src\chromium\src\third_party\depot_tools\win_toolchain\vs_files\hash\win_sdk\bin\..\..\VC\Tols\MSVC\14.x.x\include\limit.h`,
-			cwd:     `c:\Users\foo\src\chromium\src\out\Release`,
-			rootDir: `c:\Users\foo\src\chromium\src`,
-			want:    `third_party\depot_tools\win_toolchain\vs_files\hash\win_sdk\bin\..\..\VC\Tols\MSVC\14.x.x\include\limit.h`,
-		},
-		{
-			fname:   `..\..\..\base\out-of-root.h`,
-			cwd:     `c:\Users\foo\src\chromium\src\out\Release`,
-			rootDir: `c:\Users\foo\src\chromium\src`,
-			wantErr: true,
-		},
-	} {
-		var filepath winpath.FilePath
-		got, err := rootRel(filepath, tc.fname, filepath.Clean(tc.cwd), filepath.Clean(tc.rootDir))
-		if tc.wantErr {
-			if err == nil {
-				t.Errorf("rootRel(winpath.FilePath, %q, %q, %q)=%v, nil; want error", tc.fname, tc.cwd, tc.rootDir, got)
-			}
-			continue
-		}
-		if err != nil || got != tc.want {
-			t.Errorf("rootRel(winpath.FilePath, %q, %q, %q)=%q, %v; want %q, nil", tc.fname, tc.cwd, tc.rootDir, got, err, tc.want)
-		}
-	}
-}
-
-func BenchmarkRootRel(b *testing.B) {
-	for i := 0; i < b.N; i++ {
-		rootRel(posixpath.FilePath{}, "../../base/foo.cc", "/home/foo/src/chromium/src/out/Release", "/home/foo/src/chromium/src")
-	}
-}
-
-func TestHasPrefixDir(t *testing.T) {
-	for _, tc := range []struct {
-		p, prefix string
-		want      bool
-	}{
-		{
-			p:      "/home/foo/bar",
-			prefix: "/home/foo",
-			want:   true,
-		},
-		{
-			p:      "/home/foo",
-			prefix: "/home/foo",
-			want:   true,
-		},
-		{
-			p:      "/home/foo/",
-			prefix: "/home/foo",
-			want:   true,
-		},
-		// hasPrefixDir trim "/" in `prefix`
-		{
-			p:      "/home/foo",
-			prefix: "/home/foo////////////////",
-			want:   true,
-		},
-		{
-			p:      "/foo",
-			prefix: "/bar",
-			want:   false,
-		},
-		{
-			p:      "/foo/bar",
-			prefix: "/bar",
-			want:   false,
-		},
-		{
-			p:      "/foo",
-			prefix: "/bar/baz",
-			want:   false,
-		},
-		{
-			p:      "/foo",
-			prefix: "/foo/bar",
-			want:   false,
-		},
-		{
-			p:      "/home/foobar",
-			prefix: "/home/foo",
-			want:   false,
-		},
-
-		{
-			p:      "home/foo",
-			prefix: "home/foo",
-			want:   true,
-		},
-		{
-			p:      "home/foo/bar",
-			prefix: "home/foo",
-			want:   true,
-		},
-	} {
-		got := hasPrefixDir(tc.p, tc.prefix)
-		if got != tc.want {
-			t.Errorf("hasPrefixDir(%s,%s) = %t; want %t", tc.p, tc.prefix, got, tc.want)
-		}
-	}
-}
diff --git a/remoteexec/javac.go b/remoteexec/javac.go
deleted file mode 100644
index a483b1a..0000000
--- a/remoteexec/javac.go
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2018 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.
-
-package remoteexec
-
-// TODO: share exec/javac.go ?
-
-// javacOutputDirs returns output directories from javac command line.
-func javacOutputDirs(args []string) []string {
-	var dirs []string
-	dirArg := false
-
-	for _, arg := range args {
-		switch {
-		case dirArg:
-			dirs = append(dirs, arg)
-			dirArg = false
-
-		case arg == "-s" || arg == "-d":
-			dirArg = true
-		}
-	}
-	return dirs
-}
diff --git a/remoteexec/javac_test.go b/remoteexec/javac_test.go
deleted file mode 100644
index 6179a20..0000000
--- a/remoteexec/javac_test.go
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2018 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.
-
-package remoteexec
-
-import (
-	"reflect"
-	"testing"
-)
-
-func TestJavacOutputDirs(t *testing.T) {
-	for _, tc := range []struct {
-		desc string
-		args []string
-		want []string
-	}{
-		{
-			desc: "basic",
-			args: []string{
-				"javac", "-J-Xmx512M",
-				"-target", "1.8",
-				"-d", "A",
-				"-s", "B",
-				"-cp", "foo.jar:bar.jar",
-				"-bootclasspath", "baz.jar",
-				"Hello.java", "World.java",
-			},
-			want: []string{"A", "B"},
-		},
-	} {
-		t.Run(tc.desc, func(t *testing.T) {
-			if got := javacOutputDirs(tc.args); !reflect.DeepEqual(got, tc.want) {
-				t.Errorf("clangclOutputs(%q)=%q; want %q", tc.args, got, tc.want)
-			}
-		})
-	}
-}
diff --git a/remoteexec/merkletree/merkletree.go b/remoteexec/merkletree/merkletree.go
deleted file mode 100644
index c1be89d..0000000
--- a/remoteexec/merkletree/merkletree.go
+++ /dev/null
@@ -1,291 +0,0 @@
-/* Copyright 2018 Google Inc. All Rights Reserved. */
-
-// Package merkletree operates on a merkle tree for remote execution API,
-// https://github.com/bazelbuild/remote-apis/blob/c1c1ad2c97ed18943adb55f06657440daa60d833/build/bazel/remote/execution/v2/remote_execution.proto#L838
-//
-// see https://en.Wikipedia.org/wiki/Merkle_tree
-package merkletree
-
-import (
-	"context"
-	"errors"
-	"fmt"
-	"sort"
-	"strings"
-
-	"google.golang.org/protobuf/proto"
-
-	"go.chromium.org/goma/server/log"
-	"go.chromium.org/goma/server/remoteexec/digest"
-
-	rpb "github.com/bazelbuild/remote-apis/build/bazel/remote/execution/v2"
-)
-
-// MerkleTree represents a merkle tree.
-type MerkleTree struct {
-	filepath FilePath
-
-	rootDir string
-	root    *rpb.Directory
-
-	// dirname to Directory
-	m     map[string]*rpb.Directory
-	store *digest.Store
-}
-
-// FilePath provides filepath functionalities.
-type FilePath interface {
-	IsAbs(path string) bool
-	Join(elem ...string) string
-	SplitElem(path string) []string
-}
-
-// New creates new merkle tree under rootDir with cas store.
-func New(filepath FilePath, rootDir string, store *digest.Store) *MerkleTree {
-	return &MerkleTree{
-		filepath: filepath,
-		rootDir:  rootDir,
-		root:     &rpb.Directory{},
-		m:        make(map[string]*rpb.Directory),
-		store:    store,
-	}
-}
-
-// RootDir returns root dir of merkle tree.
-func (m *MerkleTree) RootDir() string {
-	return m.rootDir
-}
-
-// Entry is an entry in the tree.
-type Entry struct {
-	// Name is relative path from root dir.
-	// it might not be clean path.
-	// 'dir1/../dir2/file' will create
-	//  - 'dir1/'
-	//  - 'dir2/'
-	//  - 'dir2/file'
-	// error if name goes out to root.
-	Name string
-
-	// Data is entry's content. `nil` for directories and symlinks.
-	Data digest.Data
-
-	// IsExecutable is true if the file is executable.
-	// no need to set this for directory.
-	IsExecutable bool
-
-	// If the file is a symlink, then this should be set to the target of the symlink.
-	Target string
-}
-
-var (
-	// ErrAbsPath indicates name in Entry is absulute path.
-	ErrAbsPath = errors.New("merkletree: absolute path name")
-
-	// ErrAmbigFileSymlink indicates Entry has both `Data` and `Target` fields, cannot determine
-	// whether it is File or Symlink.
-	ErrAmbigFileSymlink = errors.New("merkletree: unable to determine file vs symlink")
-
-	// ErrBadPath indicates name in Entry contains bad path component
-	// e.g. "." or "..".
-	ErrBadPath = errors.New("merkletree: bad path component")
-)
-
-type dirstate struct {
-	name string
-	dir  *rpb.Directory
-}
-
-// Set sets an entry.
-// It may return ErrAbsPath/ErrAmbigFileSymlink/ErrBadPath as error.
-func (m *MerkleTree) Set(entry Entry) error {
-	if entry.Target != "" && entry.Data != nil {
-		return ErrAmbigFileSymlink
-	}
-	fname := entry.Name
-	if m.filepath.IsAbs(fname) {
-		return ErrAbsPath
-	}
-	if entry.Data == nil && entry.Target == "" {
-		// entry is dir
-		if _, exists := m.m[fname]; exists {
-			// dir already exists
-			return nil
-		}
-	}
-	elems := m.filepath.SplitElem(fname)
-	if len(elems) == 0 {
-		if entry.Data != nil {
-			return ErrBadPath
-		}
-		return nil
-	}
-	cur := dirstate{
-		name: ".",
-		dir:  m.root,
-	}
-	var dirstack []dirstate
-	for {
-		var name string
-		name, elems = elems[0], elems[1:]
-		if len(elems) == 0 {
-			// a leaf.
-			if entry.Data == nil {
-				if name == "." {
-					return nil
-				}
-				if name == ".." && len(dirstack) == 0 {
-					return ErrBadPath
-				}
-				if name == ".." {
-					return nil
-				}
-				if entry.Target != "" {
-					cur.dir.Symlinks = append(cur.dir.Symlinks, &rpb.SymlinkNode{
-						Name:   name,
-						Target: entry.Target,
-					})
-					return nil
-				}
-				m.setDir(cur, name)
-				return nil
-			}
-			if name == "." || name == ".." {
-				return ErrBadPath
-			}
-			m.store.Set(entry.Data)
-			cur.dir.Files = append(cur.dir.Files, &rpb.FileNode{
-				Name:         name,
-				Digest:       entry.Data.Digest(),
-				IsExecutable: entry.IsExecutable,
-			})
-			return nil
-		}
-		if name == "." {
-			continue
-		}
-		if name == ".." {
-			if len(dirstack) == 0 {
-				return ErrBadPath
-			}
-			cur, dirstack = dirstack[len(dirstack)-1], dirstack[:len(dirstack)-1]
-			continue
-		}
-		dirstack = append(dirstack, cur)
-		cur = m.setDir(cur, name)
-	}
-}
-
-func pathJoin(dir, base string) string {
-	var b strings.Builder
-	if dir == "." || dir == "" {
-		return base
-	}
-	b.Grow(len(dir) + 1 + len(base))
-	b.WriteString(dir)
-	b.WriteByte('/')
-	b.WriteString(base)
-	return b.String()
-}
-
-func (m *MerkleTree) setDir(cur dirstate, name string) dirstate {
-	dirname := pathJoin(cur.name, name)
-	dir, exists := m.m[dirname]
-	if !exists {
-		dirnode := &rpb.DirectoryNode{
-			Name: name,
-			// compute digest later
-		}
-		cur.dir.Directories = append(cur.dir.Directories, dirnode)
-		dir = &rpb.Directory{}
-		m.m[dirname] = dir
-	}
-	return dirstate{name: dirname, dir: dir}
-}
-
-// Build builds merkle tree and returns root's digest.
-func (m *MerkleTree) Build(ctx context.Context) (*rpb.Digest, error) {
-	return m.buildTree(ctx, m.root, "")
-}
-
-// buildtree builds tree at curdir, which is located as dirname.
-func (m *MerkleTree) buildTree(ctx context.Context, curdir *rpb.Directory, dirname string) (*rpb.Digest, error) {
-	logger := log.FromContext(ctx)
-	// directory should not have duplicate name.
-	// http://b/124693412
-	names := map[string]proto.Message{}
-	var files []*rpb.FileNode
-	for _, f := range curdir.Files {
-		p, found := names[f.Name]
-		if found {
-			if !proto.Equal(f, p) {
-				return nil, fmt.Errorf("duplicate file %s in %s: %s != %s", f.Name, dirname, f, p)
-			}
-			// goma client might send duplicate entries such as
-			//   dir/subdir/../otherdir/name
-			//   dir/otherdir/name
-			logger.Infof("duplicate file %s in %s: %s", f.Name, dirname, f)
-			continue
-		}
-		names[f.Name] = f
-		files = append(files, f)
-	}
-	sort.Slice(files, func(i, j int) bool {
-		return files[i].Name < files[j].Name
-	})
-	curdir.Files = files
-
-	var symlinks []*rpb.SymlinkNode
-	for _, s := range curdir.Symlinks {
-		p, found := names[s.Name]
-		if found {
-			if !proto.Equal(s, p) {
-				return nil, fmt.Errorf("duplicate symlink %s in %s: %s != %s", s.Name, dirname, s, p)
-			}
-			logger.Infof("duplicate symlink %s in %s: %s", s.Name, dirname, s)
-			continue
-		}
-		names[s.Name] = s
-		symlinks = append(symlinks, s)
-	}
-	sort.Slice(symlinks, func(i, j int) bool {
-		return symlinks[i].Name < symlinks[j].Name
-	})
-	curdir.Symlinks = symlinks
-
-	var dirs []*rpb.DirectoryNode
-	for _, subdir := range curdir.Directories {
-		dirname := pathJoin(dirname, subdir.Name)
-		dir, found := m.m[dirname]
-		if !found {
-			return nil, fmt.Errorf("directory not found: %s", dirname)
-		}
-		digest, err := m.buildTree(ctx, dir, dirname)
-		if err != nil {
-			return nil, err
-		}
-		subdir.Digest = digest
-
-		p, found := names[subdir.Name]
-		if found {
-			if !proto.Equal(subdir, p) {
-				return nil, fmt.Errorf("duplicate dir %s in %s: %s != %s", subdir.Name, dirname, subdir, p)
-			}
-			logger.Infof("duplicate dir %s in %s: %s", subdir.Name, dirname, subdir)
-			continue
-		}
-		names[subdir.Name] = subdir
-		dirs = append(dirs, subdir)
-	}
-	sort.Slice(dirs, func(i, j int) bool {
-		return dirs[i].Name < dirs[j].Name
-	})
-	curdir.Directories = dirs
-
-	data, err := digest.Proto(curdir)
-	if err != nil {
-		return nil, fmt.Errorf("directory digest %s: %v", dirname, err)
-	}
-	m.store.Set(data)
-	return data.Digest(), nil
-}
diff --git a/remoteexec/merkletree/merkletree_test.go b/remoteexec/merkletree/merkletree_test.go
deleted file mode 100644
index 925cc85..0000000
--- a/remoteexec/merkletree/merkletree_test.go
+++ /dev/null
@@ -1,569 +0,0 @@
-/* Copyright 2018 Google Inc. All Rights Reserved. */
-
-package merkletree
-
-import (
-	"context"
-	"errors"
-	"fmt"
-	"path/filepath"
-	"reflect"
-	"sort"
-	"strings"
-	"testing"
-
-	rpb "github.com/bazelbuild/remote-apis/build/bazel/remote/execution/v2"
-
-	"google.golang.org/protobuf/proto"
-
-	"go.chromium.org/goma/server/command/descriptor/posixpath"
-	"go.chromium.org/goma/server/remoteexec/datasource"
-	"go.chromium.org/goma/server/remoteexec/digest"
-)
-
-func TestRootDir(t *testing.T) {
-	ds := digest.NewStore()
-	mt := New(posixpath.FilePath{}, "/path/to/root", ds)
-
-	if got, want := mt.RootDir(), "/path/to/root"; got != want {
-		t.Errorf("mt.RootDir()=%q; want=%q", got, want)
-	}
-}
-
-func TestSet(t *testing.T) {
-	for _, tc := range []struct {
-		Entry
-		wantErr  bool
-		wantName string
-		wantNode proto.Message // one of *rpb.FileNode or *rpb.SymlinkNode
-		wantDirs []string
-	}{
-		{
-			Entry: Entry{
-				Name:         "third_party/llvm-build/Release+Asserts/bin/clang",
-				Data:         digest.Bytes("clang binary", []byte("clang binary")),
-				IsExecutable: true,
-			},
-			wantName: "clang",
-			wantNode: &rpb.FileNode{
-				Name: "third_party/llvm-build/Release+Asserts/bin/clang",
-			},
-			wantDirs: []string{
-				"third_party",
-				"third_party/llvm-build",
-				"third_party/llvm-build/Release+Asserts",
-				"third_party/llvm-build/Release+Asserts/bin",
-			},
-		},
-		{
-			Entry: Entry{
-				Name:   "third_party/llvm-build/Release+Asserts/bin/clang++",
-				Target: "clang",
-			},
-			wantName: "clang++",
-			wantNode: &rpb.SymlinkNode{
-				Name:   "third_party/llvm-build/Release+Asserts/bin/clang++",
-				Target: "clang",
-			},
-			wantDirs: []string{
-				"third_party",
-				"third_party/llvm-build",
-				"third_party/llvm-build/Release+Asserts",
-				"third_party/llvm-build/Release+Asserts/bin",
-			},
-		},
-		{
-			Entry: Entry{
-				Name: "path/../name",
-			},
-			// create 'path' dir and 'name' dir.
-			wantDirs: []string{
-				"path",
-				"name",
-			},
-		},
-		{
-			Entry: Entry{
-				Name: "../path/name",
-			},
-			// out of root.
-			wantErr: true,
-		},
-		{
-			Entry: Entry{
-				Name: "path/name/..",
-			},
-			// create 'path/name' dir.
-			wantDirs: []string{
-				"path",
-				"path/name",
-			},
-		},
-		{
-			Entry: Entry{
-				Name: "path/name/.",
-			},
-			wantDirs: []string{
-				"path",
-				"path/name",
-			},
-		},
-		{
-			Entry: Entry{
-				Name: "path/./name",
-			},
-			wantDirs: []string{
-				"path",
-				"path/name",
-			},
-		},
-		{
-			Entry: Entry{
-				Name: "path//name",
-			},
-			wantDirs: []string{
-				"path",
-				"path/name",
-			},
-		},
-		{
-			Entry: Entry{
-				Name: "..",
-			},
-			// out of root.
-			wantErr: true,
-		},
-		{
-			Entry: Entry{
-				Name: "path/name/.",
-				Data: digest.Bytes("file", []byte("file")),
-			},
-			wantErr: true,
-		},
-		{
-			Entry: Entry{
-				Name: "path/name/..",
-				Data: digest.Bytes("file", []byte("file")),
-			},
-			wantErr: true,
-		},
-		{
-			Entry: Entry{
-				Name: "path/name/../../foo",
-			},
-			// "foo" dir
-			wantDirs: []string{
-				"path",
-				"path/name",
-				"foo",
-			},
-		},
-		{
-			Entry: Entry{
-				Name: "path/name/../../foo",
-				Data: digest.Bytes("file", []byte("file")),
-			},
-			// "foo" file
-			wantName: "foo",
-			wantNode: &rpb.FileNode{
-				Name: "foo",
-			},
-			wantDirs: []string{
-				"path",
-				"path/name",
-			},
-		},
-		{
-			Entry: Entry{
-				Name: "path/name/../..",
-			},
-			// root dir
-			wantDirs: []string{
-				"path",
-				"path/name",
-			},
-		},
-		{
-			Entry: Entry{
-				Name: "path/name/../..",
-				Data: digest.Bytes("file", []byte("file")),
-			},
-			// .. should not be file.
-			wantErr: true,
-		},
-		{
-			Entry: Entry{
-				Name: "path/name/../../../path/foo",
-			},
-			// go outside of root.
-			wantErr: true,
-		},
-		{
-			Entry: Entry{
-				Name: "/full/path/name",
-			},
-			wantErr: true,
-		},
-	} {
-		ds := digest.NewStore()
-		mt := New(posixpath.FilePath{}, "/path/to/root", ds)
-		err := mt.Set(tc.Entry)
-		if (err != nil) != tc.wantErr {
-			t.Errorf("mt.Set(%v)=%v; want err=%t", tc.Entry, err, tc.wantErr)
-		}
-		if tc.wantErr {
-			continue
-		}
-		t.Logf("check for mt.Set(%v)", tc.Entry)
-		if tc.wantNode != nil {
-			key := ""
-			switch wantNode := tc.wantNode.(type) {
-			case *rpb.FileNode:
-				key = wantNode.Name
-			case *rpb.SymlinkNode:
-				key = wantNode.Name
-			default:
-				t.Fatalf("Wrong node type: %T", tc.wantNode)
-			}
-			node, err := getNode(mt, key)
-			if err != nil {
-				t.Errorf("node(%q)=%#v, %v; want node, nil", key, node, err)
-			}
-		}
-		for _, dirname := range tc.wantDirs {
-			node, err := getNode(mt, dirname)
-			_, ok := node.(*rpb.Directory)
-			if err != nil || !ok {
-				t.Errorf("node(%q)=%#v, %v; want directory, nil", dirname, node, err)
-			}
-		}
-		sort.Strings(tc.wantDirs)
-		var dirs []string
-		for k := range mt.m {
-			dirs = append(dirs, k)
-		}
-		sort.Strings(dirs)
-		if !reflect.DeepEqual(dirs, tc.wantDirs) {
-			t.Errorf("dirs=%q; want=%q", dirs, tc.wantDirs)
-		}
-	}
-}
-
-func getNode(mt *MerkleTree, path string) (proto.Message, error) {
-	elems := strings.Split(filepath.Clean(path), "/")
-	cur := mt.root
-	if len(elems) == 0 {
-		return cur, nil
-	}
-	var paths []string
-	for {
-		var name string
-		name, elems = elems[0], elems[1:]
-		if len(elems) == 0 {
-			for _, n := range cur.Files {
-				if name == n.Name {
-					return n, nil
-				}
-			}
-			for _, n := range cur.Symlinks {
-				if name == n.Name {
-					return n, nil
-				}
-			}
-			return cur, nil
-		}
-		paths = append(paths, name)
-		var ok bool
-		cur, ok = mt.m[strings.Join(paths, "/")]
-		if !ok {
-			return nil, fmt.Errorf("%s not found in %s", name, strings.Join(paths[:len(paths)-1], "/"))
-		}
-	}
-}
-
-func TestBuildInvalidEntry(t *testing.T) {
-	ds := digest.NewStore()
-	mt := New(posixpath.FilePath{}, "/path/to/root", ds)
-
-	for _, ent := range []Entry{
-		{
-			// Invalid Entry: Absolute path.
-			Name:         "/usr/bin/third_party/llvm-build/Release+Asserts/bin/clang",
-			Data:         digest.Bytes("clang binary", []byte("clang binary")),
-			IsExecutable: true,
-		},
-		{
-			// Invalid Entry: has both `Data` and `Target` fields set.
-			Name:   "third_party/llvm-build/Release+Asserts/bin/clang++",
-			Data:   digest.Bytes("clang binary", []byte("clang binary")),
-			Target: "clang",
-		},
-	} {
-		err := mt.Set(ent)
-		if err == nil {
-			t.Fatalf("mt.Set(%q)=nil; want=(error)", ent.Name)
-		}
-	}
-}
-
-func TestBuild(t *testing.T) {
-	ctx := context.Background()
-	ds := digest.NewStore()
-	mt := New(posixpath.FilePath{}, "/path/to/root", ds)
-
-	for _, ent := range []Entry{
-		{
-			Name:         "third_party/llvm-build/Release+Asserts/bin/clang",
-			Data:         digest.Bytes("clang binary", []byte("clang binary")),
-			IsExecutable: true,
-		},
-		{
-			Name:   "third_party/llvm-build/Release+Asserts/bin/clang++-1",
-			Target: "clang",
-		},
-		{
-			Name:   "third_party/llvm-build/Release+Asserts/bin/clang++",
-			Target: "clang",
-		},
-		{
-			Name: "base/build_time.h",
-			Data: digest.Bytes("base_time.h", []byte("byte_time.h content")),
-		},
-		{
-			Name: "out/Release/obj/base",
-			// directory
-		},
-		{
-			Name: "base/debug/debugger.cc",
-			Data: digest.Bytes("debugger.cc", []byte("debugger.cc content")),
-		},
-		{
-			Name: "base/test/../macros.h",
-			Data: digest.Bytes("macros.h", []byte("macros.h content")),
-		},
-		// de-dup for same content http://b/124693412
-		{
-			Name: "third_party/skia/include/private/SkSafe32.h",
-			Data: digest.Bytes("SkSafe32.h", []byte("SkSafe32.h content")),
-		},
-		{
-			Name: "third_party/skia/include/private/SkSafe32.h",
-			Data: digest.Bytes("SkSafe32.h", []byte("SkSafe32.h content")),
-		},
-	} {
-		err := mt.Set(ent)
-		if err != nil {
-			t.Fatalf("mt.Set(%q)=%v; want=nil", ent.Name, err)
-		}
-	}
-
-	d, err := mt.Build(ctx)
-	if err != nil {
-		t.Fatalf("mt.Build()=_, %v; want=nil", err)
-	}
-
-	dir, err := openDir(ctx, ds, d)
-	if err != nil {
-		t.Fatalf("root %v not found: %v", d, err)
-	}
-	checkDir(ctx, t, ds, dir, "",
-		nil,
-		[]string{"base", "out", "third_party"},
-		nil)
-	baseDir := checkDir(ctx, t, ds, dir, "base",
-		[]string{"build_time.h", "macros.h"},
-		[]string{"debug", "test"},
-		nil)
-
-	checkDir(ctx, t, ds, baseDir, "debug",
-		[]string{"debugger.cc"},
-		nil, nil)
-	checkDir(ctx, t, ds, baseDir, "test",
-		nil, nil, nil)
-
-	outDir := checkDir(ctx, t, ds, dir, "out", nil, []string{"Release"}, nil)
-	releaseDir := checkDir(ctx, t, ds, outDir, "Release", nil, []string{"obj"}, nil)
-	objDir := checkDir(ctx, t, ds, releaseDir, "obj", nil, []string{"base"}, nil)
-	checkDir(ctx, t, ds, objDir, "base", nil, nil, nil)
-
-	tpDir := checkDir(ctx, t, ds, dir, "third_party", nil, []string{"llvm-build", "skia"}, nil)
-	llvmDir := checkDir(ctx, t, ds, tpDir, "llvm-build", nil, []string{"Release+Asserts"}, nil)
-	raDir := checkDir(ctx, t, ds, llvmDir, "Release+Asserts", nil, []string{"bin"}, nil)
-	binDir := checkDir(ctx, t, ds, raDir, "bin", []string{"clang"}, nil, []string{"clang++", "clang++-1"})
-
-	_, isExecutable, err := getDigest(binDir, "clang")
-	if err != nil || !isExecutable {
-		t.Errorf("clang is not executable: %t, %v; want: true, nil", isExecutable, err)
-	}
-	for _, symlink := range []string{"clang++", "clang++-1"} {
-		_, _, err := getDigest(binDir, symlink)
-		if err != nil {
-			t.Errorf("%s not found", symlink)
-		}
-	}
-	skiaDir := checkDir(ctx, t, ds, tpDir, "skia", nil, []string{"include"}, nil)
-	skiaIncludeDir := checkDir(ctx, t, ds, skiaDir, "include", nil, []string{"private"}, nil)
-	skiaPrivateDir := checkDir(ctx, t, ds, skiaIncludeDir, "private", []string{"SkSafe32.h"}, nil, nil)
-	_, isExecutable, err = getDigest(skiaPrivateDir, "SkSafe32.h")
-	if err != nil || isExecutable {
-		t.Errorf("SkSafe32 is executable: %t %v; want: false, nil", isExecutable, err)
-	}
-}
-
-func TestBuildDuplicateError(t *testing.T) {
-	for _, tc := range []struct {
-		desc string
-		ents []Entry
-	}{
-		{
-			desc: "dup file-file",
-			ents: []Entry{
-				{
-					Name: "dir/file1",
-					Data: digest.Bytes("file1.1", []byte("file1.1")),
-				},
-				{
-					Name: "dir/file1",
-					Data: digest.Bytes("file1.2", []byte("file1.2")),
-				},
-			},
-		},
-		{
-			desc: "dup file-symlink",
-			ents: []Entry{
-				{
-					Name: "dir/foo",
-					Data: digest.Bytes("foo file", []byte("foo file")),
-				},
-				{
-					Name:   "dir/foo",
-					Target: "bar",
-				},
-			},
-		},
-		{
-			desc: "dup file-dir",
-			ents: []Entry{
-				{
-					Name: "dir/foo",
-					Data: digest.Bytes("foo file", []byte("foo file")),
-				},
-				{
-					Name: "dir/foo",
-				},
-			},
-		},
-		{
-			desc: "dup symlink-dir",
-			ents: []Entry{
-				{
-					Name:   "dir/foo",
-					Target: "bar",
-				},
-				{
-					Name: "dir/foo",
-				},
-			},
-		},
-	} {
-		t.Run(tc.desc, func(t *testing.T) {
-			ctx := context.Background()
-			ds := digest.NewStore()
-			mt := New(posixpath.FilePath{}, "/path/to/root", ds)
-			for _, ent := range tc.ents {
-				err := mt.Set(ent)
-				if err != nil {
-					t.Fatalf("mt.Set(%q)=%v; want=nil", ent.Name, err)
-				}
-			}
-			d, err := mt.Build(ctx)
-			if err == nil {
-				t.Errorf("mt.Build()=%v, nil, want=error", d)
-			}
-		})
-	}
-}
-
-func checkDir(ctx context.Context, t *testing.T, ds *digest.Store, pdir *rpb.Directory, name string,
-	wantFiles []string, wantDirs []string, wantSymlinks []string) *rpb.Directory {
-	t.Helper()
-	t.Logf("check %s", name)
-	dir := pdir
-	if name != "" {
-		d, _, err := getDigest(pdir, name)
-		if err != nil {
-			t.Fatalf("getDigest(pdir, %q)=_, _, %v; want nil err", name, err)
-		}
-		dir, err = openDir(ctx, ds, d)
-		if err != nil {
-			t.Fatalf("openDir(ds, %v)=_, %v; want nil err", d, err)
-		}
-	}
-	t.Logf("dir: %s", dir)
-	files, dirs, symlinks := readDir(dir)
-	if !reflect.DeepEqual(files, wantFiles) {
-		t.Errorf("files=%q; want=%q", files, wantFiles)
-	}
-	if !reflect.DeepEqual(dirs, wantDirs) {
-		t.Errorf("dirs=%q; want=%q", dirs, wantDirs)
-	}
-	if !reflect.DeepEqual(symlinks, wantSymlinks) {
-		t.Errorf("symlinks=%q; want=%q", symlinks, wantSymlinks)
-	}
-	return dir
-}
-
-func readDir(dir *rpb.Directory) (files, dirs, symlinks []string) {
-	for _, e := range dir.Files {
-		files = append(files, e.Name)
-	}
-	for _, e := range dir.Directories {
-		dirs = append(dirs, e.Name)
-	}
-	for _, e := range dir.Symlinks {
-		symlinks = append(symlinks, e.Name)
-	}
-	return files, dirs, symlinks
-}
-
-// Given a directory `dir` and an entry `name`, returns:
-// - Digest of entry within `dir` with name=`name`. For symlinks, returns empty digest.
-// - Whether it is executable. For symlinks, returns false even if the symlink's target can be executable.
-func getDigest(dir *rpb.Directory, name string) (*rpb.Digest, bool, error) {
-	for _, e := range dir.Files {
-		if e.Name == name {
-			return e.Digest, e.IsExecutable, nil
-		}
-	}
-	for _, e := range dir.Symlinks {
-		if e.Name == name {
-			return &rpb.Digest{}, false, nil
-		}
-	}
-	for _, e := range dir.Directories {
-		if e.Name == name {
-			return e.Digest, false, nil
-		}
-	}
-	return nil, false, errors.New("not found")
-}
-
-func openDir(ctx context.Context, ds *digest.Store, d *rpb.Digest) (*rpb.Directory, error) {
-	data, ok := ds.Get(d)
-	if !ok {
-		return nil, fmt.Errorf("%v not found", d)
-	}
-	dir := &rpb.Directory{}
-	err := datasource.ReadProto(ctx, data, dir)
-	return dir, err
-}
-
-func BenchmarkSetDir(b *testing.B) {
-	ds := digest.NewStore()
-	m := New(posixpath.FilePath{}, "/", ds)
-	cur := dirstate{name: ".", dir: m.root}
-	b.ResetTimer()
-	for i := 0; i < b.N; i++ {
-		m.setDir(cur, "subdir")
-	}
-}
diff --git a/remoteexec/nsjail.cfg b/remoteexec/nsjail.cfg
deleted file mode 100644
index cc81a48..0000000
--- a/remoteexec/nsjail.cfg
+++ /dev/null
@@ -1,109 +0,0 @@
-name: "hardening by nsjail (seccomp-bpf)"
-mode: ONCE
-# keep_env = true
-mount_proc: true
-# it runs in docker container, so ok to mount / as RO.
-mount <
- src: "/"
- dst: "/"
- is_bind: true
- rw: false
- is_dir: true
->
-mount <
- dst: "/tmp"
- fstype: "tmpfs"
- options: "size=5000000"
- rw: true
- is_dir: true
->
-# input root is per request, so ok to mount it as RW.
-# (does not affect to other requests).
-mount <
- prefix_src_env: "INPUT_ROOT"
- src: ""
- prefix_dst_env: "INPUT_ROOT"
- dst: ""
- is_bind: true
- rw: true
- is_dir: true
->
-# default may fail with "File too large"
-rlimit_fsize_type: INF
-rlimit_as_type: INF
-# syscalls used by clang.
-seccomp_string: "ALLOW {"
-seccomp_string: "  access,"
-seccomp_string: "  alarm,"
-seccomp_string: "  arch_prctl,"
-seccomp_string: "  brk,"
-seccomp_string: "  chdir,"
-seccomp_string: "  clone,"
-seccomp_string: "  close,"
-seccomp_string: "  connect,"
-seccomp_string: "  dup,"
-seccomp_string: "  dup2,"
-seccomp_string: "  epoll_create1,"
-seccomp_string: "  execve,"
-seccomp_string: "  exit_group,"
-seccomp_string: "  fcntl,"
-seccomp_string: "  fstatfs,"
-seccomp_string: "  futex,"
-seccomp_string: "  getcwd,"
-seccomp_string: "  getdents,"
-seccomp_string: "  getdents64,"
-seccomp_string: "  getegid,"
-seccomp_string: "  geteuid,"
-seccomp_string: "  getgid,"
-seccomp_string: "  getpeername,"
-seccomp_string: "  getpgrp,"
-seccomp_string: "  getpid,"
-seccomp_string: "  getppid,"
-seccomp_string: "  getrandom,"
-seccomp_string: "  getrlimit,"
-seccomp_string: "  gettid,"
-seccomp_string: "  getuid,"
-seccomp_string: "  ioctl,"
-seccomp_string: "  lseek,"
-seccomp_string: "  mkdir,"
-seccomp_string: "  mmap,"
-seccomp_string: "  mprotect,"
-seccomp_string: "  mremap,"
-seccomp_string: "  munmap,"
-seccomp_string: "  nanosleep,"
-seccomp_string: "  newfstat,"
-seccomp_string: "  newfstatat,"
-seccomp_string: "  newlstat,"
-seccomp_string: "  newstat,"
-seccomp_string: "  newuname,"
-seccomp_string: "  open,"
-seccomp_string: "  openat,"
-seccomp_string: "  pipe,"
-seccomp_string: "  pipe2,"
-seccomp_string: "  pread64,"
-seccomp_string: "  prlimit64,"
-seccomp_string: "  read,"
-seccomp_string: "  readlink,"
-seccomp_string: "  readlinkat,"
-seccomp_string: "  rename,"
-seccomp_string: "  SYSCALL[334]," # rseq, waiting https://github.com/google/kafel/pull/26
-seccomp_string: "  rt_sigaction,"
-seccomp_string: "  rt_sigprocmask,"
-seccomp_string: "  rt_sigreturn,"
-seccomp_string: "  sched_getaffinity,"
-seccomp_string: "  sched_yield,"
-seccomp_string: "  set_robust_list,"
-seccomp_string: "  set_tid_address,"
-seccomp_string: "  sigaltstack,"
-seccomp_string: "  socket,"
-seccomp_string: "  sysinfo,"
-seccomp_string: "  tgkill,"
-seccomp_string: "  unlink,"
-seccomp_string: "  vfork,"
-seccomp_string: "  wait4,"
-seccomp_string: "  write,"
-seccomp_string: "  writev"
-seccomp_string: "}"
-seccomp_string: "DEFAULT KILL_PROCESS"
-#seccomp_log: true
-iface_no_lo: true
diff --git a/remoteexec/nsjail.go b/remoteexec/nsjail.go
deleted file mode 100644
index 4a1206d..0000000
--- a/remoteexec/nsjail.go
+++ /dev/null
@@ -1,126 +0,0 @@
-// Copyright 2019 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.
-
-package remoteexec
-
-import (
-	_ "embed"
-	"fmt"
-	"sort"
-	"strings"
-
-	"google.golang.org/protobuf/encoding/prototext"
-	"google.golang.org/protobuf/proto"
-
-	gomapb "go.chromium.org/goma/server/proto/api"
-	nsjailpb "go.chromium.org/goma/server/proto/nsjail"
-)
-
-var (
-	//go:embed nsjail.cfg
-	nsjailHardeningConfig []byte
-
-	//go:embed nsjail_run.sh
-	nsjailHardeningWrapperScript []byte
-
-	//go:embed nsjail_chroot_run.sh
-	nsjailChrootRunWrapperScript []byte
-)
-
-var seccompString []string
-
-func init() {
-	m := &nsjailpb.NsJailConfig{}
-	err := prototext.Unmarshal(nsjailHardeningConfig, m)
-	if err != nil {
-		panic(fmt.Errorf("bad nsjailHardeningConfig: %v", err))
-	}
-	seccompString = m.SeccompString
-}
-
-// pathFromToolchainSpec returns ':'-joined directories of paths in toolchain spec.
-// Since symlinks may point to executables, having directories with executables
-// may not work, but it is a bit cumbersome to analyze symlinks.
-// Also, having library directories in PATH should be harmless because
-// the Goma client may not include multiple subprograms with the same name.
-func pathFromToolchainSpec(cfp clientFilePath, ts []*gomapb.ToolchainSpec) string {
-	m := make(map[string]bool)
-	for _, e := range ts {
-		m[cfp.Dir(e.GetPath())] = true
-	}
-	var r []string
-	for k := range m {
-		if k == "" || k == "." {
-			continue
-		}
-		r = append(r, k)
-	}
-	// This function must return the same result for the same input, but go
-	// does not guarantee the iteration order.
-	sort.Strings(r)
-	return strings.Join(r, ":")
-}
-
-// nsjailConfig returns nsjail configuration.
-// When you modify followings, please make sure it matches
-// nsjailChrootRunWrapperScript above.
-func nsjailChrootConfig(cwd string, cfp clientFilePath, ts []*gomapb.ToolchainSpec, envs []string) []byte {
-	chrootWorkdir := "/tmp/goma_chroot"
-	cfg := &nsjailpb.NsJailConfig{
-		Uidmap: []*nsjailpb.IdMap{
-			{
-				InsideId:  proto.String("nobody"),
-				OutsideId: proto.String("nobody"),
-			},
-		},
-		Gidmap: []*nsjailpb.IdMap{
-			{
-				InsideId:  proto.String("nogroup"),
-				OutsideId: proto.String("nogroup"),
-			},
-		},
-		Mount: []*nsjailpb.MountPt{
-			{
-				Src:    proto.String(chrootWorkdir),
-				Dst:    proto.String("/"),
-				IsBind: proto.Bool(true),
-				Rw:     proto.Bool(true),
-				IsDir:  proto.Bool(true),
-			},
-			{
-				Src:    proto.String("/dev/null"),
-				Dst:    proto.String("/dev/null"),
-				Rw:     proto.Bool(true),
-				IsBind: proto.Bool(true),
-			},
-			{
-				Src:    proto.String("/dev/urandom"),
-				Dst:    proto.String("/dev/urandom"),
-				IsBind: proto.Bool(true),
-			},
-		},
-		Cwd: proto.String(cwd),
-		// TODO: use log file and print to server log.
-		LogLevel:  nsjailpb.LogLevel_WARNING.Enum(),
-		MountProc: proto.Bool(true),
-		Envar: append(
-			[]string{
-				"PATH=" + pathFromToolchainSpec(cfp, ts),
-				// Dummy home directory is needed by pnacl-clang to
-				// import site.py to import user-defined python
-				// packages.
-				"HOME=/",
-			},
-			// Add client-side environemnt to execution environment.
-			envs...),
-		RlimitAsType:    nsjailpb.RLimit_INF.Enum(),
-		RlimitFsizeType: nsjailpb.RLimit_INF.Enum(),
-		// TODO: relax RLimit from the default.
-		// Default size might be too strict, and not suitable for
-		// compiling.
-
-		SeccompString: seccompString,
-	}
-	return []byte(prototext.Format(cfg))
-}
diff --git a/remoteexec/nsjail_chroot_run.sh b/remoteexec/nsjail_chroot_run.sh
deleted file mode 100755
index 218975b..0000000
--- a/remoteexec/nsjail_chroot_run.sh
+++ /dev/null
@@ -1,45 +0,0 @@
-#!/bin/bash
-set -e
-
-if [[ "$WORK_DIR" == "" ]]; then
-  echo "ERROR: WORK_DIR is not set" >&2
-  exit 1
-fi
-
-rundir="$(pwd)"
-chroot_workdir="/tmp/goma_chroot"
-
-#
-# mount directories under $chroot_workdir and execute.
-#
-run_dirs=($(ls -1 "$rundir"))
-sys_dirs=(dev proc)
-
-# RBE server generates __action_home__XXXXXXXXXX directory in $rundir
-# (note: XXXXXXXXXX is a random).  Let's skip it because we do not use that.
-# mount directories in the request.
-for d in "${run_dirs[@]}"; do
-  if [[ "$d" == __action_home__* ]]; then
-    continue
-  fi
-  mkdir -p "$chroot_workdir/$d"
-  mount --bind "$rundir/$d" "$chroot_workdir/$d"
-done
-
-# mount directories not included in the request.
-for d in "${sys_dirs[@]}"; do
-  # avoid to mount system directories if that exist in the user's request.
-  if [[ -d "$rundir/$d" ]]; then
-    continue
-  fi
-  # directory will be mounted by nsjail later.
-  mkdir -p "$chroot_workdir/$d"
-done
-# needed to make nsjail bind device files.
-touch "$chroot_workdir/dev/urandom"
-touch "$chroot_workdir/dev/null"
-
-# currently running with root. run the command with nobody:nogroup with chroot.
-# We use nsjail to chdir without running bash script inside chroot, and
-# libc inside chroot can be different from libc outside.
-nsjail --quiet --config "$WORK_DIR/nsjail.cfg" -- "$@"
diff --git a/remoteexec/nsjail_run.sh b/remoteexec/nsjail_run.sh
deleted file mode 100755
index 283a55d..0000000
--- a/remoteexec/nsjail_run.sh
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/bin/bash
-export INPUT_ROOT="$(pwd)"
-if [[ "$WORK_DIR" != "" ]]; then
-  cd "${WORK_DIR}"
-fi
-export PWD="$(pwd)"
-# exit 159 -> seccomp violation
-nsjail -q -C "./nsjail.cfg" --cwd "$PWD" \
-       --  \
-       "$@"
diff --git a/remoteexec/nsjail_test.go b/remoteexec/nsjail_test.go
deleted file mode 100644
index 891635f..0000000
--- a/remoteexec/nsjail_test.go
+++ /dev/null
@@ -1,143 +0,0 @@
-// Copyright 2019 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.
-
-package remoteexec
-
-import (
-	"testing"
-
-	"google.golang.org/protobuf/encoding/prototext"
-	"google.golang.org/protobuf/proto"
-
-	"go.chromium.org/goma/server/command/descriptor/posixpath"
-	gomapb "go.chromium.org/goma/server/proto/api"
-	nsjailpb "go.chromium.org/goma/server/proto/nsjail"
-)
-
-func TestPathFromToolchainSpec(t *testing.T) {
-	for _, tc := range []struct {
-		desc string
-		cfp  clientFilePath
-		ts   []*gomapb.ToolchainSpec
-		want string
-	}{
-		{
-			desc: "empty",
-			cfp:  posixpath.FilePath{},
-			ts:   nil,
-			want: "",
-		},
-		{
-			desc: "one toolchain spec",
-			cfp:  posixpath.FilePath{},
-			ts: []*gomapb.ToolchainSpec{
-				{
-					Path:         proto.String("/usr/bin/clang"),
-					Hash:         proto.String("fe4c1bb3b68376901c9f9e87dc1196a81f598eb854061ddfc5f85ef7e054feed"),
-					Size:         proto.Int64(86003704),
-					IsExecutable: proto.Bool(true),
-				},
-			},
-			want: "/usr/bin",
-		},
-		{
-			desc: "toolchain spec in the same directory",
-			cfp:  posixpath.FilePath{},
-			ts: []*gomapb.ToolchainSpec{
-				{
-					Path:         proto.String("/usr/bin/clang"),
-					Hash:         proto.String("fe4c1bb3b68376901c9f9e87dc1196a81f598eb854061ddfc5f85ef7e054feed"),
-					Size:         proto.Int64(86003704),
-					IsExecutable: proto.Bool(true),
-				},
-				{
-					Path:        proto.String("/usr/bin/clang++"),
-					SymlinkPath: proto.String("clang"),
-				},
-			},
-			want: "/usr/bin",
-		},
-		{
-			desc: "toolchain spec in the different directory",
-			cfp:  posixpath.FilePath{},
-			ts: []*gomapb.ToolchainSpec{
-				{
-					Path:         proto.String("/usr/bin/clang"),
-					Hash:         proto.String("fe4c1bb3b68376901c9f9e87dc1196a81f598eb854061ddfc5f85ef7e054feed"),
-					Size:         proto.Int64(86003704),
-					IsExecutable: proto.Bool(true),
-				},
-				{
-					Path:         proto.String("/bin/bash"),
-					Hash:         proto.String("d80e7ffe8836eb30bafb138def4c1a6e3f586d98ec39a26d9549a92141e95281"),
-					Size:         proto.Int64(715328),
-					IsExecutable: proto.Bool(true),
-				},
-			},
-			want: "/bin:/usr/bin",
-		},
-		{
-			desc: "complexed",
-			cfp:  posixpath.FilePath{},
-			ts: []*gomapb.ToolchainSpec{
-				{
-					Path:         proto.String("../../native_client/toolchain/linux_x86/pnacl_newlib/bin/pnacl-clang++"),
-					Hash:         proto.String("dummy"),
-					Size:         proto.Int64(0),
-					IsExecutable: proto.Bool(true),
-				},
-				{
-					Path:         proto.String("/home/goma/chrome_root/src/native_client/toolchain/linux_x86/pnacl_newlib/bin/clang"),
-					Hash:         proto.String("dummy"),
-					Size:         proto.Int64(0),
-					IsExecutable: proto.Bool(true),
-				},
-				{
-					// lib directory is also included.
-					Path: proto.String("/usr/lib64/python2.7/abc.py"),
-					Hash: proto.String("dummy"),
-					Size: proto.Int64(0),
-				},
-				{
-					Path:        proto.String("/usr/bin/python"),
-					SymlinkPath: proto.String("python-wrapper"),
-				},
-				{
-					Path:         proto.String("/usr/bin/python-wrapper"),
-					Hash:         proto.String("dummy"),
-					Size:         proto.Int64(0),
-					IsExecutable: proto.Bool(true),
-				},
-				{
-					Path:         proto.String("/usr/bin/bash"),
-					Hash:         proto.String("dummy"),
-					Size:         proto.Int64(0),
-					IsExecutable: proto.Bool(true),
-				},
-				{
-					Path:         proto.String("/lib64/libc-2.27.so"),
-					Hash:         proto.String("dummy"),
-					Size:         proto.Int64(0),
-					IsExecutable: proto.Bool(true),
-				},
-			},
-			want: "../../native_client/toolchain/linux_x86/pnacl_newlib/bin:/home/goma/chrome_root/src/native_client/toolchain/linux_x86/pnacl_newlib/bin:/lib64:/usr/bin:/usr/lib64/python2.7",
-		},
-	} {
-		t.Run(tc.desc, func(t *testing.T) {
-			actual := pathFromToolchainSpec(tc.cfp, tc.ts)
-			if actual != tc.want {
-				t.Errorf("pathFromToolchainSpec(%v, %v) = %q; want %q", tc.cfp, tc.ts, actual, tc.want)
-			}
-		})
-	}
-}
-
-func TestNsjailHardeningConfig(t *testing.T) {
-	cfg := &nsjailpb.NsJailConfig{}
-	err := prototext.Unmarshal(nsjailHardeningConfig, cfg)
-	if err != nil {
-		t.Errorf("unmarshal\n%s\n => %v", nsjailHardeningConfig, err)
-	}
-}
diff --git a/remoteexec/run.sh b/remoteexec/run.sh
deleted file mode 100755
index 3f2e8e3..0000000
--- a/remoteexec/run.sh
+++ /dev/null
@@ -1,6 +0,0 @@
-#!/bin/bash
-set -e
-if [[ "$WORK_DIR" != "" ]]; then
-  cd "${WORK_DIR}"
-fi
-exec "$@"
diff --git a/remoteexec/stats.go b/remoteexec/stats.go
deleted file mode 100644
index 8666c4a..0000000
--- a/remoteexec/stats.go
+++ /dev/null
@@ -1,220 +0,0 @@
-// Copyright 2019 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.
-
-package remoteexec
-
-import (
-	"context"
-
-	"go.opencensus.io/stats"
-	"go.opencensus.io/stats/view"
-	"go.opencensus.io/tag"
-)
-
-var (
-	numRunningOperations = stats.Int64(
-		"go.chromium.org/goma/server/remoteexec.running-operations",
-		"Number of current running exec operations",
-		stats.UnitDimensionless)
-
-	wrapperCount = stats.Int64(
-		"go.chromium.org/goma/server/remoteexec.wrapper-counts",
-		"Number of requests per wrapper types",
-		stats.UnitDimensionless)
-
-	wrapperTypeKey = tag.MustNewKey("wrapper")
-
-	unknownFlagCount = stats.Int64(
-		"go.chromium.org/goma/server/remoteexec.unknown-flags",
-		"Number of requests with unknown flags",
-		stats.UnitDimensionless)
-	compilerNameKey = tag.MustNewKey("compiler")
-
-	inputBufferAllocSize = stats.Int64(
-		"go.chromium.org/goma/server/remoteexec.input-buffer-alloc",
-		"Size to allocate buffer for input files",
-		stats.UnitBytes)
-
-	allocStatusKey = tag.MustNewKey("status")
-
-	execInventoryTime = stats.Float64(
-		"go.chromium.org/goma/server/remoteexec.exec-inventory",
-		"Time in inventory check",
-		stats.UnitMilliseconds)
-	execInputTreeTime = stats.Float64(
-		"go.chromium.org/goma/server/remoteexec.exec-input-tree",
-		"Time in input tree construction",
-		stats.UnitMilliseconds)
-	execSetupTime = stats.Float64(
-		"go.chromium.org/goma/server/remoteexec.exec-setup",
-		"Time in setup",
-		stats.UnitMilliseconds)
-	execCheckCacheTime = stats.Float64(
-		"go.chromium.org/goma/server/remoteexec.exec-check-cache",
-		"Time to check cache",
-		stats.UnitMilliseconds)
-	execCheckMissingTime = stats.Float64(
-		"go.chromium.org/goma/server/remoteexec.exec-check-missing",
-		"Time to check missing",
-		stats.UnitMilliseconds)
-	execUploadBlobsTime = stats.Float64(
-		"go.chromium.org/goma/server/remoteexec.exec-upload-blobs",
-		"Time to upload blobs",
-		stats.UnitMilliseconds)
-	execExecuteTime = stats.Float64(
-		"go.chromium.org/goma/server/remoteexec.exec-execute",
-		"Time to execute",
-		stats.UnitMilliseconds)
-	execResponseTime = stats.Float64(
-		"go.chromium.org/goma/server/remoteexec.exec-response",
-		"Time in response",
-		stats.UnitMilliseconds)
-
-	rbeQueueTime = stats.Float64(
-		"go.chromium.org/goma/server/remoteexec.rbe-queue",
-		"Time in RBE queue",
-		stats.UnitMilliseconds)
-	rbeWorkerTime = stats.Float64(
-		"go.chromium.org/goma/server/remoteexec.rbe-worker",
-		"Time in RBE worker",
-		stats.UnitMilliseconds)
-	rbeInputTime = stats.Float64(
-		"go.chromium.org/goma/server/remoteexec.rbe-input",
-		"Time in RBE input",
-		stats.UnitMilliseconds)
-	rbeExecTime = stats.Float64(
-		"go.chromium.org/goma/server/remoteexec.rbe-exec",
-		"TIme in RBE exec",
-		stats.UnitMilliseconds)
-	rbeOutputTime = stats.Float64(
-		"go.chromium.org/goma/server/remoteexec.rbe-output",
-		"Time in RBE output",
-		stats.UnitMilliseconds)
-
-	rbeExitKey                  = tag.MustNewKey("exit")
-	rbeCacheKey                 = tag.MustNewKey("cache")
-	rbePlatformOSFamilyKey      = tag.MustNewKey("os-family")
-	rbePlatformDockerRuntimeKey = tag.MustNewKey("docker-runtime")
-	rbeCrossKey                 = tag.MustNewKey("cross")
-	// wrapper?
-
-	rbeTagKeys = []tag.Key{
-		rbeExitKey,
-		rbeCacheKey,
-		rbePlatformOSFamilyKey,
-		rbePlatformDockerRuntimeKey,
-		rbeCrossKey,
-	}
-
-	defaultLatencyDistribution = view.Distribution(1, 2, 3, 4, 5, 6, 8, 10, 13, 16, 20, 25, 30, 40, 50, 65, 80, 100, 130, 160, 200, 250, 300, 400, 500, 650, 800, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000)
-
-	DefaultViews = []*view.View{
-		{
-			Description: `Number of current running exec operations`,
-			Measure:     numRunningOperations,
-			Aggregation: view.Sum(),
-		},
-		{
-			Description: "Number of requests per wrapper types",
-			TagKeys: []tag.Key{
-				wrapperTypeKey,
-			},
-			Measure:     wrapperCount,
-			Aggregation: view.Count(),
-		},
-		{
-			Measure: unknownFlagCount,
-			TagKeys: []tag.Key{
-				compilerNameKey,
-			},
-			Aggregation: view.Count(),
-		},
-		{
-			Description: "Size to allocate buffer for input files",
-			TagKeys: []tag.Key{
-				allocStatusKey,
-			},
-			Measure:     inputBufferAllocSize,
-			Aggregation: view.Sum(),
-		},
-		{
-			Description: "Time in inventory check",
-			Measure:     execInventoryTime,
-			Aggregation: defaultLatencyDistribution,
-		},
-		{
-			Description: "Time in input tree construction",
-			Measure:     execInputTreeTime,
-			Aggregation: defaultLatencyDistribution,
-		},
-		{
-			Description: "Time in setup",
-			Measure:     execSetupTime,
-			Aggregation: defaultLatencyDistribution,
-		},
-		{
-			Description: "Time to check cache",
-			Measure:     execCheckCacheTime,
-			Aggregation: defaultLatencyDistribution,
-		},
-		{
-			Description: "Time to check missing",
-			Measure:     execCheckMissingTime,
-			Aggregation: defaultLatencyDistribution,
-		},
-		{
-			Description: "Time to upload blobs",
-			Measure:     execUploadBlobsTime,
-			Aggregation: defaultLatencyDistribution,
-		},
-		{
-			Description: "Time to execute",
-			Measure:     execExecuteTime,
-			Aggregation: defaultLatencyDistribution,
-		},
-		{
-			Description: "Time in response",
-			Measure:     execResponseTime,
-			Aggregation: defaultLatencyDistribution,
-		},
-		{
-			Description: "Time in RBE queue",
-			Measure:     rbeQueueTime,
-			TagKeys:     rbeTagKeys,
-			Aggregation: defaultLatencyDistribution,
-		},
-		{
-			Description: "Time in RBE worker",
-			Measure:     rbeWorkerTime,
-			TagKeys:     rbeTagKeys,
-			Aggregation: defaultLatencyDistribution,
-		},
-		{
-			Description: "Time in RBE input",
-			Measure:     rbeInputTime,
-			TagKeys:     rbeTagKeys,
-			Aggregation: defaultLatencyDistribution,
-		},
-		{
-			Description: "Time in RBE exec",
-			Measure:     rbeExecTime,
-			TagKeys:     rbeTagKeys,
-			Aggregation: defaultLatencyDistribution,
-		},
-		{
-			Description: "Time in RBE output",
-			Measure:     rbeOutputTime,
-			TagKeys:     rbeTagKeys,
-			Aggregation: defaultLatencyDistribution,
-		},
-	}
-)
-
-func recordRemoteExecStart(ctx context.Context) {
-	stats.Record(ctx, numRunningOperations.M(1))
-}
-
-func recordRemoteExecFinish(ctx context.Context) {
-	stats.Record(ctx, numRunningOperations.M(-1))
-}
diff --git a/rpc/client.go b/rpc/client.go
deleted file mode 100644
index df63632..0000000
--- a/rpc/client.go
+++ /dev/null
@@ -1,313 +0,0 @@
-// Copyright 2017 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.
-
-package rpc
-
-import (
-	"context"
-	"fmt"
-	"net"
-	"sort"
-	"sync"
-	"time"
-
-	"github.com/golang/groupcache/consistenthash"
-	"go.opencensus.io/trace"
-	"google.golang.org/grpc"
-	"google.golang.org/grpc/codes"
-
-	"go.chromium.org/goma/server/log"
-)
-
-const (
-	lookupInterval = 3 * time.Second
-)
-
-// backend represents each backend per IP.
-type backend struct {
-	addr string
-
-	mu     sync.Mutex
-	cc     *grpc.ClientConn
-	client interface{} // proto's client interface
-	nreq   int64
-	nerr   int64
-	load   int
-	err    error
-}
-
-func (b *backend) init(ctx context.Context, target string, newc func(*grpc.ClientConn) interface{}, dialOpts []grpc.DialOption) error {
-	span := trace.FromContext(ctx)
-	span.AddAttributes(
-		trace.StringAttribute("target", target),
-		trace.StringAttribute("addr", b.addr),
-	)
-	logger := log.FromContext(ctx)
-
-	b.mu.Lock()
-	defer b.mu.Unlock()
-	if b.client != nil {
-		return nil
-	}
-	logger.Infof("dial %s (for %s)", b.addr, target)
-	cc, err := grpc.DialContext(ctx, b.addr, dialOpts...)
-	if err != nil {
-		logger.Errorf("dial %s (for %s) ... %v", b.addr, target, err)
-		b.err = err
-		return err
-	}
-	b.cc = cc
-	b.client = newc(cc)
-	return nil
-}
-
-func (b *backend) use() {
-	b.mu.Lock()
-	defer b.mu.Unlock()
-	b.load++
-	b.nreq++
-}
-
-func (b *backend) done(ctx context.Context, err error) {
-	logger := log.FromContext(ctx)
-
-	b.mu.Lock()
-	defer b.mu.Unlock()
-	b.load--
-	if err != nil {
-		b.nerr++
-		logger.Warnf("backend error %s: %v", b.addr, err)
-	}
-}
-
-func (b *backend) close() {
-	if b == nil {
-		return
-	}
-	if b.cc == nil {
-		return
-	}
-	b.cc.Close()
-	b.cc = nil
-	b.client = nil
-}
-
-// Client represents load-balancing client.
-type Client struct {
-	target   string
-	newc     func(cc *grpc.ClientConn) interface{}
-	dialOpts []grpc.DialOption
-
-	closech chan chan bool
-
-	mu        sync.RWMutex
-	timestamp time.Time
-	addrs     []string
-	backends  map[string]*backend
-	shards    *consistenthash.Map
-}
-
-var (
-	clientsMu sync.Mutex
-	clients   []*Client
-)
-
-// Option configures Client.
-type Option func(*Client)
-
-// DialOption returns an Option to configure dial option.
-func DialOption(opt grpc.DialOption) Option {
-	return func(c *Client) {
-		c.dialOpts = append(c.dialOpts, opt)
-	}
-}
-
-// DialOptions converts grpc.DialOptions to Options.
-func DialOptions(opts ...grpc.DialOption) []Option {
-	var dopts []Option
-	for _, opt := range opts {
-		dopts = append(dopts, DialOption(opt))
-	}
-	return dopts
-}
-
-// NewClient creates new load-balancing client.
-// newc should return grpc client interface for given *grpc.ClientConn.
-// target is <hostname>:<port>.
-// TODO: support grpc new naming?
-func NewClient(ctx context.Context, target string, newc func(cc *grpc.ClientConn) interface{}, opts ...Option) *Client {
-	client := &Client{
-		target:  target,
-		newc:    newc,
-		closech: make(chan chan bool),
-	}
-	for _, opt := range opts {
-		opt(client)
-	}
-	logger := log.FromContext(ctx)
-	client.update(ctx)
-	go func() {
-		ctx := context.Background()
-		logger := log.FromContext(ctx)
-		for {
-			select {
-			case ch := <-client.closech:
-				close(ch)
-				logger.Debugf("finish client update for %s", target)
-				return
-			case <-time.After(lookupInterval):
-			}
-			logger := log.FromContext(ctx)
-			if err := client.update(ctx); err != nil {
-				logger.Errorf("rpc.Client.update(%s)=%v", target, err)
-			}
-		}
-	}()
-	clientsMu.Lock()
-	clients = append(clients, client)
-	logger.Debugf("add client %s", target)
-	clientsMu.Unlock()
-	return client
-}
-
-func (c *Client) update(ctx context.Context) error {
-	span := trace.FromContext(ctx)
-	span.AddAttributes(trace.StringAttribute("update.target", c.target))
-	logger := log.FromContext(ctx)
-	c.mu.Lock()
-	defer c.mu.Unlock()
-	if time.Now().Before(c.timestamp.Add(lookupInterval)) {
-		return nil
-	}
-	logger.Debugf("lookup %s", c.target)
-	host, port, err := net.SplitHostPort(c.target)
-	if err != nil {
-		logger.Errorf("lookup %s ... %v", c.target, err)
-	}
-	addrs, err := net.LookupHost(host)
-	if err != nil {
-		logger.Errorf("lookup %s ... %v", host, err)
-		return err
-	}
-	c.timestamp = time.Now()
-
-	c.addrs = make([]string, len(addrs))
-	copy(c.addrs, addrs)
-	sort.Strings(c.addrs)
-
-	m := make(map[string]bool)
-	for addr := range c.backends {
-		m[addr] = true
-	}
-	// creates new backends from addrs
-	backends := make(map[string]*backend)
-	for _, addr := range addrs {
-		b := &backend{
-			addr: net.JoinHostPort(addr, port),
-		}
-		ob := c.backends[addr]
-		if ob != nil {
-			delete(m, addr)
-			ob.mu.Lock()
-			if ob.err == nil {
-				b = ob
-			} else if ob.cc != nil {
-				ob.close()
-			}
-			ob.mu.Unlock()
-		} else {
-			m[addr] = true
-		}
-		backends[addr] = b
-	}
-	if len(m) == 0 {
-		logger.Debug("address not changed")
-		return nil
-	}
-	logger.Debugf("address updated: %v", m)
-
-	for addr, ob := range c.backends {
-		if _, ok := backends[addr]; !ok {
-			ob.close()
-		}
-	}
-
-	c.backends = backends
-	c.shards = consistenthash.New(len(addrs), nil)
-	c.shards.Add(addrs...)
-	return nil
-}
-
-// Shard picks one backend from client's target to request the key, and returns
-// selected backend.
-func (c *Client) Shard(ctx context.Context, key interface{}) (*backend, error) {
-	span := trace.FromContext(ctx)
-	logger := log.FromContext(ctx)
-
-	err := c.update(ctx)
-	if err != nil {
-		span.SetStatus(trace.Status{
-			Code:    int32(codes.Aborted),
-			Message: fmt.Sprintf("lookup failed: %v", err),
-		})
-		logger.Errorf("lookup failed: %v", err)
-		return nil, grpc.Errorf(codes.Aborted, "rpc: %s lookup failed: %v", c.target, err)
-	}
-	c.mu.RLock()
-	defer c.mu.RUnlock()
-	keystr, _ := key.(string)
-	addr := c.shards.Get(keystr)
-	if addr == "" {
-		span.SetStatus(trace.Status{
-			Code:    int32(codes.Aborted),
-			Message: "no backend",
-		})
-		logger.Errorf("no backend")
-		return nil, grpc.Errorf(codes.Aborted, "rpc: %s no backends available", c.target)
-	}
-	span.Annotatef(nil, "shard %s (for %s) from %d", addr, key, len(c.backends))
-	logger.Debugf("shard %s (for %s) from %d", addr, key, len(c.backends))
-	b := c.backends[addr]
-	err = b.init(ctx, c.target, c.newc, c.dialOpts)
-	if err != nil {
-		return nil, err
-	}
-	b.use()
-	return b, nil
-}
-
-// Call calls new rpc call.
-// picker and key will be used to pick backend.
-// picker will be Client's Pick, or Shard.
-// Pick will use key for backend addr, or empty for least loaded.
-// Shard will use key for sharding.
-// Rand will use key for *RandomState.
-// f is called with grpc client inferface for selected backend.
-func (c *Client) Call(ctx context.Context, picker func(context.Context, interface{}) (*backend, error), key interface{}, f func(interface{}) error) error {
-	logger := log.FromContext(ctx)
-	b, err := picker(ctx, key)
-	if err != nil {
-		return err
-	}
-	err = f(b.client)
-	if err != nil {
-		logger.Debugf("call %s done: %v", b.addr, err)
-	} else {
-		logger.Debugf("call %s done", b.addr)
-	}
-	b.done(ctx, err)
-	return err
-}
-
-func (c *Client) Close() error {
-	ch := make(chan bool)
-	c.closech <- ch
-	<-ch
-	c.mu.Lock()
-	defer c.mu.Unlock()
-	for _, b := range c.backends {
-		b.close()
-	}
-	return nil
-}
diff --git a/rpc/debug.go b/rpc/debug.go
deleted file mode 100644
index 2ddd5ea..0000000
--- a/rpc/debug.go
+++ /dev/null
@@ -1,133 +0,0 @@
-// Copyright 2017 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.
-
-package rpc
-
-import (
-	"context"
-	"html/template"
-	"net/http"
-	"sort"
-	"time"
-
-	"go.chromium.org/goma/server/log"
-)
-
-func init() {
-	http.HandleFunc("/debug/backends", func(w http.ResponseWriter, req *http.Request) {
-		// TODO: auth request as net/trace
-		w.Header().Set("Content-Type", "text/html; charset=utf=8")
-		Render(w, req)
-	})
-}
-
-// Render renders backend information.
-// It is handled on /debug/backends on default http mux.
-func Render(w http.ResponseWriter, req *http.Request) {
-	type backendInfo struct {
-		Addr string
-		Conn bool
-		NReq int64
-		NErr int64
-		Load int
-		Err  error
-	}
-	type clientInfo struct {
-		Target    string
-		Timestamp time.Time
-		Backends  []backendInfo
-	}
-
-	data := struct {
-		Clients []clientInfo
-	}{}
-
-	clientsMu.Lock()
-	for _, client := range clients {
-		client.mu.RLock()
-		ci := clientInfo{
-			Target:    client.target,
-			Timestamp: client.timestamp,
-		}
-		var addrs []string
-		for addr := range client.backends {
-			addrs = append(addrs, addr)
-		}
-		sort.Strings(addrs)
-		for _, addr := range addrs {
-			b := client.backends[addr]
-			b.mu.Lock()
-			bi := backendInfo{
-				Addr: b.addr,
-				Conn: b.cc != nil,
-				NReq: b.nreq,
-				NErr: b.nerr,
-				Load: b.load,
-				Err:  b.err,
-			}
-			ci.Backends = append(ci.Backends, bi)
-			b.mu.Unlock()
-		}
-		client.mu.RUnlock()
-		data.Clients = append(data.Clients, ci)
-	}
-	clientsMu.Unlock()
-
-	err := pageTmpl.ExecuteTemplate(w, "Page", data)
-	if err != nil {
-		logger := log.FromContext(context.Background())
-		logger.Errorf("rpc: Failed executing template: %v", err)
-	}
-}
-
-var pageTmpl = template.Must(template.New("Page").Parse(pageHTML))
-
-const pageHTML = `
-{{template "Prolog" .}}
-{{template "StatusTable" .}}
-{{template "Epilog" .}}
-
-{{define "Prolog"}}
-<html>
- <head>
-  <title>/debug/backends</title>
- </head>
- <body>
-<h1>/debug/backends</h1>
-{{end}}
-
-{{define "StatusTable"}}
-<table border="1">
- <tr>
-  <th>target
-  <td>timestamp
-  <td>backends
-  <td>conn
-  <td># of reqs
-  <td># of err
-  <td>load
-  <td>err
- {{range $c := .Clients}}
- <tr>
-  <td>{{$c.Target}}
-  <td>{{$c.Timestamp}}
-  {{range $b := $c.Backends}}
-  <tr>
-   <td colspan="2">
-   <td>{{$b.Addr}}
-   <td>{{$b.Conn}}
-   <td>{{$b.NReq}}
-   <td>{{$b.NErr}}
-   <td>{{$b.Load}}
-   <td>{{$b.Err}}
-  {{end}}
- {{end}}
-</table>
-{{end}}
-
-{{define "Epilog"}}
- </body>
-</html>
-{{end}}
-`
diff --git a/rpc/doc.go b/rpc/doc.go
deleted file mode 100644
index 8b64d96..0000000
--- a/rpc/doc.go
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright 2017 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.
-
-/*
-Package rpc provides goma specific rpc features on gRPC.
-
-# Load balancing RPC client
-
-gRPC doesn't provide good loadbalancing client (for kubernetes, yet).
-Typical gRPC client could be created as follows:
-
-	conn, err := grpc.Dial(address, grpc.WithInsecure())
-	if err != nil {
-		log.Fatalf("did not connect: %v", err)
-	}
-	defer conn.Close()
-	c := pb.NewGreeterClient(conn)
-
-This package provides load balancing client if address has multiple IP
-addresses:
-
-	type GreeterClient struct {
-		c *rpc.Client
-	}
-
-	func NewGreeterClient(address string, opts ...grpc.DialOption) GreeterClient {
-		return GreeterClient{
-			c: rpc.NewClient(address,
-				func(cc *grpc.ClientConn) interface{} {
-					return pb.NewGreeterClient(cc)
-				}, opts...),
-		}
-	}
-
-	func (c GreeterClient) SayHello(ctx context.Context, in *pb.Req, opts...grpc.CallOption) (*pb.Resp, error) {
-		var resp *pb.Resp
-		var err error
-		err = c.Call(ctx, c.client.Pick, "",
-			func(client interface{}) error {
-				resp, err = client.(GreeterClient).SayHello(ctx, in, opts...)
-				return err
-			},
-		)
-		return resp, err
-	}
-
-	c := NewGreeterClient(address, grpc.WithInsecure())
-
-rpc call would return codes.Unavailable if no backends available for address.
-
-TODO: use grpc's Balancer?
-TODO: use statefulset for sharding?
-*/
-package rpc
diff --git a/rpc/grpctest/grpctest.go b/rpc/grpctest/grpctest.go
deleted file mode 100644
index fe39f59..0000000
--- a/rpc/grpctest/grpctest.go
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2018 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.
-
-/*
-Package grpctest provides a test server for unit tests that use gRPC.
-*/
-package grpctest
-
-import (
-	"fmt"
-	"net"
-
-	"google.golang.org/grpc"
-)
-
-// StartServer instantiates a gRPC server suitable for unit tests, and
-// returns the server address the client can use to connect and a stop
-// function that must be called if err is nil to stop the server and
-// cleanup resources.
-func StartServer(server *grpc.Server) (addr string, stop func(), err error) {
-	lis, err := net.Listen("tcp", ":0")
-	if err != nil {
-		return "", func() {}, fmt.Errorf("error creating TCP listener: %v", err)
-	}
-	addr = lis.Addr().String()
-	go server.Serve(lis)
-	stop = func() {
-		server.Stop()
-		lis.Close()
-	}
-	return addr, stop, nil
-}
diff --git a/rpc/grpctest/grpctest_test.go b/rpc/grpctest/grpctest_test.go
deleted file mode 100644
index eeab798..0000000
--- a/rpc/grpctest/grpctest_test.go
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright 2018 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.
-
-package grpctest_test
-
-import (
-	"context"
-	"fmt"
-	"os"
-
-	"google.golang.org/grpc"
-
-	gomapb "go.chromium.org/goma/server/proto/api"
-	pb "go.chromium.org/goma/server/proto/execlog"
-	"go.chromium.org/goma/server/rpc/grpctest"
-)
-
-// MyServer is fake execlog server.
-type MyServer struct {
-	pb.UnimplementedLogServiceServer
-	Req  *gomapb.SaveLogReq
-	Resp *gomapb.SaveLogResp
-	Err  error
-}
-
-func (s *MyServer) SaveLog(ctx context.Context, req *gomapb.SaveLogReq) (*gomapb.SaveLogResp, error) {
-	s.Req = req
-	return s.Resp, s.Err
-}
-
-func Example() {
-	srv := grpc.NewServer()
-	s := &MyServer{
-		Resp: &gomapb.SaveLogResp{},
-	}
-	pb.RegisterLogServiceServer(srv, s)
-	addr, stop, err := grpctest.StartServer(srv)
-	if err != nil {
-		fmt.Printf("error creating test server: %v\n", err)
-		os.Exit(1)
-	}
-	defer stop()
-
-	conn, err := grpc.Dial(addr, grpc.WithInsecure())
-	if err != nil {
-		fmt.Printf("error connecting to %s: %v\n", addr, err)
-		os.Exit(1)
-	}
-	defer conn.Close()
-	client := pb.NewLogServiceClient(conn)
-	ctx := context.Background()
-	resp, err := client.SaveLog(ctx, &gomapb.SaveLogReq{})
-	if err != nil {
-		fmt.Printf("SaveLog()=%v, %v; want nil error\n", resp, err)
-		os.Exit(1)
-	}
-}
diff --git a/rpc/id.go b/rpc/id.go
deleted file mode 100644
index ae78460..0000000
--- a/rpc/id.go
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2017 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.
-
-package rpc
-
-import (
-	"context"
-	"fmt"
-
-	"go.opencensus.io/tag"
-
-	"go.chromium.org/goma/server/log"
-	gomapb "go.chromium.org/goma/server/proto/api"
-)
-
-var (
-	requestID = tag.MustNewKey("go.chromium.org/goma/server/rpc.request_id")
-)
-
-func init() {
-	log.RegisterTagKey(requestID)
-}
-
-// RequestID returns string identifier of this request.
-func RequestID(r *gomapb.RequesterInfo) string {
-	return fmt.Sprintf("%s#%d", r.GetCompilerProxyId(), r.GetRetry())
-}
-
-// TagID tags request id in context.
-func TagID(ctx context.Context, r *gomapb.RequesterInfo) (context.Context, string) {
-	logger := log.FromContext(ctx)
-	id := RequestID(r)
-	tctx, err := tag.New(ctx, tag.Upsert(requestID, id))
-	if err != nil {
-		logger.Errorf("tag error: %v", err)
-		return ctx, id
-	}
-	return tctx, id
-}
diff --git a/rpc/retry.go b/rpc/retry.go
deleted file mode 100644
index f1128e7..0000000
--- a/rpc/retry.go
+++ /dev/null
@@ -1,285 +0,0 @@
-// Copyright 2017 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.
-
-package rpc
-
-import (
-	"context"
-	"errors"
-	"fmt"
-	"io"
-	"math/rand"
-	"strings"
-	"time"
-
-	"go.opencensus.io/trace"
-	epb "google.golang.org/genproto/googleapis/rpc/errdetails"
-	"google.golang.org/grpc"
-	"google.golang.org/grpc/codes"
-	"google.golang.org/grpc/status"
-
-	"go.chromium.org/goma/server/log"
-)
-
-// see google.golang.org/grpc backoff.go
-
-// Retry handles rpc retry.
-type Retry struct {
-	// MaxRetry represents how many times to retry.
-	// If it is not positive, it retries while error is
-	// codes.Unavailable or codes.ResourceExhausted.
-	MaxRetry  int
-	BaseDelay time.Duration
-	MaxDelay  time.Duration
-
-	// backoff factor. default is 1.6
-	Factor float64
-}
-
-func (r Retry) retry() int {
-	if r.MaxRetry == 0 {
-		return -1
-	}
-	return r.MaxRetry
-}
-
-func (r Retry) baseDelay() time.Duration {
-	if r.BaseDelay == 0 && r.MaxDelay == 0 {
-		return 10 * time.Millisecond
-	}
-	return r.BaseDelay
-}
-
-func (r Retry) maxDelay() time.Duration {
-	if r.MaxDelay == 0 {
-		return 120 * time.Second // ctx timeout if set?
-	}
-	return r.MaxDelay
-}
-
-func (r Retry) factor() float64 {
-	if r.Factor == 0 {
-		return 1.6
-	}
-	return r.Factor
-}
-
-func (r Retry) jitter() float64 { return 0.2 }
-
-func (r Retry) backoff(n int) time.Duration {
-	if n == 0 {
-		return r.baseDelay()
-	}
-	backoff, max := float64(r.baseDelay()), float64(r.maxDelay())
-	for backoff < max && n > 0 {
-		backoff *= r.factor()
-		n--
-	}
-	if backoff > max {
-		backoff = max
-	}
-	backoff *= 1 + r.jitter()*(rand.Float64()*2-1)
-	if backoff < 0 {
-		return 0
-	}
-	return time.Duration(backoff)
-}
-
-// RetriableError represents retriable error in Retry.Do.
-type RetriableError struct {
-	Err    error
-	Max    int
-	Delay  time.Duration
-	Factor float64
-}
-
-func (e RetriableError) Error() string {
-	if e.Err != nil {
-		return e.Err.Error()
-	}
-	return fmt.Sprintf("retriable error %v delay: %s", e.Err, e.Delay)
-}
-
-const (
-	transportUnexpectedContentType     = `transport: received the unexpected content-type "text/html; charset=UTF-8"`
-	streamTerminatedByRSTInternalError = `stream terminated by RST_STREAM with error code: INTERNAL_ERROR`
-)
-
-func retryInfo(ctx context.Context, err error) error {
-	if e, ok := err.(RetriableError); ok {
-		return e
-	}
-	if err == context.DeadlineExceeded && ctx.Err() == nil {
-		// f might used shorter deadline than ctx for Retry.Do.
-		// In this case, we could retry until ctx for Retry.Do
-		// reaches deadline.
-		return RetriableError{
-			Err: err,
-		}
-	}
-	st, ok := status.FromError(err)
-	if !ok {
-		return nil
-	}
-	var retryMax int
-	var retryDelay time.Duration
-	var retryFactor float64
-	switch st.Code() {
-	case codes.ResourceExhausted:
-		// start slowly and backing off more quickly.
-		retryMax = 5
-		retryDelay = 1 * time.Second
-		retryFactor = 2.0
-
-	case codes.Unavailable:
-
-	case codes.DeadlineExceeded:
-		if ctx.Err() == nil {
-			// f might used shorter deadline than ctx for Retry.Do.
-			// In this case, we could retry until ctx for Retry.Do
-			// reaches deadline.
-			return RetriableError{
-				Err: err,
-			}
-		}
-
-	case codes.Internal:
-		// grpc sometimes gets internal error of
-		//   transport: received the unexpected content-type "text/html; charset=UTF-8"
-		// from cloud load balancer or endpoint-runtime (?).
-		// http://b/122702746
-		// to mitigate the issue, retry with such error.
-		// it is not good to test error message, but there is no way
-		// to distinguish with other internal error.
-		if strings.Contains(st.Message(), transportUnexpectedContentType) {
-			return RetriableError{
-				Err: errors.New(st.Message()),
-			}
-		}
-		// grpc also gets internal error of
-		//  unexpected EOF
-		// or
-		//  stream terminated by RST_STREAM with error code: INTERNAL_ERROR
-		// http://b/122995912
-		if strings.Contains(st.Message(), io.ErrUnexpectedEOF.Error()) {
-			return RetriableError{
-				Err: errors.New(st.Message()),
-			}
-		}
-		if strings.Contains(st.Message(), streamTerminatedByRSTInternalError) {
-			return RetriableError{
-				Err: errors.New(st.Message()),
-			}
-		}
-		// other internal error is not retriable.
-		return nil
-	default:
-		return nil
-	}
-	for _, d := range st.Details() {
-		if ri, ok := d.(*epb.RetryInfo); ok {
-			dur := ri.GetRetryDelay()
-			var d time.Duration
-			err := dur.CheckValid()
-			if err == nil {
-				d = dur.AsDuration()
-			}
-			return RetriableError{
-				Err:    err,
-				Max:    retryMax,
-				Delay:  d,
-				Factor: retryFactor,
-			}
-		}
-	}
-	return RetriableError{
-		Err:    errors.New("no errdetails.RetryInfo in status"),
-		Max:    retryMax,
-		Delay:  retryDelay,
-		Factor: retryFactor,
-	}
-}
-
-var timeAfter = time.After
-
-// Do calls f with retry, while f returns RetriableError, codes.Unavailable or
-// codes.ResourceExhausted.
-// It returns codes.DeadlineExceeded if ctx is cancelled.
-// It returns last error if f returns error other than codes.Unavailable or
-// codes.ResourceExhausted, or it reaches too many retries.
-// It respects RetriableError.Delay or errdetail RetryInfo
-// if it is specified as error details.
-func (r Retry) Do(ctx context.Context, f func() error) error {
-	ctx, span := trace.StartSpan(ctx, "go.chromium.org/goma/server/rpc.Retry.Do")
-	defer span.End()
-	logger := log.FromContext(ctx)
-
-	var d []time.Duration
-	var errs []error
-
-	toError := func(errs []error) error {
-		if len(errs) == 0 {
-			return status.Errorf(codes.Internal, "retry finished with no error?")
-		}
-		if len(errs) == 1 {
-			return errs[0]
-		}
-		lastErr, ok := status.FromError(errs[len(errs)-1])
-		if !ok {
-			return status.Errorf(codes.Unknown, "%v", errs)
-		}
-		return status.Errorf(lastErr.Code(), "%s :%v", lastErr.Message(), errs[:len(errs)-1])
-	}
-
-	for i := 0; ; i++ {
-		if maxRetry := r.retry(); maxRetry > 0 && i >= maxRetry {
-			span.Annotatef([]trace.Attribute{
-				trace.Int64Attribute("retry", int64(i)),
-			}, "too many retries %v: %v", d, errs)
-			logger.Warnf("too many retries %d: %v: %v", i, d, errs)
-			break
-		}
-		t := time.Now()
-		err := f()
-		if err == nil {
-			return nil
-		}
-		d = append(d, time.Since(t))
-		errs = append(errs, err)
-
-		rerr, ok := retryInfo(ctx, err).(RetriableError)
-		if !ok {
-			return toError(errs)
-		}
-		if rerr.Err != nil {
-			span.Annotatef(nil, "retryInfo.err: %v", rerr.Err)
-		}
-		if (r.MaxRetry <= 0 && rerr.Max != r.MaxRetry) || (rerr.Max > 0 && rerr.Max < r.MaxRetry) {
-			r.MaxRetry = rerr.Max
-			span.Annotatef(nil, "retryInfo.delay=%s", rerr.Delay)
-		}
-		if rerr.Delay > r.BaseDelay {
-			r.BaseDelay = rerr.Delay
-			span.Annotatef(nil, "retryInfo.delay=%s", rerr.Delay)
-		}
-		if rerr.Factor != 0 {
-			r.Factor = rerr.Factor
-			span.Annotatef(nil, "retryInfo.factor=%v", rerr.Factor)
-		}
-
-		delay := r.backoff(i)
-		span.Annotatef([]trace.Attribute{
-			trace.Int64Attribute("retry", int64(i)),
-			trace.Int64Attribute("backoff_msec", delay.Nanoseconds()/1e6),
-		}, "retry backoff %s for err:%v", delay, err)
-		logger.Warnf("retry %d backoff %s for err:%v retryInfo=%#v", i, delay, err, r)
-		select {
-		case <-ctx.Done():
-			return grpc.Errorf(codes.DeadlineExceeded, "%v: %v: %v", ctx.Err(), d, errs)
-		case <-timeAfter(delay):
-			d = append(d, delay)
-		}
-	}
-	return toError(errs)
-}
diff --git a/rpc/retry_test.go b/rpc/retry_test.go
deleted file mode 100644
index 163c4fb..0000000
--- a/rpc/retry_test.go
+++ /dev/null
@@ -1,211 +0,0 @@
-// Copyright 2019 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.
-
-package rpc
-
-import (
-	"context"
-	"errors"
-	"io"
-	"testing"
-	"time"
-
-	"google.golang.org/grpc/codes"
-	"google.golang.org/grpc/status"
-)
-
-type retrySpy struct {
-	errs []error
-	n    int
-}
-
-func (r *retrySpy) f() error {
-	i := r.n
-	r.n++
-	if i < len(r.errs) {
-		return r.errs[i]
-	}
-	return errors.New("too many errors")
-}
-
-func TestRetry(t *testing.T) {
-	origTimeAfter := timeAfter
-	timeAfter = func(d time.Duration) <-chan time.Time {
-		ch := make(chan time.Time)
-		close(ch)
-		return ch
-	}
-	defer func() {
-		timeAfter = origTimeAfter
-	}()
-	for _, tc := range []struct {
-		desc    string
-		retry   Retry
-		f       *retrySpy
-		wantN   int
-		wantErr bool
-	}{
-		{
-			desc: "success",
-			f: &retrySpy{
-				errs: []error{nil},
-			},
-			wantN: 1,
-		},
-		{
-			desc: "error",
-			f: &retrySpy{
-				errs: []error{errors.New("non retriable error")},
-			},
-			wantN:   1,
-			wantErr: true,
-		},
-		{
-			desc: "retry with retriable error",
-			f: &retrySpy{
-				errs: []error{
-					RetriableError{
-						Err: errors.New("retriable error"),
-					},
-					nil,
-				},
-			},
-			wantN: 2,
-		},
-		{
-			desc: "retry with unavailable",
-			f: &retrySpy{
-				errs: []error{
-					status.Error(codes.Unavailable, "unavailable -> retry"),
-					nil,
-				},
-			},
-			wantN: 2,
-		},
-		{
-			desc: "retry with internal transport unexpected content-type",
-			f: &retrySpy{
-				errs: []error{
-					status.Error(codes.Internal, transportUnexpectedContentType),
-					nil,
-				},
-			},
-			wantN: 2,
-		},
-		{
-			desc: "retry with internal transport unexpected EOF",
-			f: &retrySpy{
-				errs: []error{
-					status.Errorf(codes.Internal, "call method: %v", io.ErrUnexpectedEOF),
-					nil,
-				},
-			},
-			wantN: 2,
-		},
-		{
-			desc: "retry with stream terminated by RST STREAM with error code INTERNAL ERROR",
-			f: &retrySpy{
-				errs: []error{
-					status.Errorf(codes.Internal, "call method: %v", streamTerminatedByRSTInternalError),
-					nil,
-				},
-			},
-			wantN: 2,
-		},
-		{
-			desc: "no retry with internal error",
-			f: &retrySpy{
-				errs: []error{
-					status.Error(codes.Internal, "non retriable internal error"),
-					nil,
-				},
-			},
-			wantN:   1,
-			wantErr: true,
-		},
-		{
-			desc: "no retry with bare unexpected EOF",
-			f: &retrySpy{
-				errs: []error{
-					io.ErrUnexpectedEOF,
-					nil,
-				},
-			},
-			wantN:   1,
-			wantErr: true,
-		},
-		{
-			desc: "retry with retriable error only once",
-			retry: Retry{
-				MaxRetry: 1,
-			},
-			f: &retrySpy{
-				errs: []error{
-					RetriableError{
-						Err: errors.New("retriable error"),
-					},
-					RetriableError{
-						Err: errors.New("retriable error"),
-					},
-					nil,
-				},
-			},
-			wantN:   1,
-			wantErr: true,
-		},
-		{
-			desc: "retry with shorter deadlinein f",
-			f: &retrySpy{
-				errs: []error{
-					context.DeadlineExceeded,
-					status.Error(codes.DeadlineExceeded, "deadline exceeded"),
-					nil,
-				},
-			},
-			wantN: 3,
-		},
-		{
-			desc: "retry with RESOURCE_EXHAUSTED",
-			f: &retrySpy{
-				errs: []error{
-					status.Error(codes.ResourceExhausted, "resource exhausted"),
-					status.Error(codes.ResourceExhausted, "resource exhausted"),
-					status.Error(codes.ResourceExhausted, "resource exhausted"),
-					status.Error(codes.ResourceExhausted, "resource exhausted"),
-					status.Error(codes.ResourceExhausted, "resource exhausted"),
-					status.Error(codes.ResourceExhausted, "resource exhausted"),
-					status.Error(codes.ResourceExhausted, "resource exhausted"),
-					status.Error(codes.ResourceExhausted, "resource exhausted"),
-					status.Error(codes.ResourceExhausted, "resource exhausted"),
-					status.Error(codes.ResourceExhausted, "resource exhausted"),
-				},
-			},
-			wantN:   5,
-			wantErr: true,
-		},
-	} {
-		t.Run(tc.desc, func(t *testing.T) {
-			ctx := context.Background()
-			err := tc.retry.Do(ctx, tc.f.f)
-			if tc.f.n != tc.wantN || (err != nil) != tc.wantErr {
-				t.Errorf("retry %d, %v; want %d, err=%t", tc.f.n, err, tc.wantN, tc.wantErr)
-			}
-		})
-	}
-}
-
-func TestRetryDeadline(t *testing.T) {
-	ctx, cancel := context.WithTimeout(context.Background(), 1*time.Millisecond)
-	defer cancel()
-
-	n := 0
-	err := Retry{}.Do(ctx, func() error {
-		n++
-		<-ctx.Done()
-		return ctx.Err()
-	})
-	if n > 1 || err == nil {
-		t.Errorf("retry %d, %v; want 1, err", n, err)
-	}
-}
diff --git a/server/cluster.go b/server/cluster.go
deleted file mode 100644
index ef5f717..0000000
--- a/server/cluster.go
+++ /dev/null
@@ -1,109 +0,0 @@
-// Copyright 2018 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.
-
-package server
-
-import (
-	"context"
-	"os"
-	"path"
-	"strings"
-	"sync"
-
-	gce "cloud.google.com/go/compute/metadata"
-
-	"go.chromium.org/goma/server/log"
-)
-
-var (
-	clusterOnce     sync.Once
-	clusterName     string
-	clusterLocation string
-)
-
-func initClusterName(ctx context.Context) {
-	if !gce.OnGCE() {
-		return
-	}
-	logger := log.FromContext(ctx)
-	logger.Debug("trying to get cluster name from metadata")
-	var err error
-	clusterName, err = gce.InstanceAttributeValue("cluster-name")
-	if err != nil {
-		logger.Errorf("failed to get cluster-name on gce: %v", err)
-	} else {
-		clusterName = strings.TrimSpace(clusterName)
-		logger.Infof("cluster name: %s", clusterName)
-	}
-	clusterLocation, err = gce.InstanceAttributeValue("cluster-location")
-	if err != nil {
-		logger.Errorf("failed to get cluster-location on gce: %v", err)
-	} else {
-		clusterLocation = strings.TrimSpace(clusterLocation)
-		logger.Infof("cluster location: %s", clusterLocation)
-	}
-}
-
-// ClusterName returns cluster name where server is running on.
-func ClusterName(ctx context.Context) string {
-	clusterOnce.Do(func() {
-		initClusterName(ctx)
-	})
-	return clusterName
-}
-
-var (
-	projectOnce sync.Once
-	projectID   string
-)
-
-// ProjectID returns current project id.
-func ProjectID(ctx context.Context) string {
-	projectOnce.Do(func() {
-		if !gce.OnGCE() {
-			return
-		}
-		logger := log.FromContext(ctx)
-		var err error
-		projectID, err = gce.ProjectID()
-		if err != nil {
-			logger.Errorf("failed to get project id: %v", err)
-		}
-	})
-	return projectID
-}
-
-// serverName returns service name, prefixed by cluster name if any.
-func serverName(ctx context.Context, name string) string {
-	clusterName := ClusterName(ctx)
-	if clusterName != "" {
-		name = path.Join(clusterName, name)
-	}
-	return name
-}
-
-var (
-	hostnameOnce sync.Once
-	hostname     string
-)
-
-func initHostname(ctx context.Context) {
-	logger := log.FromContext(ctx)
-
-	var err error
-	hostname, err = os.Hostname()
-	if err != nil {
-		logger.Errorf("hostname: %v", err)
-		hostname = os.Getenv("HOSTNAME")
-	}
-	logger.Infof("hostname: %s", hostname)
-}
-
-// HostName returns hostname. in k8s, it is podname.
-func HostName(ctx context.Context) string {
-	hostnameOnce.Do(func() {
-		initHostname(ctx)
-	})
-	return hostname
-}
diff --git a/server/dial.go b/server/dial.go
deleted file mode 100644
index 39d0fac..0000000
--- a/server/dial.go
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright 2018 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.
-
-package server
-
-import (
-	"context"
-	"time"
-
-	"go.opencensus.io/plugin/ocgrpc"
-	"google.golang.org/grpc"
-	_ "google.golang.org/grpc/encoding/gzip" // also register compressor for server side
-	"google.golang.org/grpc/keepalive"
-)
-
-// DefaultDialOption is default dial option to record opencensus stats and traces.
-func DefaultDialOption() []grpc.DialOption {
-	return []grpc.DialOption{
-		grpc.WithInsecure(),
-		grpc.WithKeepaliveParams(keepalive.ClientParameters{
-			Time:                10 * time.Second,
-			Timeout:             5 * time.Second,
-			PermitWithoutStream: false,
-		}),
-		grpc.WithStatsHandler(&ocgrpc.ClientHandler{}),
-	}
-}
-
-// DialContext dials to addr with default dial options.
-func DialContext(ctx context.Context, addr string, opts ...grpc.DialOption) (*grpc.ClientConn, error) {
-	opts = append(opts, DefaultDialOption()...)
-	return grpc.DialContext(ctx, addr, opts...)
-}
diff --git a/server/healthz/healthz.go b/server/healthz/healthz.go
deleted file mode 100644
index 68805eb..0000000
--- a/server/healthz/healthz.go
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright 2017 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.
-
-// Package healthz provides /healthz for grpc server.
-package healthz
-
-import (
-	"context"
-	"fmt"
-	"net/http"
-	"sync"
-	"time"
-
-	"google.golang.org/grpc"
-	"google.golang.org/grpc/health"
-	healthpb "google.golang.org/grpc/health/grpc_health_v1"
-
-	"go.chromium.org/goma/server/log"
-)
-
-var (
-	connMu sync.Mutex
-	conn   *grpc.ClientConn
-)
-
-func dialOnce(ctx context.Context, addr string) (*grpc.ClientConn, error) {
-	connMu.Lock()
-	defer connMu.Unlock()
-	if conn != nil {
-		return conn, nil
-	}
-	logger := log.FromContext(ctx)
-	deadline, ok := ctx.Deadline()
-	if ok {
-		logger.Debugf("dialing %s for /healthz check. deadline=%s", addr, deadline)
-	} else {
-		logger.Debugf("dialing %s for /healthz check", addr)
-	}
-	var err error
-	conn, err = grpc.DialContext(ctx, addr, grpc.WithInsecure())
-	if err != nil {
-		return nil, err
-	}
-	return conn, nil
-}
-
-var (
-	mu        sync.Mutex
-	unhealthy string
-)
-
-// SetUnhealthy sets m as unhealthy message.
-// empty message means healthy.
-func SetUnhealthy(m string) {
-	mu.Lock()
-	defer mu.Unlock()
-	unhealthy = m
-}
-
-func getUnhealthy() string {
-	mu.Lock()
-	defer mu.Unlock()
-	return unhealthy
-}
-
-// Register registers /healthz handler for grpc server.
-func Register(s *grpc.Server, addr string) {
-	healthpb.RegisterHealthServer(s, health.NewServer())
-	http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
-		ctx := r.Context()
-		logger := log.FromContext(ctx)
-		now := time.Now()
-
-		m := getUnhealthy()
-		if m != "" {
-			logger.Warnf("/healthz reports unhealthy: %s", m)
-			http.Error(w, m, http.StatusServiceUnavailable)
-			return
-		}
-
-		conn, err := dialOnce(ctx, addr)
-		if err != nil {
-			logger.Warnf("/healthz check failed for %s to dial: %v", time.Since(now), err)
-			http.Error(w, fmt.Sprintf("failed to create grpc connection: %v", err), http.StatusServiceUnavailable)
-			return
-		}
-
-		hc := healthpb.NewHealthClient(conn)
-		resp, err := hc.Check(ctx, &healthpb.HealthCheckRequest{})
-		if err != nil {
-			logger.Errorf("/healthz check failed to call Check: %v", err)
-			http.Error(w, fmt.Sprintf("failed to call Check: %v", err), http.StatusServiceUnavailable)
-			return
-		}
-		if resp.Status != healthpb.HealthCheckResponse_SERVING {
-			logger.Errorf("/healthz check failed to get serving status: %v", resp.Status)
-			http.Error(w, fmt.Sprintf("health server is not serving: %v", resp.Status), http.StatusServiceUnavailable)
-			return
-		}
-		w.Write([]byte("ok"))
-		logger.Debugf("%s is healthy: %s", addr, time.Since(now))
-	})
-}
diff --git a/server/init.go b/server/init.go
deleted file mode 100644
index 0d70ac8..0000000
--- a/server/init.go
+++ /dev/null
@@ -1,117 +0,0 @@
-// Copyright 2018 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.
-
-package server
-
-import (
-	"context"
-	"fmt"
-	"net/http"
-	"time"
-
-	"contrib.go.opencensus.io/exporter/stackdriver"
-	"contrib.go.opencensus.io/exporter/stackdriver/monitoredresource"
-	"contrib.go.opencensus.io/exporter/stackdriver/propagation"
-	"go.opencensus.io/plugin/ocgrpc"
-	"go.opencensus.io/plugin/ochttp"
-	"go.opencensus.io/stats/view"
-	"go.opencensus.io/trace"
-	"google.golang.org/api/option"
-	"google.golang.org/grpc/codes"
-	"google.golang.org/grpc/status"
-
-	"go.chromium.org/goma/server/log"
-	"go.chromium.org/goma/server/log/errorreporter"
-)
-
-var (
-	exporter *stackdriver.Exporter
-
-	// Increased from Default 10 seconds for quota limit.
-	// The recommended reporting period by Stackdriver Monitoring is >= 1 minute:
-	// https://cloud.google.com/monitoring/custom-metrics/creating-metrics#writing-ts
-	// https://pkg.go.dev/go.opencensus.io/stats/view?tab=doc#SetReportingPeriod
-	reportingInterval = 90 * time.Second
-)
-
-// Init initializes opencensus instrumentations, and error reporter.
-// If projectID is not empty, it registers stackdriver exporter for the project.
-// It also calls SetupHTTPClient.
-func Init(ctx context.Context, projectID, name string) error {
-	logger := log.FromContext(ctx)
-	if projectID != "" {
-		logger.Infof("send stackdriver trace log to project %s", projectID)
-
-		var err error
-		exporter, err = stackdriver.NewExporter(stackdriver.Options{
-			ProjectID: projectID,
-			OnError: func(err error) {
-				switch status.Code(err) {
-				case codes.Unavailable:
-					logger.Warnf("Failed to export to Stackdriver: %v", err)
-				default:
-					logger.Errorf("Failed to export to Stackdriver: %v", err)
-				}
-			},
-			MonitoredResource: monitoredresource.Autodetect(),
-
-			// Disallow grpc in google-api-go-client to send stats/trace of monitoring grpc's api call.
-			MonitoringClientOptions: []option.ClientOption{option.WithTelemetryDisabled()},
-			TraceClientOptions:      []option.ClientOption{option.WithTelemetryDisabled()},
-		})
-		if err != nil {
-			return fmt.Errorf("failed to create exporter: %v", err)
-		}
-		view.RegisterExporter(exporter)
-		trace.RegisterExporter(exporter)
-		view.SetReportingPeriod(reportingInterval)
-
-		errorreporter.DefaultErrorReporter = errorreporter.New(ctx, projectID, serverName(ctx, name))
-	}
-
-	err := view.Register(ocgrpc.DefaultServerViews...)
-	if err != nil {
-		return fmt.Errorf("failed to subscribe ocgrpc view: %v", err)
-	}
-	err = view.Register(ocgrpc.DefaultClientViews...)
-	if err != nil {
-		return fmt.Errorf("failed to subscribe ocgrpc client view: %v", err)
-	}
-	err = view.Register(ochttp.DefaultServerViews...)
-	if err != nil {
-		return fmt.Errorf("failed to subscribe ochttp view: %v", err)
-	}
-	err = view.Register(ochttp.DefaultClientViews...)
-	if err != nil {
-		return fmt.Errorf("failed to subscribe ochttp view: %v", err)
-	}
-	SetupHTTPClient()
-
-	err = view.Register(procStatViews...)
-	if err != nil {
-		return fmt.Errorf("failed to subscribe proc stat view: %v", err)
-	}
-	go reportProcStats(context.Background())
-	return nil
-}
-
-// SetupHTTPClient sets up http default client to monitor by opencensus.
-func SetupHTTPClient() {
-	// we can't set the transport as http.DefaultTransport, because
-	// ochttp.Transport will use http.DefaultTransport so it causes
-	// recursive calls.
-	http.DefaultClient = &http.Client{
-		Transport: &ochttp.Transport{
-			Propagation: &propagation.HTTPFormat{},
-		},
-	}
-}
-
-// Flush flushes opencensus data.
-func Flush() {
-	if exporter == nil {
-		return
-	}
-	exporter.Flush()
-}
diff --git a/server/limited_sampler.go b/server/limited_sampler.go
deleted file mode 100644
index 827bba1..0000000
--- a/server/limited_sampler.go
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2019 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.
-
-package server
-
-import (
-	"sync"
-	"time"
-
-	"go.opencensus.io/trace"
-)
-
-const (
-	// same as default sampler
-	// https://github.com/census-instrumentation/opencensus-go/blob/052120675fac2ace91dc2c01e5f63c3e6ec62f04/trace/sampling.go#L21
-	DefaultTraceFraction = 1e-4
-
-	// trace API limit is 4800/minutes.
-	// https://cloud.google.com/trace/docs/quotas#trace-api-limit
-	// 4800/60/(total number of replicas in the project)
-	DefaultTraceQPS = 0.05
-)
-
-type limitedSampler struct {
-	sampler        trace.Sampler
-	sampleDuration time.Duration
-
-	mu          sync.Mutex
-	lastSampled time.Time
-}
-
-func (ls *limitedSampler) Sample(p trace.SamplingParameters) trace.SamplingDecision {
-	d := ls.sampler(p)
-	ls.mu.Lock()
-	defer ls.mu.Unlock()
-	if d.Sample && time.Since(ls.lastSampled) < ls.sampleDuration {
-		d.Sample = false
-	}
-	if d.Sample {
-		ls.lastSampled = time.Now()
-	}
-	return d
-}
-
-// NewLimitedSampler returns trace sampler limited by fraction and qps.
-func NewLimitedSampler(fraction, qps float64) trace.Sampler {
-	return (&limitedSampler{
-		sampler:        trace.ProbabilitySampler(fraction),
-		lastSampled:    time.Now(),
-		sampleDuration: time.Duration(float64(1*time.Second) / qps),
-	}).Sample
-}
diff --git a/server/process_collector.go b/server/process_collector.go
deleted file mode 100644
index 5cc4003..0000000
--- a/server/process_collector.go
+++ /dev/null
@@ -1,231 +0,0 @@
-// Copyright 2018 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.
-
-package server
-
-import (
-	"bufio"
-	"bytes"
-	"context"
-	"errors"
-	"fmt"
-	"io"
-	"io/ioutil"
-	"os"
-	"runtime"
-	"runtime/debug"
-	"strconv"
-	"sync/atomic"
-	"time"
-
-	"go.opencensus.io/stats"
-	"go.opencensus.io/stats/view"
-
-	"go.chromium.org/goma/server/log"
-)
-
-var (
-	openFDs = stats.Int64("go.chromium.org/goma/server/server/process-open-fds",
-		"Number of open file descriptors",
-		stats.UnitDimensionless)
-	maxFDs = stats.Int64("go.chromium.org/goma/server/server/process-max-fds",
-		"Maximum number of open file descriptors",
-		stats.UnitDimensionless)
-
-	virtualMemorySize = stats.Int64("go.chromium.org/goma/server/server/process-virtual-memory",
-		"Virtual memory size",
-		stats.UnitBytes)
-	residentMemorySize = stats.Int64("go.chromium.org/goma/server/server/process-resident-memory",
-		"Resident memory size",
-		stats.UnitBytes)
-
-	procStatViews = []*view.View{
-		{
-			Name:        "go.chromium.org/goma/server/server/process-open-fds",
-			Description: "Number of open file descriptors",
-			Measure:     openFDs,
-			Aggregation: view.LastValue(),
-		},
-		{
-			Name:        "go.chromium.org/goma/server/server/process-max-fds",
-			Description: "Maxinum number of open file descriptors",
-			Measure:     maxFDs,
-			Aggregation: view.LastValue(),
-		},
-		{
-			Name:        "go.chromium.org/goma/server/server/process-virtual-memory",
-			Description: "Virtual memory size",
-			Measure:     virtualMemorySize,
-			Aggregation: view.LastValue(),
-		},
-		{
-			Name:        "go.chromium.org/goma/server/server/process-resident-memory",
-			Description: "Resident memory size",
-			Measure:     residentMemorySize,
-			Aggregation: view.LastValue(),
-		},
-	}
-
-	lastResidentMemorySize int64 // atomic.
-
-	samplingInterval = time.Second
-
-	gcSema = make(chan bool, 1)
-)
-
-// ResidentMemorySize reports latest measured resident memory size in bytes.
-func ResidentMemorySize() int64 {
-	return atomic.LoadInt64(&lastResidentMemorySize)
-}
-
-// GC runs garbage-collector and reports latest measured resident memory size in bytes.
-func GC(ctx context.Context) int64 {
-	logger := log.FromContext(ctx)
-	rss := ResidentMemorySize()
-	logger.Infof("GC start: rss=%d", rss)
-	select {
-	case gcSema <- true:
-		debug.FreeOSMemory()
-		<-gcSema
-	default:
-		logger.Infof("GC already running")
-	}
-	procStats(ctx)
-	rss = ResidentMemorySize()
-	logger.Infof("GC end: rss=%d", rss)
-	return rss
-}
-
-func numOpenFDs(ctx context.Context) (int64, error) {
-	d, err := os.Open("/proc/self/fd")
-	if err != nil {
-		return 0, err
-	}
-	defer d.Close()
-	names, err := d.Readdirnames(-1)
-	if err != nil {
-		return 0, err
-	}
-	return int64(len(names)), nil
-}
-
-func parseMaxFDs(ctx context.Context, r io.Reader) (int64, error) {
-	// soft limit of "Max open files"
-	const maxOpenFiles = `Max open files`
-	s := bufio.NewScanner(r)
-	for s.Scan() {
-		line := s.Bytes()
-		if !bytes.HasPrefix(line, []byte(maxOpenFiles)) {
-			continue
-		}
-		line = bytes.TrimPrefix(line, []byte(maxOpenFiles))
-		line = bytes.TrimSpace(line)
-		cols := bytes.Fields(line)
-		if len(cols) == 0 {
-			return 0, fmt.Errorf("wrong line for Max open files: %q", string(line))
-		}
-		return strconv.ParseInt(string(cols[0]), 10, 64)
-	}
-	err := s.Err()
-	if err != nil {
-		return 0, err
-	}
-	return 0, errors.New(`"Max open files" not found`)
-}
-
-func numMaxFDs(ctx context.Context) (int64, error) {
-	f, err := os.Open("/proc/self/limits")
-	if err != nil {
-		return 0, err
-	}
-	defer f.Close()
-	return parseMaxFDs(ctx, f)
-}
-
-func parseStatMemory(ctx context.Context, r io.Reader) (vsize, rss int64, err error) {
-	// see proc(5)
-	data, err := ioutil.ReadAll(r)
-	if err != nil {
-		return 0, 0, err
-	}
-	i := bytes.LastIndex(data, []byte(")"))
-	if i < 0 {
-		return 0, 0, fmt.Errorf("unexpected format of stat (no comm): %q", string(data))
-	}
-	cols := bytes.Fields(data[i+2:])
-	if len(cols) < 22 {
-		return 0, 0, fmt.Errorf("unexpected format of stat (few data): %q %d", string(data), len(cols))
-	}
-	// cols starts from (3) state.
-	// we want (23) vsize and (24) rss.
-	const vsizeIndex = 23 - 3
-	const rssIndex = 24 - 3
-	vsize, err = strconv.ParseInt(string(cols[vsizeIndex]), 10, 64)
-	if err != nil {
-		return 0, 0, fmt.Errorf("parse vsize %q: %v", string(cols[vsizeIndex]), err)
-	}
-	rss, err = strconv.ParseInt(string(cols[rssIndex]), 10, 64)
-	if err != nil {
-		return 0, 0, fmt.Errorf("parse rss %q: %v", string(cols[rssIndex]), err)
-	}
-	// vsize is in bytes, rss is in pages.
-	rssBytes := rss * int64(os.Getpagesize())
-	atomic.StoreInt64(&lastResidentMemorySize, rssBytes)
-	return vsize, rssBytes, nil
-}
-
-func statMemory(ctx context.Context) (vsize, rss int64, err error) {
-	f, err := os.Open("/proc/self/stat")
-	if err != nil {
-		return 0, 0, err
-	}
-	defer f.Close()
-	return parseStatMemory(ctx, f)
-}
-
-func procStats(ctx context.Context) {
-	logger := log.FromContext(ctx)
-
-	var m []stats.Measurement
-
-	n, err := numOpenFDs(ctx)
-	if err != nil {
-		logger.Errorf("failed to get open-fds: %v", err)
-	} else {
-		m = append(m, openFDs.M(n))
-	}
-	n, err = numMaxFDs(ctx)
-	if err != nil {
-		logger.Errorf("failed to get max-fds: %v", err)
-	} else {
-		m = append(m, maxFDs.M(n))
-	}
-	vsize, rss, err := statMemory(ctx)
-	if err != nil {
-		logger.Errorf("failed to get stat: %v", err)
-	} else {
-		m = append(m,
-			virtualMemorySize.M(vsize),
-			residentMemorySize.M(rss))
-	}
-	stats.Record(ctx, m...)
-}
-
-func reportProcStats(ctx context.Context) {
-	logger := log.FromContext(ctx)
-	if "linux" != runtime.GOOS {
-		logger.Warnf("Reporting proc stats is not supported on %q", runtime.GOOS)
-		return
-	}
-	t := time.NewTicker(samplingInterval)
-	defer t.Stop()
-	for {
-		select {
-		case <-ctx.Done():
-			return
-		case <-t.C:
-			procStats(ctx)
-		}
-	}
-}
diff --git a/server/process_collector_test.go b/server/process_collector_test.go
deleted file mode 100644
index 0ef11a9..0000000
--- a/server/process_collector_test.go
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright 2018 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.
-
-package server
-
-import (
-	"context"
-	"os"
-	"strings"
-	"testing"
-)
-
-func TestNumOpenFDs(t *testing.T) {
-	ctx := context.Background()
-	n0, err := numOpenFDs(ctx)
-	if err != nil {
-		t.Fatalf("numOpenFDs(ctx)=_, %v", err)
-	}
-	t.Logf("open /dev/null")
-	f, err := os.Open("/dev/null")
-	if err != nil {
-		t.Fatal(err)
-	}
-	n, err := numOpenFDs(ctx)
-	if err != nil {
-		t.Fatalf("numOpenFDs(ctx)=_, %v", err)
-	}
-	if n != n0+1 {
-		t.Errorf("%d != %d + 1", n, n0)
-	}
-	t.Logf("close")
-	f.Close()
-	n, err = numOpenFDs(ctx)
-	if err != nil {
-		t.Fatalf("numOpenFDs(ctx)=_, %v", err)
-	}
-	if n != n0 {
-		t.Errorf("%d != %d", n, n0)
-	}
-}
-
-func TestParseMaxFDs(t *testing.T) {
-	const procSelfLimits = `Limit                     Soft Limit           Hard Limit           Units
-Max cpu time              unlimited            unlimited            seconds
-Max file size             unlimited            unlimited            bytes
-Max data size             unlimited            unlimited            bytes
-Max stack size            8388608              unlimited            bytes
-Max core file size        0                    unlimited            bytes
-Max resident set          unlimited            unlimited            bytes
-Max processes             32768                32768                processes
-Max open files            32768                32768                files
-Max locked memory         unlimited            unlimited            bytes
-Max address space         unlimited            unlimited            bytes
-Max file locks            unlimited            unlimited            locks
-Max pending signals       256886               256886               signals
-Max msgqueue size         819200               819200               bytes
-Max nice priority         0                    0
-Max realtime priority     0                    0
-Max realtime timeout      unlimited            unlimited            us
-`
-
-	ctx := context.Background()
-	n, err := parseMaxFDs(ctx, strings.NewReader(procSelfLimits))
-	if err != nil {
-		t.Errorf("parseMaxFDs(ctx, procSelfLimits)=_, %v", err)
-	}
-	if got, want := n, int64(32768); got != want {
-		t.Errorf("parseMaxFDs(ctx, procSelfLimits)=%d; want=%d", got, want)
-	}
-}
-
-func TestParseStatMemory(t *testing.T) {
-	const procSelfStat = `9883 (cat) R 19182 9883 19182 34827 9883 4194304 107 0 0 0 0 0 0 0 20 0 1 0 95447658 8044544 169 18446744073709551615 94700269010944 94700269041680 140721631836896 0 0 0 0 0 0 0 0 0 17 13 0 0 0 0 0 94700271139920 94700271141536 94700293513216 140721631845545 140721631845565 140721631845565 140721631850479 0
-`
-	ctx := context.Background()
-	vsize, rss, err := parseStatMemory(ctx, strings.NewReader(procSelfStat))
-	if err != nil {
-		t.Errorf("parseStatMemory(ctx, procSelfStat)=_, _, %v", err)
-	}
-	if got, want := vsize, int64(8044544); got != want {
-		t.Errorf("parseStatMemory(ctx, procSelfStat).vss=%d; want=%d", got, want)
-	}
-	if got, want := rss, int64(169*os.Getpagesize()); got != want {
-		t.Errorf("parseStatMemory(ctx, procSelfStat).rss=%d; want=%d", got, want)
-	}
-}
diff --git a/server/resource.go b/server/resource.go
deleted file mode 100644
index 6be5925..0000000
--- a/server/resource.go
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2019 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.
-
-package server
-
-import (
-	"fmt"
-	"io/ioutil"
-	"strconv"
-	"strings"
-)
-
-// MemoryLimit returns server memory limit, set by cgroup.
-func MemoryLimit() (int64, error) {
-	const fname = "/sys/fs/cgroup/memory/memory.limit_in_bytes"
-	buf, err := ioutil.ReadFile(fname)
-	if err != nil {
-		return 0, err
-	}
-	data := strings.TrimSpace(string(buf))
-	v, err := strconv.ParseInt(data, 10, 64)
-	if err != nil {
-		return 0, fmt.Errorf("failed to parse %s: %q: %v", fname, data, err)
-	}
-	return v, nil
-}
diff --git a/server/server.go b/server/server.go
deleted file mode 100644
index 2568709..0000000
--- a/server/server.go
+++ /dev/null
@@ -1,161 +0,0 @@
-// Copyright 2018 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.
-
-// Package server provides functions for goma servers.
-package server
-
-import (
-	"context"
-	"fmt"
-	"net"
-	"net/http"
-	"os"
-	"os/signal"
-	"sync"
-	"syscall"
-	"time"
-
-	"go.opencensus.io/plugin/ocgrpc"
-	"google.golang.org/grpc"
-	"google.golang.org/grpc/keepalive"
-	"google.golang.org/grpc/reflection"
-
-	"go.chromium.org/goma/server/log"
-	"go.chromium.org/goma/server/log/errorreporter"
-	"go.chromium.org/goma/server/server/healthz"
-)
-
-// Server is interface to control server.
-type Server interface {
-	// ListenAndServe listens and then serve to handle requests on incoming
-	// connections.
-	ListenAndServe() error
-
-	// Shutdown gracefully shuts down the server.
-	Shutdown(context.Context) error
-}
-
-// GRPC represents grpc server.
-type GRPC struct {
-	*grpc.Server
-	net.Listener
-}
-
-// ListenAndServe listens on Listener and handles requests with Server.
-func (g GRPC) ListenAndServe() error {
-	reflection.Register(g.Server)
-	healthz.Register(g.Server, g.Listener.Addr().String())
-	return g.Server.Serve(g.Listener)
-}
-
-// Shutdown gracefully shuts down the server.
-func (g GRPC) Shutdown(ctx context.Context) error {
-	done := make(chan struct{})
-	go func() {
-		g.Server.GracefulStop()
-		close(done)
-	}()
-	select {
-	case <-done:
-		return nil
-	case <-ctx.Done():
-		return ctx.Err()
-	}
-}
-
-// NewGRPC creates grpc server listening on port.
-func NewGRPC(port int, opts ...grpc.ServerOption) (GRPC, error) {
-	lis, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
-	if err != nil {
-		return GRPC{}, err
-	}
-	opts = append(opts,
-		grpc.KeepaliveEnforcementPolicy(keepalive.EnforcementPolicy{
-			MinTime:             5 * time.Second,
-			PermitWithoutStream: false,
-		}),
-		grpc.StatsHandler(&ocgrpc.ServerHandler{}),
-		grpc.UnaryInterceptor(func() func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
-			interceptor := log.GRPCUnaryServerInterceptor()
-			if errorreporter.Enabled() {
-				return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
-					defer errorreporter.Do(nil, &err)
-					return interceptor(ctx, req, info, handler)
-				}
-			}
-			return interceptor
-		}()))
-	s := grpc.NewServer(opts...)
-	return GRPC{Server: s, Listener: lis}, nil
-}
-
-// NewHTTP creates http server.
-func NewHTTP(port int, handler http.Handler) *http.Server {
-	return &http.Server{
-		Addr:    fmt.Sprintf(":%d", port),
-		Handler: handler,
-	}
-}
-
-type httpsServer struct {
-	*http.Server
-	certFile, keyFile string
-}
-
-func (hs httpsServer) ListenAndServe() error {
-	return hs.Server.ListenAndServeTLS(hs.certFile, hs.keyFile)
-}
-
-// NewHTTPS creates https server.
-func NewHTTPS(hs *http.Server, certFile, keyFile string) Server {
-	return httpsServer{Server: hs, certFile: certFile, keyFile: keyFile}
-}
-
-// Run runs servers.
-// This is typically invoked as the last statement in the server's main function.
-func Run(ctx context.Context, servers ...Server) {
-	ctx, cancel := context.WithCancel(ctx)
-	logger := log.FromContext(ctx)
-
-	// TODO: enable zpages here.
-	// zpages.Handle(http.DefaultServeMux, "/debug")
-	for _, s := range servers {
-		go func(s Server) {
-			defer cancel()
-			err := s.ListenAndServe()
-			if err == http.ErrServerClosed || err == nil {
-				logger.Infof("http server closed")
-				return
-			}
-			logger.Errorf("serve error: %v", err)
-		}(s)
-	}
-	// Wait SIGTERM from kubernetes.
-	signalCh := make(chan os.Signal, 1)
-	signal.Notify(signalCh, syscall.SIGTERM)
-
-	select {
-	case <-ctx.Done():
-	case sig := <-signalCh:
-		logger.Infof("catch signal: %s", sig)
-	}
-	cancel()
-	ctx = context.Background()
-	var wg sync.WaitGroup
-	for _, s := range servers {
-		wg.Add(1)
-		go func(s Server) {
-			defer wg.Done()
-			err := s.Shutdown(ctx)
-			if err != nil {
-				logger.Errorf("Shutdown server error: %v", err)
-			}
-		}(s)
-	}
-	wg.Wait()
-	Flush()
-	logger.Infof("server shutdown complete")
-	logger.Sync()
-	os.Exit(0)
-}