Repository: vmware-tanzu/octant
Pull request #1177 (2020-07-29 00:55:01)

Add stepper component.
Signed-off-by: Wayne Witzel III <wayne@riotousliving.com>

![image](https://user-images.githubusercontent.com/27317/88744268-dcab8200-d114-11ea-886a-db940fdbc5a0.png)

![image](https://user-images.githubusercontent.com/27317/88744296-f0ef7f00-d114-11ea-8abd-2860e4f5d98e.png)



Patch:
diff --git a/.github/workflows/nightly.yaml b/.github/workflows/nightly.yaml
index 45cffb26..9e771545 100644
--- a/.github/workflows/nightly.yaml
+++ b/.github/workflows/nightly.yaml
@@ -50,7 +50,7 @@ jobs:
           go generate ./web
           go generate ./pkg/plugin/plugin.go
       - name: Upload web/rice-box.go
-        uses: actions/upload-artifact@v2.1.2
+        uses: actions/upload-artifact@v2.1.1
         with:
           name: rice-web
           path: web/rice-box.go
diff --git a/.github/workflows/preflight-checks.yaml b/.github/workflows/preflight-checks.yaml
index a52ba38e..153c625d 100644
--- a/.github/workflows/preflight-checks.yaml
+++ b/.github/workflows/preflight-checks.yaml
@@ -145,7 +145,7 @@ jobs:
           ./build/octant version
       - name: Upload web/rice-box.go
         if: matrix.platform == 'ubuntu-latest'
-        uses: actions/upload-artifact@v2.1.2
+        uses: actions/upload-artifact@v2.1.1
         with:
           name: rice-web
           path: web/rice-box.go
diff --git a/changelogs/unreleased/1102-wwitzel3 b/changelogs/unreleased/1102-wwitzel3
new file mode 100644
index 00000000..b7ef3e54
--- /dev/null
+++ b/changelogs/unreleased/1102-wwitzel3
@@ -0,0 +1,1 @@
+Added stepper component.
diff --git a/changelogs/unreleased/1176-GuessWhoSamFoo b/changelogs/unreleased/1176-GuessWhoSamFoo
deleted file mode 100644
index 50ef8e0c..00000000
--- a/changelogs/unreleased/1176-GuessWhoSamFoo
+++ /dev/null
@@ -1,1 +0,0 @@
-Fixed bug where kubeconfigs specified by multiple file paths were not watched
diff --git a/go.mod b/go.mod
index f298629b..ca8f575c 100644
--- a/go.mod
+++ b/go.mod
@@ -35,15 +35,15 @@ require (
 	github.com/pkg/errors v0.9.1
 	github.com/skratchdot/open-golang v0.0.0-20190402232053-79abb63cd66e
 	github.com/soheilhy/cmux v0.1.4
-	github.com/spf13/afero v1.3.3
+	github.com/spf13/afero v1.3.2
 	github.com/spf13/cobra v0.0.7
-	github.com/spf13/viper v1.7.1
+	github.com/spf13/viper v1.7.0
 	github.com/stretchr/testify v1.6.1
 	go.opencensus.io v0.22.4
 	go.uber.org/zap v1.15.0
 	golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208
 	golang.org/x/tools v0.0.0-20200716134326-a8f9df4c9543
-	google.golang.org/grpc v1.31.0
+	google.golang.org/grpc v1.30.0
 	google.golang.org/grpc/examples v0.0.0-20200707005602-4258d12073b4 // indirect
 	k8s.io/api v0.19.0-alpha.3
 	k8s.io/apiextensions-apiserver v0.19.0-alpha.3
diff --git a/go.sum b/go.sum
index f99f868f..22a6afb1 100644
--- a/go.sum
+++ b/go.sum
@@ -463,8 +463,8 @@ github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4k
 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 h1:p5gZEKLYoL7wh8VrJesMaYeNxdEd1v3cb4irOk9zB54=
-github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4=
+github.com/spf13/afero v1.3.2 h1:GDarE4TJQI52kYSbSAmLiId1Elfj+xgSDqrUZxFhxlU=
+github.com/spf13/afero v1.3.2/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4=
 github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
 github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
 github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
@@ -480,8 +480,8 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
 github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
 github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
 github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
-github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk=
-github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
+github.com/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM=
+github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
 github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
 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=
@@ -784,8 +784,6 @@ google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8
 google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
 google.golang.org/grpc v1.30.0 h1:M5a8xTlYTxwMn5ZFkwhRabsygDY5G8TYLyQDBxJNAxE=
 google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
-google.golang.org/grpc v1.31.0 h1:T7P4R73V3SSDPhH7WW7ATbfViLtmamH0DKrP3f9AuDI=
-google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
 google.golang.org/grpc/examples v0.0.0-20200707005602-4258d12073b4 h1:aO5Yh7ygpoceTmlzR0dl6xC4LKYmrkLcW0MBGx2uyBg=
 google.golang.org/grpc/examples v0.0.0-20200707005602-4258d12073b4/go.mod h1:5j1uub0jRGhRiSghIlrThmBUgcgLXOVJQ/l1getT4uo=
 google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
@@ -827,7 +825,6 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
 gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
 gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
 gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
 honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
diff --git a/pkg/dash/dash.go b/pkg/dash/dash.go
index a1e23931..7b7ed0f8 100644
--- a/pkg/dash/dash.go
+++ b/pkg/dash/dash.go
@@ -12,14 +12,12 @@ import (
 	"net"
 	"net/http"
 	"os"
-	"path/filepath"
 	"strings"
 	"time"
 
 	"contrib.go.opencensus.io/exporter/jaeger"
 	"github.com/skratchdot/open-golang/open"
 	"github.com/soheilhy/cmux"
-	"github.com/spf13/afero"
 	"github.com/spf13/viper"
 	"go.opencensus.io/trace"
 
@@ -70,7 +68,6 @@ type Runner struct {
 	actionManager          *action.Manager
 	websocketClientManager *api.WebsocketClientManager
 	apiCreated             bool
-	fs                     afero.Fs
 }
 
 func NewRunner(ctx context.Context, logger log.Logger, options Options) (*Runner, error) {
@@ -99,9 +96,7 @@ func NewRunner(ctx context.Context, logger log.Logger, options Options) (*Runner
 	var apiService api.Service
 	var apiErr error
 
-	r.fs = afero.NewOsFs()
-
-	if options.KubeConfig, err = ValidateKubeConfig(logger, options.KubeConfig, r.fs); err == nil {
+	if err := findKubeConfig(logger, options.KubeConfig); err == nil {
 		apiService, pluginService, apiErr = r.initAPI(ctx, logger, options)
 		if apiErr != nil {
 			return nil, fmt.Errorf("failed to start service api: %w", apiErr)
@@ -141,8 +136,7 @@ func (r *Runner) Start(ctx context.Context, logger log.Logger, options Options,
 	if !r.apiCreated {
 		go func() {
 			if r.dash != nil {
-				var err error
-				if options.KubeConfig, err = ValidateKubeConfig(logger, options.KubeConfig, r.fs); err != nil {
+				if err := findKubeConfig(logger, options.KubeConfig); err != nil {
 					logger.Infof("waiting for kube config ...")
 					options.KubeConfig = <-kubeConfigPath
 				}
@@ -548,28 +542,13 @@ func enableOpenCensus() error {
 	return nil
 }
 
-// ValidateKubeConfig returns a valid file list of kube config(s)
-func ValidateKubeConfig(logger log.Logger, kubeConfig string, fs afero.Fs) (string, error) {
-	fileList := []string{}
-	paths := filepath.SplitList(kubeConfig)
-
-	for _, path := range paths {
-		exists, err := afero.Exists(fs, path)
-		if err != nil {
-			logger.Errorf("check path exists: %v", err)
-		}
-
-		if exists {
-			fileList = append(fileList, path)
-			continue
-		}
-		logger.Infof("cannot find kube config: %v", path)
-	}
-
-	if len(fileList) > 0 {
-		return strings.Join(fileList, string(filepath.ListSeparator)), nil
+// findKubeConfig looks for kube config from .kube or provided by user
+func findKubeConfig(logger log.Logger, kubeConfig string) error {
+	if _, err := os.Stat(kubeConfig); err == nil {
+		logger.Infof("using kube config: %v", kubeConfig)
+		return nil
 	}
-	return "", fmt.Errorf("no kubeconfig found")
+	return fmt.Errorf("no kubeconfig found")
 }
 
 func (r *Runner) startAPIService(ctx context.Context, logger log.Logger, options Options) {
diff --git a/pkg/view/component/base.go b/pkg/view/component/base.go
index 6d63886c..ffa4e786 100644
--- a/pkg/view/component/base.go
+++ b/pkg/view/component/base.go
@@ -34,6 +34,7 @@ const (
 	typeResourceViewer     = "resourceViewer"
 	typeSelectors          = "selectors"
 	typeSingleStat         = "singleStat"
+	typeStepper            = "stepper"
 	typeSummary            = "summary"
 	typeTable              = "table"
 	typeTerminal           = "terminal"
diff --git a/pkg/view/component/stepper.go b/pkg/view/component/stepper.go
new file mode 100644
index 00000000..80321884
--- /dev/null
+++ b/pkg/view/component/stepper.go
@@ -0,0 +1,50 @@
+/*
+Copyright (c) 2019 the Octant contributors. All Rights Reserved.
+SPDX-License-Identifier: Apache-2.0
+*/
+
+package component
+
+import "encoding/json"
+
+// Stepper component implements json.Marshaler
+type Stepper struct {
+	base
+	Config StepperConfig `json:"config"`
+}
+
+type stepperMarshal Stepper
+
+func (t *Stepper) MarshalJSON() ([]byte, error) {
+	m := stepperMarshal(*t)
+	m.Metadata.Type = typeStepper
+	return json.Marshal(&m)
+}
+
+type StepperConfig struct {
+	Action string       `json:"action"`
+	Steps  []StepConfig `json:"steps"`
+}
+
+func (t *StepperConfig) UnmarshalJSON(data []byte) error {
+	x := struct {
+		Action string
+		Steps  []StepConfig
+	}{}
+
+	if err := json.Unmarshal(data, &x); err != nil {
+		return err
+	}
+
+	t.Action = x.Action
+	t.Steps = x.Steps
+
+	return nil
+}
+
+type StepConfig struct {
+	Name        string `json:"name"`
+	Form        Form   `json:"form"`
+	Title       string `json:"title"`
+	Description string `json:"description"`
+}
diff --git a/pkg/view/component/unmarshal.go b/pkg/view/component/unmarshal.go
index 8b09dbf0..07de6c22 100644
--- a/pkg/view/component/unmarshal.go
+++ b/pkg/view/component/unmarshal.go
@@ -7,6 +7,7 @@ package component
 
 import (
 	"encoding/json"
+	"fmt"
 
 	"github.com/pkg/errors"
 )
@@ -136,6 +137,12 @@ func unmarshal(to TypedObject) (Component, error) {
 		err = errors.Wrapf(json.Unmarshal(to.Config, &t.Config),
 			"unmarshal singleStat config")
 		o = t
+	case typeStepper:
+		t := &Stepper{base: base{Metadata: to.Metadata}}
+		if uErr := json.Unmarshal(to.Config, &t.Config); uErr != nil {
+			err = fmt.Errorf("unmarshal stepper config: %w", uErr)
+		}
+		o = t
 	case typeSummary:
 		t := &Summary{base: base{Metadata: to.Metadata}}
 		err = errors.Wrapf(json.Unmarshal(to.Config, &t.Config),
diff --git a/vendor/github.com/spf13/afero/memmap.go b/vendor/github.com/spf13/afero/memmap.go
index bbcc2381..6be0e9c2 100644
--- a/vendor/github.com/spf13/afero/memmap.go
+++ b/vendor/github.com/spf13/afero/memmap.go
@@ -25,8 +25,6 @@ import (
 	"github.com/spf13/afero/mem"
 )
 
-const chmodBits = os.ModePerm | os.ModeSetuid | os.ModeSetgid | os.ModeSticky // Only a subset of bits are allowed to be changed. Documented under os.Chmod()
-
 type MemMapFs struct {
 	mu   sync.RWMutex
 	data map[string]*mem.FileData
@@ -42,9 +40,7 @@ func (m *MemMapFs) getData() map[string]*mem.FileData {
 		m.data = make(map[string]*mem.FileData)
 		// Root should always exist, right?
 		// TODO: what about windows?
-		root := mem.CreateDir(FilePathSeparator)
-		mem.SetMode(root, os.ModeDir|0755)
-		m.data[FilePathSeparator] = root
+		m.data[FilePathSeparator] = mem.CreateDir(FilePathSeparator)
 	})
 	return m.data
 }
@@ -56,7 +52,7 @@ func (m *MemMapFs) Create(name string) (File, error) {
 	m.mu.Lock()
 	file := mem.CreateFile(name)
 	m.getData()[name] = file
-	m.registerWithParent(file, 0)
+	m.registerWithParent(file)
 	m.mu.Unlock()
 	return mem.NewFileHandle(file), nil
 }
@@ -87,14 +83,14 @@ func (m *MemMapFs) findParent(f *mem.FileData) *mem.FileData {
 	return pfile
 }
 
-func (m *MemMapFs) registerWithParent(f *mem.FileData, perm os.FileMode) {
+func (m *MemMapFs) registerWithParent(f *mem.FileData) {
 	if f == nil {
 		return
 	}
 	parent := m.findParent(f)
 	if parent == nil {
 		pdir := filepath.Dir(filepath.Clean(f.Name()))
-		err := m.lockfreeMkdir(pdir, perm)
+		err := m.lockfreeMkdir(pdir, 0777)
 		if err != nil {
 			//log.Println("Mkdir error:", err)
 			return
@@ -123,15 +119,13 @@ func (m *MemMapFs) lockfreeMkdir(name string, perm os.FileMode) error {
 		}
 	} else {
 		item := mem.CreateDir(name)
-		mem.SetMode(item, os.ModeDir|perm)
 		m.getData()[name] = item
-		m.registerWithParent(item, perm)
+		m.registerWithParent(item)
 	}
 	return nil
 }
 
 func (m *MemMapFs) Mkdir(name string, perm os.FileMode) error {
-	perm &= chmodBits
 	name = normalizePath(name)
 
 	m.mu.RLock()
@@ -143,9 +137,8 @@ func (m *MemMapFs) Mkdir(name string, perm os.FileMode) error {
 
 	m.mu.Lock()
 	item := mem.CreateDir(name)
-	mem.SetMode(item, os.ModeDir|perm)
 	m.getData()[name] = item
-	m.registerWithParent(item, perm)
+	m.registerWithParent(item)
 	m.mu.Unlock()
 
 	return m.setFileMode(name, perm|os.ModeDir)
@@ -215,7 +208,6 @@ func (m *MemMapFs) lockfreeOpen(name string) (*mem.FileData, error) {
 }
 
 func (m *MemMapFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
-	perm &= chmodBits
 	chmod := false
 	file, err := m.openWrite(name)
 	if err == nil && (flag&os.O_EXCL > 0) {
@@ -308,7 +300,7 @@ func (m *MemMapFs) Rename(oldname, newname string) error {
 		delete(m.getData(), oldname)
 		mem.ChangeFileName(fileData, newname)
 		m.getData()[newname] = fileData
-		m.registerWithParent(fileData, 0)
+		m.registerWithParent(fileData)
 		m.mu.Unlock()
 		m.mu.RLock()
 	} else {
@@ -327,6 +319,7 @@ func (m *MemMapFs) Stat(name string) (os.FileInfo, error) {
 }
 
 func (m *MemMapFs) Chmod(name string, mode os.FileMode) error {
+	const chmodBits = os.ModePerm | os.ModeSetuid | os.ModeSetgid | os.ModeSticky // Only a subset of bits are allowed to be changed. Documented under os.Chmod()
 	mode &= chmodBits
 
 	m.mu.RLock()
diff --git a/vendor/github.com/spf13/viper/util.go b/vendor/github.com/spf13/viper/util.go
index cee6b242..b7889696 100644
--- a/vendor/github.com/spf13/viper/util.go
+++ b/vendor/github.com/spf13/viper/util.go
@@ -91,22 +91,13 @@ func insensitiviseMap(m map[string]interface{}) {
 func absPathify(inPath string) string {
 	jww.INFO.Println("Trying to resolve absolute path to", inPath)
 
-	if inPath == "$HOME" || strings.HasPrefix(inPath, "$HOME"+string(os.PathSeparator)) {
+	if strings.HasPrefix(inPath, "$HOME") {
 		inPath = userHomeDir() + inPath[5:]
 	}
 
 	if strings.HasPrefix(inPath, "$") {
 		end := strings.Index(inPath, string(os.PathSeparator))
-
-		var value, suffix string
-		if end == -1 {
-			value = os.Getenv(inPath[1:])
-		} else {
-			value = os.Getenv(inPath[1:end])
-			suffix = inPath[end:]
-		}
-
-		inPath = value + suffix
+		inPath = os.Getenv(inPath[1:end]) + inPath[end:]
 	}
 
 	if filepath.IsAbs(inPath) {
diff --git a/vendor/github.com/spf13/viper/viper.go b/vendor/github.com/spf13/viper/viper.go
index 405dc20f..f61f4ed7 100644
--- a/vendor/github.com/spf13/viper/viper.go
+++ b/vendor/github.com/spf13/viper/viper.go
@@ -896,7 +896,13 @@ func UnmarshalKey(key string, rawVal interface{}, opts ...DecoderConfigOption) e
 	return v.UnmarshalKey(key, rawVal, opts...)
 }
 func (v *Viper) UnmarshalKey(key string, rawVal interface{}, opts ...DecoderConfigOption) error {
-	return decode(v.Get(key), defaultDecoderConfig(rawVal, opts...))
+	err := decode(v.Get(key), defaultDecoderConfig(rawVal, opts...))
+
+	if err != nil {
+		return err
+	}
+
+	return nil
 }
 
 // Unmarshal unmarshals the config into a Struct. Make sure that the tags
@@ -905,7 +911,13 @@ func Unmarshal(rawVal interface{}, opts ...DecoderConfigOption) error {
 	return v.Unmarshal(rawVal, opts...)
 }
 func (v *Viper) Unmarshal(rawVal interface{}, opts ...DecoderConfigOption) error {
-	return decode(v.AllSettings(), defaultDecoderConfig(rawVal, opts...))
+	err := decode(v.AllSettings(), defaultDecoderConfig(rawVal, opts...))
+
+	if err != nil {
+		return err
+	}
+
+	return nil
 }
 
 // defaultDecoderConfig returns default mapsstructure.DecoderConfig with suppot
@@ -944,7 +956,13 @@ func (v *Viper) UnmarshalExact(rawVal interface{}, opts ...DecoderConfigOption)
 	config := defaultDecoderConfig(rawVal, opts...)
 	config.ErrorUnused = true
 
-	return decode(v.AllSettings(), config)
+	err := decode(v.AllSettings(), config)
+
+	if err != nil {
+		return err
+	}
+
+	return nil
 }
 
 // BindPFlags binds a full flag set to the configuration, using each flag's long
diff --git a/vendor/google.golang.org/grpc/README.md b/vendor/google.golang.org/grpc/README.md
index fef78e4a..249cd206 100644
--- a/vendor/google.golang.org/grpc/README.md
+++ b/vendor/google.golang.org/grpc/README.md
@@ -1,53 +1,65 @@
 # gRPC-Go
 
 [![Build Status](https://travis-ci.org/grpc/grpc-go.svg)](https://travis-ci.org/grpc/grpc-go)
-[![GoDoc](https://godoc.org/google.golang.org/grpc?status.svg)][API]
+[![GoDoc](https://godoc.org/google.golang.org/grpc?status.svg)](https://godoc.org/google.golang.org/grpc)
 [![GoReportCard](https://goreportcard.com/badge/grpc/grpc-go)](https://goreportcard.com/report/github.com/grpc/grpc-go)
 
-The [Go][] implementation of [gRPC][]: A high performance, open source, general
-RPC framework that puts mobile and HTTP/2 first. For more information see the
-[Go gRPC docs][], or jump directly into the [quick start][].
+The Go implementation of [gRPC](https://grpc.io/): A high performance, open
+source, general RPC framework that puts mobile and HTTP/2 first. For more
+information see the [gRPC Quick Start:
+Go](https://grpc.io/docs/languages/go/quickstart/) guide.
 
-## Prerequisites
+Installation
+------------
 
-- **[Go][]**: any one of the **three latest major** [releases][go-releases].
+To install this package, you need to install Go and setup your Go workspace on
+your computer. The simplest way to install the library is to run:
 
-## Installation
-
-With [Go module][] support (Go 1.11+), simply add the following import
-
-```go
-import "google.golang.org/grpc"
+```
+$ go get -u google.golang.org/grpc
 ```
 
-to your code, and then `go [build|run|test]` will automatically fetch the
-necessary dependencies.
+With Go module support (Go 1.11+), simply `import "google.golang.org/grpc"` in
+your source code and `go [build|run|test]` will automatically download the
+necessary dependencies ([Go modules
+ref](https://github.com/golang/go/wiki/Modules)).
 
-Otherwise, to install the `grpc-go` package, run the following command:
+If you are trying to access grpc-go from within China, please see the
+[FAQ](#FAQ) below.
 
-```console
-$ go get -u google.golang.org/grpc
-```
+Prerequisites
+-------------
+gRPC-Go officially supports the
+[three latest major releases of Go](https://golang.org/doc/devel/release.html).
 
-> **Note:** If you are trying to access `grpc-go` from **China**, see the
-> [FAQ](#FAQ) below.
+Documentation
+-------------
+- See [godoc](https://godoc.org/google.golang.org/grpc) for package and API
+  descriptions.
+- Documentation on specific topics can be found in the [Documentation
+  directory](Documentation/).
+- Examples can be found in the [examples directory](examples/).
 
-## Learn more
+Performance
+-----------
+Performance benchmark data for grpc-go and other languages is maintained in
+[this
+dashboard](https://performance-dot-grpc-testing.appspot.com/explore?dashboard=5652536396611584&widget=490377658&container=1286539696).
 
-- [Go gRPC docs][], which include a [quick start][] and [API
-  reference][API] among other resources
-- [Low-level technical docs](Documentation) from this repository
-- [Performance benchmark][]
-- [Examples](examples)
+Status
+------
+General Availability [Google Cloud Platform Launch
+Stages](https://cloud.google.com/terms/launch-stages).
 
-## FAQ
+FAQ
+---
 
-### I/O Timeout Errors
+#### I/O Timeout Errors
 
-The `golang.org` domain may be blocked from some countries. `go get` usually
+The `golang.org` domain may be blocked from some countries.  `go get` usually
 produces an error like the following when this happens:
 
-```console
+```
 $ go get -u google.golang.org/grpc
 package google.golang.org/grpc: unrecognized import path "google.golang.org/grpc" (https fetch: Get https://google.golang.org/grpc?go-get=1: dial tcp 216.239.37.1:443: i/o timeout)
 ```
@@ -58,7 +70,7 @@ To build Go code, there are several options:
 
 - Without Go module support: `git clone` the repo manually:
 
-  ```sh
+  ```
   git clone https://github.com/grpc/grpc-go.git $GOPATH/src/google.golang.org/grpc
   ```
 
@@ -68,7 +80,7 @@ To build Go code, there are several options:
 - With Go module support: it is possible to use the `replace` feature of `go
   mod` to create aliases for golang.org packages.  In your project's directory:
 
-  ```sh
+  ```
   go mod edit -replace=google.golang.org/grpc=github.com/grpc/grpc-go@latest
   go mod tidy
   go mod vendor
@@ -76,17 +88,19 @@ To build Go code, there are several options:
   ```
 
   Again, this will need to be done for all transitive dependencies hosted on
-  golang.org as well. For details, refer to [golang/go issue #28652](https://github.com/golang/go/issues/28652).
+  golang.org as well.  Please refer to [this
+  issue](https://github.com/golang/go/issues/28652) in the golang repo regarding
+  this concern.
 
-### Compiling error, undefined: grpc.SupportPackageIsVersion
+#### Compiling error, undefined: grpc.SupportPackageIsVersion
 
-#### If you are using Go modules:
+##### If you are using Go modules:
 
-Ensure your gRPC-Go version is `require`d at the appropriate version in
+Please ensure your gRPC-Go version is `require`d at the appropriate version in
 the same module containing the generated `.pb.go` files.  For example,
 `SupportPackageIsVersion6` needs `v1.27.0`, so in your `go.mod` file:
 
-```go
+```
 module <your module name>
 
 require (
@@ -94,27 +108,23 @@ require (
 )
 ```
 
-#### If you are *not* using Go modules:
+##### If you are *not* using Go modules:
 
-Update the `proto` package, gRPC package, and rebuild the `.proto` files:
+Please update proto package, gRPC package and rebuild the proto files:
+ - `go get -u github.com/golang/protobuf/{proto,protoc-gen-go}`
+ - `go get -u google.golang.org/grpc`
+ - `protoc --go_out=plugins=grpc:. *.proto`
 
-```sh
-go get -u github.com/golang/protobuf/{proto,protoc-gen-go}
-go get -u google.golang.org/grpc
-protoc --go_out=plugins=grpc:. *.proto
-```
+#### How to turn on logging
 
-### How to turn on logging
+The default logger is controlled by the environment variables. Turn everything
+on by setting:
 
-The default logger is controlled by environment variables. Turn everything on
-like this:
-
-```console
-$ export GRPC_GO_LOG_VERBOSITY_LEVEL=99
-$ export GRPC_GO_LOG_SEVERITY_LEVEL=info
+```
+GRPC_GO_LOG_VERBOSITY_LEVEL=99 GRPC_GO_LOG_SEVERITY_LEVEL=info
 ```
 
-### The RPC failed with error `"code = Unavailable desc = transport is closing"`
+#### The RPC failed with error `"code = Unavailable desc = transport is closing"`
 
 This error means the connection the RPC is using was closed, and there are many
 possible reasons, including:
@@ -130,12 +140,3 @@ It can be tricky to debug this because the error happens on the client side but
 the root cause of the connection being closed is on the server side. Turn on
 logging on __both client and server__, and see if there are any transport
 errors.
-
-[API]: https://grpc.io/docs/languages/go/api
-[Go]: https://golang.org
-[Go module]: https://github.com/golang/go/wiki/Modules
-[gRPC]: https://grpc.io
-[Go gRPC docs]: https://grpc.io/docs/languages/go
-[Performance benchmark]: https://performance-dot-grpc-testing.appspot.com/explore?dashboard=5652536396611584&widget=490377658&container=1286539696
-[quick start]: https://grpc.io/docs/languages/go/quickstart
-[go-releases]: https://golang.org/doc/devel/release.html
diff --git a/vendor/google.golang.org/grpc/balancer/balancer.go b/vendor/google.golang.org/grpc/balancer/balancer.go
index 8bf359db..e75b2843 100644
--- a/vendor/google.golang.org/grpc/balancer/balancer.go
+++ b/vendor/google.golang.org/grpc/balancer/balancer.go
@@ -312,6 +312,16 @@ type Balancer interface {
 	Close()
 }
 
+// V2Balancer is temporarily defined for backward compatibility reasons.
+//
+// Deprecated: use Balancer directly instead.
+type V2Balancer = Balancer
+
+// V2Picker is temporarily defined for backward compatibility reasons.
+//
+// Deprecated: use Picker directly instead.
+type V2Picker = Picker
+
 // SubConnState describes the state of a SubConn.
 type SubConnState struct {
 	// ConnectivityState is the connectivity state of the SubConn.
diff --git a/vendor/google.golang.org/grpc/balancer/base/balancer.go b/vendor/google.golang.org/grpc/balancer/base/balancer.go
index 32d782f1..d62b4b60 100644
--- a/vendor/google.golang.org/grpc/balancer/base/balancer.go
+++ b/vendor/google.golang.org/grpc/balancer/base/balancer.go
@@ -28,8 +28,6 @@ import (
 	"google.golang.org/grpc/resolver"
 )
 
-var logger = grpclog.Component("balancer")
-
 type baseBuilder struct {
 	name          string
 	pickerBuilder PickerBuilder
@@ -93,8 +91,8 @@ func (b *baseBalancer) ResolverError(err error) {
 
 func (b *baseBalancer) UpdateClientConnState(s balancer.ClientConnState) error {
 	// TODO: handle s.ResolverState.ServiceConfig?
-	if logger.V(2) {
-		logger.Info("base.baseBalancer: got new ClientConn state: ", s)
+	if grpclog.V(2) {
+		grpclog.Infoln("base.baseBalancer: got new ClientConn state: ", s)
 	}
 	// Successful resolution; clear resolver error and ensure we return nil.
 	b.resolverErr = nil
@@ -106,7 +104,7 @@ func (b *baseBalancer) UpdateClientConnState(s balancer.ClientConnState) error {
 			// a is a new address (not existing in b.subConns).
 			sc, err := b.cc.NewSubConn([]resolver.Address{a}, balancer.NewSubConnOptions{HealthCheckEnabled: b.config.HealthCheck})
 			if err != nil {
-				logger.Warningf("base.baseBalancer: failed to create new SubConn: %v", err)
+				grpclog.Warningf("base.baseBalancer: failed to create new SubConn: %v", err)
 				continue
 			}
 			b.subConns[a] = sc
@@ -170,13 +168,13 @@ func (b *baseBalancer) regeneratePicker() {
 
 func (b *baseBalancer) UpdateSubConnState(sc balancer.SubConn, state balancer.SubConnState) {
 	s := state.ConnectivityState
-	if logger.V(2) {
-		logger.Infof("base.baseBalancer: handle SubConn state change: %p, %v", sc, s)
+	if grpclog.V(2) {
+		grpclog.Infof("base.baseBalancer: handle SubConn state change: %p, %v", sc, s)
 	}
 	oldS, ok := b.scStates[sc]
 	if !ok {
-		if logger.V(2) {
-			logger.Infof("base.baseBalancer: got state changes for an unknown SubConn: %p, %v", sc, s)
+		if grpclog.V(2) {
+			grpclog.Infof("base.baseBalancer: got state changes for an unknown SubConn: %p, %v", sc, s)
 		}
 		return
 	}
diff --git a/vendor/google.golang.org/grpc/balancer/base/base.go b/vendor/google.golang.org/grpc/balancer/base/base.go
index e31d76e3..c4fc8911 100644
--- a/vendor/google.golang.org/grpc/balancer/base/base.go
+++ b/vendor/google.golang.org/grpc/balancer/base/base.go
@@ -69,3 +69,14 @@ func NewBalancerBuilder(name string, pb PickerBuilder, config Config) balancer.B
 		config:        config,
 	}
 }
+
+// NewBalancerBuilderV2 is temporarily defined for backward compatibility
+// reasons.
+//
+// Deprecated: use NewBalancerBuilder instead.
+var NewBalancerBuilderV2 = NewBalancerBuilder
+
+// V2PickerBuilder is temporarily defined for backward compatibility reasons.
+//
+// Deprecated: use PickerBuilder instead.
+type V2PickerBuilder = PickerBuilder
diff --git a/vendor/google.golang.org/grpc/balancer/roundrobin/roundrobin.go b/vendor/google.golang.org/grpc/balancer/roundrobin/roundrobin.go
index 43c2a153..a02b372c 100644
--- a/vendor/google.golang.org/grpc/balancer/roundrobin/roundrobin.go
+++ b/vendor/google.golang.org/grpc/balancer/roundrobin/roundrobin.go
@@ -33,8 +33,6 @@ import (
 // Name is the name of round_robin balancer.
 const Name = "round_robin"
 
-var logger = grpclog.Component("roundrobin")
-
 // newBuilder creates a new roundrobin balancer builder.
 func newBuilder() balancer.Builder {
 	return base.NewBalancerBuilder(Name, &rrPickerBuilder{}, base.Config{HealthCheck: true})
@@ -47,7 +45,7 @@ func init() {
 type rrPickerBuilder struct{}
 
 func (*rrPickerBuilder) Build(info base.PickerBuildInfo) balancer.Picker {
-	logger.Infof("roundrobinPicker: newPicker called with info: %v", info)
+	grpclog.Infof("roundrobinPicker: newPicker called with info: %v", info)
 	if len(info.ReadySCs) == 0 {
 		return base.NewErrPicker(balancer.ErrNoSubConnAvailable)
 	}
diff --git a/vendor/google.golang.org/grpc/balancer_conn_wrappers.go b/vendor/google.golang.org/grpc/balancer_conn_wrappers.go
index 11e592aa..807d1919 100644
--- a/vendor/google.golang.org/grpc/balancer_conn_wrappers.go
+++ b/vendor/google.golang.org/grpc/balancer_conn_wrappers.go
@@ -220,7 +220,7 @@ func (acbw *acBalancerWrapper) UpdateAddresses(addrs []resolver.Address) {
 
 		ac, err := cc.newAddrConn(addrs, opts)
 		if err != nil {
-			channelz.Warningf(logger, acbw.ac.channelzID, "acBalancerWrapper: UpdateAddresses: failed to newAddrConn: %v", err)
+			channelz.Warningf(acbw.ac.channelzID, "acBalancerWrapper: UpdateAddresses: failed to newAddrConn: %v", err)
 			return
 		}
 		acbw.ac = ac
diff --git a/vendor/google.golang.org/grpc/clientconn.go b/vendor/google.golang.org/grpc/clientconn.go
index ae5ce494..ef327e8a 100644
--- a/vendor/google.golang.org/grpc/clientconn.go
+++ b/vendor/google.golang.org/grpc/clientconn.go
@@ -149,7 +149,7 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *
 	if channelz.IsOn() {
 		if cc.dopts.channelzParentID != 0 {
 			cc.channelzID = channelz.RegisterChannel(&channelzChannel{cc}, cc.dopts.channelzParentID, target)
-			channelz.AddTraceEvent(logger, cc.channelzID, 0, &channelz.TraceEventDesc{
+			channelz.AddTraceEvent(cc.channelzID, 0, &channelz.TraceEventDesc{
 				Desc:     "Channel Created",
 				Severity: channelz.CtINFO,
 				Parent: &channelz.TraceEventDesc{
@@ -159,7 +159,7 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *
 			})
 		} else {
 			cc.channelzID = channelz.RegisterChannel(&channelzChannel{cc}, 0, target)
-			channelz.Info(logger, cc.channelzID, "Channel Created")
+			channelz.Info(cc.channelzID, "Channel Created")
 		}
 		cc.csMgr.channelzID = cc.channelzID
 	}
@@ -245,14 +245,13 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *
 
 	// Determine the resolver to use.
 	cc.parsedTarget = grpcutil.ParseTarget(cc.target)
-	unixScheme := strings.HasPrefix(cc.target, "unix:")
-	channelz.Infof(logger, cc.channelzID, "parsed scheme: %q", cc.parsedTarget.Scheme)
+	channelz.Infof(cc.channelzID, "parsed scheme: %q", cc.parsedTarget.Scheme)
 	resolverBuilder := cc.getResolver(cc.parsedTarget.Scheme)
 	if resolverBuilder == nil {
 		// If resolver builder is still nil, the parsed target's scheme is
 		// not registered. Fallback to default resolver and set Endpoint to
 		// the original target.
-		channelz.Infof(logger, cc.channelzID, "scheme %q not registered, fallback to default scheme", cc.parsedTarget.Scheme)
+		channelz.Infof(cc.channelzID, "scheme %q not registered, fallback to default scheme", cc.parsedTarget.Scheme)
 		cc.parsedTarget = resolver.Target{
 			Scheme:   resolver.GetDefaultScheme(),
 			Endpoint: target,
@@ -268,8 +267,6 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *
 		cc.authority = creds.Info().ServerName
 	} else if cc.dopts.insecure && cc.dopts.authority != "" {
 		cc.authority = cc.dopts.authority
-	} else if unixScheme {
-		cc.authority = "localhost"
 	} else {
 		// Use endpoint from "scheme://authority/endpoint" as the default
 		// authority for ClientConn.
@@ -425,7 +422,7 @@ func (csm *connectivityStateManager) updateState(state connectivity.State) {
 		return
 	}
 	csm.state = state
-	channelz.Infof(logger, csm.channelzID, "Channel Connectivity change to %v", state)
+	channelz.Infof(csm.channelzID, "Channel Connectivity change to %v", state)
 	if csm.notifyChan != nil {
 		// There are other goroutines waiting on this channel.
 		close(csm.notifyChan)
@@ -678,9 +675,9 @@ func (cc *ClientConn) switchBalancer(name string) {
 		return
 	}
 
-	channelz.Infof(logger, cc.channelzID, "ClientConn switching balancer to %q", name)
+	channelz.Infof(cc.channelzID, "ClientConn switching balancer to %q", name)
 	if cc.dopts.balancerBuilder != nil {
-		channelz.Info(logger, cc.channelzID, "ignoring balancer switching: Balancer DialOption used instead")
+		channelz.Info(cc.channelzID, "ignoring balancer switching: Balancer DialOption used instead")
 		return
 	}
 	if cc.balancerWrapper != nil {
@@ -689,11 +686,11 @@ func (cc *ClientConn) switchBalancer(name string) {
 
 	builder := balancer.Get(name)
 	if builder == nil {
-		channelz.Warningf(logger, cc.channelzID, "Channel switches to new LB policy %q due to fallback from invalid balancer name", PickFirstBalancerName)
-		channelz.Infof(logger, cc.channelzID, "failed to get balancer builder for: %v, using pick_first instead", name)
+		channelz.Warningf(cc.channelzID, "Channel switches to new LB policy %q due to fallback from invalid balancer name", PickFirstBalancerName)
+		channelz.Infof(cc.channelzID, "failed to get balancer builder for: %v, using pick_first instead", name)
 		builder = newPickfirstBuilder()
 	} else {
-		channelz.Infof(logger, cc.channelzID, "Channel switches to new LB policy %q", name)
+		channelz.Infof(cc.channelzID, "Channel switches to new LB policy %q", name)
 	}
 
 	cc.curBalancerName = builder.Name()
@@ -734,7 +731,7 @@ func (cc *ClientConn) newAddrConn(addrs []resolver.Address, opts balancer.NewSub
 	}
 	if channelz.IsOn() {
 		ac.channelzID = channelz.RegisterSubChannel(ac, cc.channelzID, "")
-		channelz.AddTraceEvent(logger, ac.channelzID, 0, &channelz.TraceEventDesc{
+		channelz.AddTraceEvent(ac.channelzID, 0, &channelz.TraceEventDesc{
 			Desc:     "Subchannel Created",
 			Severity: channelz.CtINFO,
 			Parent: &channelz.TraceEventDesc{
@@ -832,7 +829,7 @@ func (ac *addrConn) connect() error {
 func (ac *addrConn) tryUpdateAddrs(addrs []resolver.Address) bool {
 	ac.mu.Lock()
 	defer ac.mu.Unlock()
-	channelz.Infof(logger, ac.channelzID, "addrConn: tryUpdateAddrs curAddr: %v, addrs: %v", ac.curAddr, addrs)
+	channelz.Infof(ac.channelzID, "addrConn: tryUpdateAddrs curAddr: %v, addrs: %v", ac.curAddr, addrs)
 	if ac.state == connectivity.Shutdown ||
 		ac.state == connectivity.TransientFailure ||
 		ac.state == connectivity.Idle {
@@ -852,7 +849,7 @@ func (ac *addrConn) tryUpdateAddrs(addrs []resolver.Address) bool {
 			break
 		}
 	}
-	channelz.Infof(logger, ac.channelzID, "addrConn: tryUpdateAddrs curAddrFound: %v", curAddrFound)
+	channelz.Infof(ac.channelzID, "addrConn: tryUpdateAddrs curAddrFound: %v", curAddrFound)
 	if curAddrFound {
 		ac.addrs = addrs
 	}
@@ -863,10 +860,9 @@ func (ac *addrConn) tryUpdateAddrs(addrs []resolver.Address) bool {
 // GetMethodConfig gets the method config of the input method.
 // If there's an exact match for input method (i.e. /service/method), we return
 // the corresponding MethodConfig.
-// If there isn't an exact match for the input method, we look for the service's default
-// config under the service (i.e /service/) and then for the default for all services (empty string).
-//
-// If there is a default MethodConfig for the service, we return it.
+// If there isn't an exact match for the input method, we look for the default config
+// under the service (i.e /service/). If there is a default MethodConfig for
+// the service, we return it.
 // Otherwise, we return an empty MethodConfig.
 func (cc *ClientConn) GetMethodConfig(method string) MethodConfig {
 	// TODO: Avoid the locking here.
@@ -875,14 +871,12 @@ func (cc *ClientConn) GetMethodConfig(method string) MethodConfig {
 	if cc.sc == nil {
 		return MethodConfig{}
 	}
-	if m, ok := cc.sc.Methods[method]; ok {
-		return m
-	}
-	i := strings.LastIndex(method, "/")
-	if m, ok := cc.sc.Methods[method[:i+1]]; ok {
-		return m
+	m, ok := cc.sc.Methods[method]
+	if !ok {
+		i := strings.LastIndex(method, "/")
+		m = cc.sc.Methods[method[:i+1]]
 	}
-	return cc.sc.Methods[""]
+	return m
 }
 
 func (cc *ClientConn) healthCheckConfig() *healthCheckConfig {
@@ -1026,7 +1020,7 @@ func (cc *ClientConn) Close() error {
 				Severity: channelz.CtINFO,
 			}
 		}
-		channelz.AddTraceEvent(logger, cc.channelzID, 0, ted)
+		channelz.AddTraceEvent(cc.channelzID, 0, ted)
 		// TraceEvent needs to be called before RemoveEntry, as TraceEvent may add trace reference to
 		// the entity being deleted, and thus prevent it from being deleted right away.
 		channelz.RemoveEntry(cc.channelzID)
@@ -1070,7 +1064,7 @@ func (ac *addrConn) updateConnectivityState(s connectivity.State, lastErr error)
 		return
 	}
 	ac.state = s
-	channelz.Infof(logger, ac.channelzID, "Subchannel Connectivity change to %v", s)
+	channelz.Infof(ac.channelzID, "Subchannel Connectivity change to %v", s)
 	ac.cc.handleSubConnStateChange(ac.acbw, s, lastErr)
 }
 
@@ -1207,7 +1201,7 @@ func (ac *addrConn) tryAllAddrs(addrs []resolver.Address, connectDeadline time.T
 		}
 		ac.mu.Unlock()
 
-		channelz.Infof(logger, ac.channelzID, "Subchannel picks a new address %q to connect", addr.Addr)
+		channelz.Infof(ac.channelzID, "Subchannel picks a new address %q to connect", addr.Addr)
 
 		newTr, reconnect, err := ac.createTransport(addr, copts, connectDeadline)
 		if err == nil {
@@ -1282,7 +1276,7 @@ func (ac *addrConn) createTransport(addr resolver.Address, copts transport.Conne
 	newTr, err := transport.NewClientTransport(connectCtx, ac.cc.ctx, addr, copts, onPrefaceReceipt, onGoAway, onClose)
 	if err != nil {
 		// newTr is either nil, or closed.
-		channelz.Warningf(logger, ac.channelzID, "grpc: addrConn.createTransport failed to connect to %v. Err: %v. Reconnecting...", addr, err)
+		channelz.Warningf(ac.channelzID, "grpc: addrConn.createTransport failed to connect to %v. Err: %v. Reconnecting...", addr, err)
 		return nil, nil, err
 	}
 
@@ -1290,7 +1284,7 @@ func (ac *addrConn) createTransport(addr resolver.Address, copts transport.Conne
 	case <-time.After(time.Until(connectDeadline)):
 		// We didn't get the preface in time.
 		newTr.Close()
-		channelz.Warningf(logger, ac.channelzID, "grpc: addrConn.createTransport failed to connect to %v: didn't receive server preface in time. Reconnecting...", addr)
+		channelz.Warningf(ac.channelzID, "grpc: addrConn.createTransport failed to connect to %v: didn't receive server preface in time. Reconnecting...", addr)
 		return nil, nil, errors.New("timed out waiting for server handshake")
 	case <-prefaceReceived:
 		// We got the preface - huzzah! things are good.
@@ -1307,7 +1301,7 @@ func (ac *addrConn) createTransport(addr resolver.Address, copts transport.Conne
 //
 // LB channel health checking is enabled when all requirements below are met:
 // 1. it is not disabled by the user with the WithDisableHealthCheck DialOption
-// 2. internal.HealthCheckFunc is set by importing the grpc/health package
+// 2. internal.HealthCheckFunc is set by importing the grpc/healthcheck package
 // 3. a service config with non-empty healthCheckConfig field is provided
 // 4. the load balancer requests it
 //
@@ -1337,7 +1331,7 @@ func (ac *addrConn) startHealthCheck(ctx context.Context) {
 		// The health package is not imported to set health check function.
 		//
 		// TODO: add a link to the health check doc in the error message.
-		channelz.Error(logger, ac.channelzID, "Health check is requested but health check function is not set.")
+		channelz.Error(ac.channelzID, "Health check is requested but health check function is not set.")
 		return
 	}
 
@@ -1367,9 +1361,9 @@ func (ac *addrConn) startHealthCheck(ctx context.Context) {
 		err := ac.cc.dopts.healthCheckFunc(ctx, newStream, setConnectivityState, healthCheckConfig.ServiceName)
 		if err != nil {
 			if status.Code(err) == codes.Unimplemented {
-				channelz.Error(logger, ac.channelzID, "Subchannel health check is unimplemented at server side, thus health check is disabled")
+				channelz.Error(ac.channelzID, "Subchannel health check is unimplemented at server side, thus health check is disabled")
 			} else {
-				channelz.Errorf(logger, ac.channelzID, "HealthCheckFunc exits with unexpected error %v", err)
+				channelz.Errorf(ac.channelzID, "HealthCheckFunc exits with unexpected error %v", err)
 			}
 		}
 	}()
@@ -1434,7 +1428,7 @@ func (ac *addrConn) tearDown(err error) {
 		ac.mu.Lock()
 	}
 	if channelz.IsOn() {
-		channelz.AddTraceEvent(logger, ac.channelzID, 0, &channelz.TraceEventDesc{
+		channelz.AddTraceEvent(ac.channelzID, 0, &channelz.TraceEventDesc{
 			Desc:     "Subchannel Deleted",
 			Severity: channelz.CtINFO,
 			Parent: &channelz.TraceEventDesc{
diff --git a/vendor/google.golang.org/grpc/codes/codes.go b/vendor/google.golang.org/grpc/codes/codes.go
index 11b10618..02738839 100644
--- a/vendor/google.golang.org/grpc/codes/codes.go
+++ b/vendor/google.golang.org/grpc/codes/codes.go
@@ -33,9 +33,6 @@ const (
 	OK Code = 0
 
 	// Canceled indicates the operation was canceled (typically by the caller).
-	//
-	// The gRPC framework will generate this error code when cancellation
-	// is requested.
 	Canceled Code = 1
 
 	// Unknown error. An example of where this error may be returned is
@@ -43,17 +40,12 @@ const (
 	// an error-space that is not known in this address space. Also
 	// errors raised by APIs that do not return enough error information
 	// may be converted to this error.
-	//
-	// The gRPC framework will generate this error code in the above two
-	// mentioned cases.
 	Unknown Code = 2
 
 	// InvalidArgument indicates client specified an invalid argument.
 	// Note that this differs from FailedPrecondition. It indicates arguments
 	// that are problematic regardless of the state of the system
 	// (e.g., a malformed file name).
-	//
-	// This error code will not be generated by the gRPC framework.
 	InvalidArgument Code = 3
 
 	// DeadlineExceeded means operation expired before completion.
@@ -61,21 +53,14 @@ const (
 	// returned even if the operation has completed successfully. For
 	// example, a successful response from a server could have been delayed
 	// long enough for the deadline to expire.
-	//
-	// The gRPC framework will generate this error code when the deadline is
-	// exceeded.
 	DeadlineExceeded Code = 4
 
 	// NotFound means some requested entity (e.g., file or directory) was
 	// not found.
-	//
-	// This error code will not be generated by the gRPC framework.
 	NotFound Code = 5
 
 	// AlreadyExists means an attempt to create an entity failed because one
 	// already exists.
-	//
-	// This error code will not be generated by the gRPC framework.
 	AlreadyExists Code = 6
 
 	// PermissionDenied indicates the caller does not have permission to
@@ -84,17 +69,10 @@ const (
 	// instead for those errors). It must not be
 	// used if the caller cannot be identified (use Unauthenticated
 	// instead for those errors).
-	//
-	// This error code will not be generated by the gRPC core framework,
-	// but expect authentication middleware to use it.
 	PermissionDenied Code = 7
 
 	// ResourceExhausted indicates some resource has been exhausted, perhaps
 	// a per-user quota, or perhaps the entire file system is out of space.
-	//
-	// This error code will be generated by the gRPC framework in
-	// out-of-memory and server overload situations, or when a message is
-	// larger than the configured maximum size.
 	ResourceExhausted Code = 8
 
 	// FailedPrecondition indicates operation was rejected because the
@@ -116,8 +94,6 @@ const (
 	//      REST Get/Update/Delete on a resource and the resource on the
 	//      server does not match the condition. E.g., conflicting
 	//      read-modify-write on the same resource.
-	//
-	// This error code will not be generated by the gRPC framework.
 	FailedPrecondition Code = 9
 
 	// Aborted indicates the operation was aborted, typically due to a
@@ -126,8 +102,6 @@ const (
 	//
 	// See litmus test above for deciding between FailedPrecondition,
 	// Aborted, and Unavailable.
-	//
-	// This error code will not be generated by the gRPC framework.
 	Aborted Code = 10
 
 	// OutOfRange means operation was attempted past the valid range.
@@ -145,26 +119,15 @@ const (
 	// error) when it applies so that callers who are iterating through
 	// a space can easily look for an OutOfRange error to detect when
 	// they are done.
-	//
-	// This error code will not be generated by the gRPC framework.
 	OutOfRange Code = 11
 
 	// Unimplemented indicates operation is not implemented or not
 	// supported/enabled in this service.
-	//
-	// This error code will be generated by the gRPC framework. Most
-	// commonly, you will see this error code when a method implementation
-	// is missing on the server. It can also be generated for unknown
-	// compression algorithms or a disagreement as to whether an RPC should
-	// be streaming.
 	Unimplemented Code = 12
 
 	// Internal errors. Means some invariants expected by underlying
 	// system has been broken. If you see one of these errors,
 	// something is very broken.
-	//
-	// This error code will be generated by the gRPC framework in several
-	// internal error conditions.
 	Internal Code = 13
 
 	// Unavailable indicates the service is currently unavailable.
@@ -174,22 +137,13 @@ const (
 	//
 	// See litmus test above for deciding between FailedPrecondition,
 	// Aborted, and Unavailable.
-	//
-	// This error code will be generated by the gRPC framework during
-	// abrupt shutdown of a server process or network connection.
 	Unavailable Code = 14
 
 	// DataLoss indicates unrecoverable data loss or corruption.
-	//
-	// This error code will not be generated by the gRPC framework.
 	DataLoss Code = 15
 
 	// Unauthenticated indicates the request does not have valid
 	// authentication credentials for the operation.
-	//
-	// The gRPC framework will generate this error code when the
-	// authentication metadata is invalid or a Credentials callback fails,
-	// but also expect authentication middleware to generate it.
 	Unauthenticated Code = 16
 
 	_maxCode = 17
diff --git a/vendor/google.golang.org/grpc/credentials/tls.go b/vendor/google.golang.org/grpc/credentials/tls.go
index 1ba6f3a6..86e956bc 100644
--- a/vendor/google.golang.org/grpc/credentials/tls.go
+++ b/vendor/google.golang.org/grpc/credentials/tls.go
@@ -25,10 +25,8 @@ import (
 	"fmt"
 	"io/ioutil"
 	"net"
-	"net/url"
 
 	"google.golang.org/grpc/credentials/internal"
-	credinternal "google.golang.org/grpc/internal/credentials"
 )
 
 // TLSInfo contains the auth information for a TLS authenticated connection.
@@ -36,8 +34,6 @@ import (
 type TLSInfo struct {
 	State tls.ConnectionState
 	CommonAuthInfo
-	// This API is experimental.
-	SPIFFEID *url.URL
 }
 
 // AuthType returns the type of TLSInfo as a string.
@@ -98,17 +94,7 @@ func (c *tlsCreds) ClientHandshake(ctx context.Context, authority string, rawCon
 		conn.Close()
 		return nil, nil, ctx.Err()
 	}
-	tlsInfo := TLSInfo{
-		State: conn.ConnectionState(),
-		CommonAuthInfo: CommonAuthInfo{
-			SecurityLevel: PrivacyAndIntegrity,
-		},
-	}
-	id := credinternal.SPIFFEIDFromState(conn.ConnectionState())
-	if id != nil {
-		tlsInfo.SPIFFEID = id
-	}
-	return internal.WrapSyscallConn(rawConn, conn), tlsInfo, nil
+	return internal.WrapSyscallConn(rawConn, conn), TLSInfo{conn.ConnectionState(), CommonAuthInfo{PrivacyAndIntegrity}}, nil
 }
 
 func (c *tlsCreds) ServerHandshake(rawConn net.Conn) (net.Conn, AuthInfo, error) {
@@ -117,17 +103,7 @@ func (c *tlsCreds) ServerHandshake(rawConn net.Conn) (net.Conn, AuthInfo, error)
 		conn.Close()
 		return nil, nil, err
 	}
-	tlsInfo := TLSInfo{
-		State: conn.ConnectionState(),
-		CommonAuthInfo: CommonAuthInfo{
-			SecurityLevel: PrivacyAndIntegrity,
-		},
-	}
-	id := credinternal.SPIFFEIDFromState(conn.ConnectionState())
-	if id != nil {
-		tlsInfo.SPIFFEID = id
-	}
-	return internal.WrapSyscallConn(rawConn, conn), tlsInfo, nil
+	return internal.WrapSyscallConn(rawConn, conn), TLSInfo{conn.ConnectionState(), CommonAuthInfo{PrivacyAndIntegrity}}, nil
 }
 
 func (c *tlsCreds) Clone() TransportCredentials {
diff --git a/vendor/google.golang.org/grpc/dialoptions.go b/vendor/google.golang.org/grpc/dialoptions.go
index decb4c5e..d5030c07 100644
--- a/vendor/google.golang.org/grpc/dialoptions.go
+++ b/vendor/google.golang.org/grpc/dialoptions.go
@@ -27,6 +27,7 @@ import (
 	"google.golang.org/grpc/backoff"
 	"google.golang.org/grpc/balancer"
 	"google.golang.org/grpc/credentials"
+	"google.golang.org/grpc/grpclog"
 	"google.golang.org/grpc/internal"
 	internalbackoff "google.golang.org/grpc/internal/backoff"
 	"google.golang.org/grpc/internal/envconfig"
@@ -422,7 +423,7 @@ func WithUserAgent(s string) DialOption {
 // for the client transport.
 func WithKeepaliveParams(kp keepalive.ClientParameters) DialOption {
 	if kp.Time < internal.KeepaliveMinPingTime {
-		logger.Warningf("Adjusting keepalive ping interval to minimum period of %v", internal.KeepaliveMinPingTime)
+		grpclog.Warningf("Adjusting keepalive ping interval to minimum period of %v", internal.KeepaliveMinPingTime)
 		kp.Time = internal.KeepaliveMinPingTime
 	}
 	return newFuncDialOption(func(o *dialOptions) {
diff --git a/vendor/google.golang.org/grpc/grpclog/component.go b/vendor/google.golang.org/grpc/grpclog/component.go
deleted file mode 100644
index b513281a..00000000
--- a/vendor/google.golang.org/grpc/grpclog/component.go
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- *
- * Copyright 2020 gRPC authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package grpclog
-
-import (
-	"fmt"
-
-	"google.golang.org/grpc/internal/grpclog"
-)
-
-// componentData records the settings for a component.
-type componentData struct {
-	name string
-}
-
-var cache = map[string]*componentData{}
-
-func (c *componentData) InfoDepth(depth int, args ...interface{}) {
-	args = append([]interface{}{"[" + string(c.name) + "]"}, args...)
-	grpclog.InfoDepth(depth+1, args...)
-}
-
-func (c *componentData) WarningDepth(depth int, args ...interface{}) {
-	args = append([]interface{}{"[" + string(c.name) + "]"}, args...)
-	grpclog.WarningDepth(depth+1, args...)
-}
-
-func (c *componentData) ErrorDepth(depth int, args ...interface{}) {
-	args = append([]interface{}{"[" + string(c.name) + "]"}, args...)
-	grpclog.ErrorDepth(depth+1, args...)
-}
-
-func (c *componentData) FatalDepth(depth int, args ...interface{}) {
-	args = append([]interface{}{"[" + string(c.name) + "]"}, args...)
-	grpclog.FatalDepth(depth+1, args...)
-}
-
-func (c *componentData) Info(args ...interface{}) {
-	c.InfoDepth(1, args...)
-}
-
-func (c *componentData) Warning(args ...interface{}) {
-	c.WarningDepth(1, args...)
-}
-
-func (c *componentData) Error(args ...interface{}) {
-	c.ErrorDepth(1, args...)
-}
-
-func (c *componentData) Fatal(args ...interface{}) {
-	c.FatalDepth(1, args...)
-}
-
-func (c *componentData) Infof(format string, args ...interface{}) {
-	c.InfoDepth(1, fmt.Sprintf(format, args...))
-}
-
-func (c *componentData) Warningf(format string, args ...interface{}) {
-	c.WarningDepth(1, fmt.Sprintf(format, args...))
-}
-
-func (c *componentData) Errorf(format string, args ...interface{}) {
-	c.ErrorDepth(1, fmt.Sprintf(format, args...))
-}
-
-func (c *componentData) Fatalf(format string, args ...interface{}) {
-	c.FatalDepth(1, fmt.Sprintf(format, args...))
-}
-
-func (c *componentData) Infoln(args ...interface{}) {
-	c.InfoDepth(1, args...)
-}
-
-func (c *componentData) Warningln(args ...interface{}) {
-	c.WarningDepth(1, args...)
-}
-
-func (c *componentData) Errorln(args ...interface{}) {
-	c.ErrorDepth(1, args...)
-}
-
-func (c *componentData) Fatalln(args ...interface{}) {
-	c.FatalDepth(1, args...)
-}
-
-func (c *componentData) V(l int) bool {
-	return grpclog.Logger.V(l)
-}
-
-// Component creates a new component and returns it for logging. If a component
-// with the name already exists, nothing will be created and it will be
-// returned. SetLoggerV2 will panic if it is called with a logger created by
-// Component.
-func Component(componentName string) DepthLoggerV2 {
-	if cData, ok := cache[componentName]; ok {
-		return cData
-	}
-	c := &componentData{componentName}
-	cache[componentName] = c
-	return c
-}
diff --git a/vendor/google.golang.org/grpc/grpclog/loggerv2.go b/vendor/google.golang.org/grpc/grpclog/loggerv2.go
index 8eba2d0e..23612b7c 100644
--- a/vendor/google.golang.org/grpc/grpclog/loggerv2.go
+++ b/vendor/google.golang.org/grpc/grpclog/loggerv2.go
@@ -67,9 +67,6 @@ type LoggerV2 interface {
 // SetLoggerV2 sets logger that is used in grpc to a V2 logger.
 // Not mutex-protected, should be called before any gRPC functions.
 func SetLoggerV2(l LoggerV2) {
-	if _, ok := l.(*componentData); ok {
-		panic("cannot use component logger as grpclog logger")
-	}
 	grpclog.Logger = l
 	grpclog.DepthLogger, _ = l.(grpclog.DepthLoggerV2)
 }
@@ -206,7 +203,6 @@ func (g *loggerT) V(l int) bool {
 //
 // This API is EXPERIMENTAL.
 type DepthLoggerV2 interface {
-	LoggerV2
 	// InfoDepth logs to INFO log at the specified depth. Arguments are handled in the manner of fmt.Print.
 	InfoDepth(depth int, args ...interface{})
 	// WarningDepth logs to WARNING log at the specified depth. Arguments are handled in the manner of fmt.Print.
diff --git a/vendor/google.golang.org/grpc/internal/binarylog/binarylog.go b/vendor/google.golang.org/grpc/internal/binarylog/binarylog.go
index 5cc3aedd..b7a3dd8f 100644
--- a/vendor/google.golang.org/grpc/internal/binarylog/binarylog.go
+++ b/vendor/google.golang.org/grpc/internal/binarylog/binarylog.go
@@ -40,8 +40,6 @@ type Logger interface {
 // It is used to get a methodLogger for each individual method.
 var binLogger Logger
 
-var grpclogLogger = grpclog.Component("binarylog")
-
 // SetLogger sets the binarg logger.
 //
 // Only call this at init time.
@@ -151,7 +149,7 @@ func (l *logger) setBlacklist(method string) error {
 func (l *logger) getMethodLogger(methodName string) *MethodLogger {
 	s, m, err := grpcutil.ParseMethod(methodName)
 	if err != nil {
-		grpclogLogger.Infof("binarylogging: failed to parse %q: %v", methodName, err)
+		grpclog.Infof("binarylogging: failed to parse %q: %v", methodName, err)
 		return nil
 	}
 	if ml, ok := l.methods[s+"/"+m]; ok {
diff --git a/vendor/google.golang.org/grpc/internal/binarylog/env_config.go b/vendor/google.golang.org/grpc/internal/binarylog/env_config.go
index d8f4e760..be30d0e6 100644
--- a/vendor/google.golang.org/grpc/internal/binarylog/env_config.go
+++ b/vendor/google.golang.org/grpc/internal/binarylog/env_config.go
@@ -24,6 +24,8 @@ import (
 	"regexp"
 	"strconv"
 	"strings"
+
+	"google.golang.org/grpc/grpclog"
 )
 
 // NewLoggerFromConfigString reads the string and build a logger. It can be used
@@ -50,7 +52,7 @@ func NewLoggerFromConfigString(s string) Logger {
 	methods := strings.Split(s, ",")
 	for _, method := range methods {
 		if err := l.fillMethodLoggerWithConfigString(method); err != nil {
-			grpclogLogger.Warningf("failed to parse binary log config: %v", err)
+			grpclog.Warningf("failed to parse binary log config: %v", err)
 			return nil
 		}
 	}
diff --git a/vendor/google.golang.org/grpc/internal/binarylog/method_logger.go b/vendor/google.golang.org/grpc/internal/binarylog/method_logger.go
index 5e108353..160f6e86 100644
--- a/vendor/google.golang.org/grpc/internal/binarylog/method_logger.go
+++ b/vendor/google.golang.org/grpc/internal/binarylog/method_logger.go
@@ -27,6 +27,7 @@ import (
 	"github.com/golang/protobuf/proto"
 	"github.com/golang/protobuf/ptypes"
 	pb "google.golang.org/grpc/binarylog/grpc_binarylog_v1"
+	"google.golang.org/grpc/grpclog"
 	"google.golang.org/grpc/metadata"
 	"google.golang.org/grpc/status"
 )
@@ -218,12 +219,12 @@ func (c *ClientMessage) toProto() *pb.GrpcLogEntry {
 	if m, ok := c.Message.(proto.Message); ok {
 		data, err = proto.Marshal(m)
 		if err != nil {
-			grpclogLogger.Infof("binarylogging: failed to marshal proto message: %v", err)
+			grpclog.Infof("binarylogging: failed to marshal proto message: %v", err)
 		}
 	} else if b, ok := c.Message.([]byte); ok {
 		data = b
 	} else {
-		grpclogLogger.Infof("binarylogging: message to log is neither proto.message nor []byte")
+		grpclog.Infof("binarylogging: message to log is neither proto.message nor []byte")
 	}
 	ret := &pb.GrpcLogEntry{
 		Type: pb.GrpcLogEntry_EVENT_TYPE_CLIENT_MESSAGE,
@@ -258,12 +259,12 @@ func (c *ServerMessage) toProto() *pb.GrpcLogEntry {
 	if m, ok := c.Message.(proto.Message); ok {
 		data, err = proto.Marshal(m)
 		if err != nil {
-			grpclogLogger.Infof("binarylogging: failed to marshal proto message: %v", err)
+			grpclog.Infof("binarylogging: failed to marshal proto message: %v", err)
 		}
 	} else if b, ok := c.Message.([]byte); ok {
 		data = b
 	} else {
-		grpclogLogger.Infof("binarylogging: message to log is neither proto.message nor []byte")
+		grpclog.Infof("binarylogging: message to log is neither proto.message nor []byte")
 	}
 	ret := &pb.GrpcLogEntry{
 		Type: pb.GrpcLogEntry_EVENT_TYPE_SERVER_MESSAGE,
@@ -314,7 +315,7 @@ type ServerTrailer struct {
 func (c *ServerTrailer) toProto() *pb.GrpcLogEntry {
 	st, ok := status.FromError(c.Err)
 	if !ok {
-		grpclogLogger.Info("binarylogging: error in trailer is not a status error")
+		grpclog.Info("binarylogging: error in trailer is not a status error")
 	}
 	var (
 		detailsBytes []byte
@@ -324,7 +325,7 @@ func (c *ServerTrailer) toProto() *pb.GrpcLogEntry {
 	if stProto != nil && len(stProto.Details) != 0 {
 		detailsBytes, err = proto.Marshal(stProto)
 		if err != nil {
-			grpclogLogger.Infof("binarylogging: failed to marshal status proto: %v", err)
+			grpclog.Infof("binarylogging: failed to marshal status proto: %v", err)
 		}
 	}
 	ret := &pb.GrpcLogEntry{
diff --git a/vendor/google.golang.org/grpc/internal/binarylog/sink.go b/vendor/google.golang.org/grpc/internal/binarylog/sink.go
index 835f5104..a2e7c346 100644
--- a/vendor/google.golang.org/grpc/internal/binarylog/sink.go
+++ b/vendor/google.golang.org/grpc/internal/binarylog/sink.go
@@ -29,6 +29,7 @@ import (
 
 	"github.com/golang/protobuf/proto"
 	pb "google.golang.org/grpc/binarylog/grpc_binarylog_v1"
+	"google.golang.org/grpc/grpclog"
 )
 
 var (
@@ -77,7 +78,7 @@ type writerSink struct {
 func (ws *writerSink) Write(e *pb.GrpcLogEntry) error {
 	b, err := proto.Marshal(e)
 	if err != nil {
-		grpclogLogger.Infof("binary logging: failed to marshal proto message: %v", err)
+		grpclog.Infof("binary logging: failed to marshal proto message: %v", err)
 	}
 	hdr := make([]byte, 4)
 	binary.BigEndian.PutUint32(hdr, uint32(len(b)))
diff --git a/vendor/google.golang.org/grpc/internal/channelz/funcs.go b/vendor/google.golang.org/grpc/internal/channelz/funcs.go
index 81d3dd33..e4252e5b 100644
--- a/vendor/google.golang.org/grpc/internal/channelz/funcs.go
+++ b/vendor/google.golang.org/grpc/internal/channelz/funcs.go
@@ -30,7 +30,7 @@ import (
 	"sync/atomic"
 	"time"
 
-	"google.golang.org/grpc/grpclog"
+	"google.golang.org/grpc/internal/grpclog"
 )
 
 const (
@@ -216,7 +216,7 @@ func RegisterChannel(c Channel, pid int64, ref string) int64 {
 // by pid). It returns the unique channelz tracking id assigned to this subchannel.
 func RegisterSubChannel(c Channel, pid int64, ref string) int64 {
 	if pid == 0 {
-		logger.Error("a SubChannel's parent id cannot be 0")
+		grpclog.ErrorDepth(0, "a SubChannel's parent id cannot be 0")
 		return 0
 	}
 	id := idGen.genID()
@@ -253,7 +253,7 @@ func RegisterServer(s Server, ref string) int64 {
 // this listen socket.
 func RegisterListenSocket(s Socket, pid int64, ref string) int64 {
 	if pid == 0 {
-		logger.Error("a ListenSocket's parent id cannot be 0")
+		grpclog.ErrorDepth(0, "a ListenSocket's parent id cannot be 0")
 		return 0
 	}
 	id := idGen.genID()
@@ -268,7 +268,7 @@ func RegisterListenSocket(s Socket, pid int64, ref string) int64 {
 // this normal socket.
 func RegisterNormalSocket(s Socket, pid int64, ref string) int64 {
 	if pid == 0 {
-		logger.Error("a NormalSocket's parent id cannot be 0")
+		grpclog.ErrorDepth(0, "a NormalSocket's parent id cannot be 0")
 		return 0
 	}
 	id := idGen.genID()
@@ -294,17 +294,17 @@ type TraceEventDesc struct {
 }
 
 // AddTraceEvent adds trace related to the entity with specified id, using the provided TraceEventDesc.
-func AddTraceEvent(l grpclog.DepthLoggerV2, id int64, depth int, desc *TraceEventDesc) {
+func AddTraceEvent(id int64, depth int, desc *TraceEventDesc) {
 	for d := desc; d != nil; d = d.Parent {
 		switch d.Severity {
 		case CtUNKNOWN:
-			l.InfoDepth(depth+1, d.Desc)
+			grpclog.InfoDepth(depth+1, d.Desc)
 		case CtINFO:
-			l.InfoDepth(depth+1, d.Desc)
+			grpclog.InfoDepth(depth+1, d.Desc)
 		case CtWarning:
-			l.WarningDepth(depth+1, d.Desc)
+			grpclog.WarningDepth(depth+1, d.Desc)
 		case CtError:
-			l.ErrorDepth(depth+1, d.Desc)
+			grpclog.ErrorDepth(depth+1, d.Desc)
 		}
 	}
 	if getMaxTraceEntry() == 0 {
diff --git a/vendor/google.golang.org/grpc/internal/channelz/logging.go b/vendor/google.golang.org/grpc/internal/channelz/logging.go
index e94039ee..59c7bede 100644
--- a/vendor/google.golang.org/grpc/internal/channelz/logging.go
+++ b/vendor/google.golang.org/grpc/internal/channelz/logging.go
@@ -21,82 +21,80 @@ package channelz
 import (
 	"fmt"
 
-	"google.golang.org/grpc/grpclog"
+	"google.golang.org/grpc/internal/grpclog"
 )
 
-var logger = grpclog.Component("channelz")
-
-// Info logs and adds a trace event if channelz is on.
-func Info(l grpclog.DepthLoggerV2, id int64, args ...interface{}) {
+// Info logs through grpclog.Info and adds a trace event if channelz is on.
+func Info(id int64, args ...interface{}) {
 	if IsOn() {
-		AddTraceEvent(l, id, 1, &TraceEventDesc{
+		AddTraceEvent(id, 1, &TraceEventDesc{
 			Desc:     fmt.Sprint(args...),
 			Severity: CtINFO,
 		})
 	} else {
-		l.InfoDepth(1, args...)
+		grpclog.InfoDepth(1, args...)
 	}
 }
 
-// Infof logs and adds a trace event if channelz is on.
-func Infof(l grpclog.DepthLoggerV2, id int64, format string, args ...interface{}) {
+// Infof logs through grpclog.Infof and adds a trace event if channelz is on.
+func Infof(id int64, format string, args ...interface{}) {
 	msg := fmt.Sprintf(format, args...)
 	if IsOn() {
-		AddTraceEvent(l, id, 1, &TraceEventDesc{
+		AddTraceEvent(id, 1, &TraceEventDesc{
 			Desc:     msg,
 			Severity: CtINFO,
 		})
 	} else {
-		l.InfoDepth(1, msg)
+		grpclog.InfoDepth(1, msg)
 	}
 }
 
-// Warning logs and adds a trace event if channelz is on.
-func Warning(l grpclog.DepthLoggerV2, id int64, args ...interface{}) {
+// Warning logs through grpclog.Warning and adds a trace event if channelz is on.
+func Warning(id int64, args ...interface{}) {
 	if IsOn() {
-		AddTraceEvent(l, id, 1, &TraceEventDesc{
+		AddTraceEvent(id, 1, &TraceEventDesc{
 			Desc:     fmt.Sprint(args...),
 			Severity: CtWarning,
 		})
 	} else {
-		l.WarningDepth(1, args...)
+		grpclog.WarningDepth(1, args...)
 	}
 }
 
-// Warningf logs and adds a trace event if channelz is on.
-func Warningf(l grpclog.DepthLoggerV2, id int64, format string, args ...interface{}) {
+// Warningf logs through grpclog.Warningf and adds a trace event if channelz is on.
+func Warningf(id int64, format string, args ...interface{}) {
 	msg := fmt.Sprintf(format, args...)
 	if IsOn() {
-		AddTraceEvent(l, id, 1, &TraceEventDesc{
+		AddTraceEvent(id, 1, &TraceEventDesc{
 			Desc:     msg,
 			Severity: CtWarning,
 		})
 	} else {
-		l.WarningDepth(1, msg)
+		grpclog.WarningDepth(1, msg)
 	}
 }
 
-// Error logs and adds a trace event if channelz is on.
-func Error(l grpclog.DepthLoggerV2, id int64, args ...interface{}) {
+// Error logs through grpclog.Error and adds a trace event if channelz is on.
+func Error(id int64, args ...interface{}) {
 	if IsOn() {
-		AddTraceEvent(l, id, 1, &TraceEventDesc{
+		AddTraceEvent(id, 1, &TraceEventDesc{
 			Desc:     fmt.Sprint(args...),
 			Severity: CtError,
 		})
 	} else {
-		l.ErrorDepth(1, args...)
+		grpclog.ErrorDepth(1, args...)
 	}
 }
 
-// Errorf logs and adds a trace event if channelz is on.
-func Errorf(l grpclog.DepthLoggerV2, id int64, format string, args ...interface{}) {
+// Errorf logs through grpclog.Errorf and adds a trace event if channelz is on.
+func Errorf(id int64, format string, args ...interface{}) {
 	msg := fmt.Sprintf(format, args...)
 	if IsOn() {
-		AddTraceEvent(l, id, 1, &TraceEventDesc{
+		AddTraceEvent(id, 1, &TraceEventDesc{
 			Desc:     msg,
 			Severity: CtError,
 		})
 	} else {
-		l.ErrorDepth(1, msg)
+		grpclog.ErrorDepth(1, msg)
 	}
 }
diff --git a/vendor/google.golang.org/grpc/internal/channelz/types.go b/vendor/google.golang.org/grpc/internal/channelz/types.go
index 075dc7d1..17c2274c 100644
--- a/vendor/google.golang.org/grpc/internal/channelz/types.go
+++ b/vendor/google.golang.org/grpc/internal/channelz/types.go
@@ -26,6 +26,7 @@ import (
 
 	"google.golang.org/grpc/connectivity"
 	"google.golang.org/grpc/credentials"
+	"google.golang.org/grpc/grpclog"
 )
 
 // entry represents a node in the channelz database.
@@ -59,17 +60,17 @@ func (d *dummyEntry) addChild(id int64, e entry) {
 	// the addrConn will create a new transport. And when registering the new transport in
 	// channelz, its parent addrConn could have already been torn down and deleted
 	// from channelz tracking, and thus reach the code here.
-	logger.Infof("attempt to add child of type %T with id %d to a parent (id=%d) that doesn't currently exist", e, id, d.idNotFound)
+	grpclog.Infof("attempt to add child of type %T with id %d to a parent (id=%d) that doesn't currently exist", e, id, d.idNotFound)
 }
 
 func (d *dummyEntry) deleteChild(id int64) {
 	// It is possible for a normal program to reach here under race condition.
 	// Refer to the example described in addChild().
-	logger.Infof("attempt to delete child with id %d from a parent (id=%d) that doesn't currently exist", id, d.idNotFound)
+	grpclog.Infof("attempt to delete child with id %d from a parent (id=%d) that doesn't currently exist", id, d.idNotFound)
 }
 
 func (d *dummyEntry) triggerDelete() {
-	logger.Warningf("attempt to delete an entry (id=%d) that doesn't currently exist", d.idNotFound)
+	grpclog.Warningf("attempt to delete an entry (id=%d) that doesn't currently exist", d.idNotFound)
 }
 
 func (*dummyEntry) deleteSelfIfReady() {
@@ -214,7 +215,7 @@ func (c *channel) addChild(id int64, e entry) {
 	case *channel:
 		c.nestedChans[id] = v.refName
 	default:
-		logger.Errorf("cannot add a child (id = %d) of type %T to a channel", id, e)
+		grpclog.Errorf("cannot add a child (id = %d) of type %T to a channel", id, e)
 	}
 }
 
@@ -325,7 +326,7 @@ func (sc *subChannel) addChild(id int64, e entry) {
 	if v, ok := e.(*normalSocket); ok {
 		sc.sockets[id] = v.refName
 	} else {
-		logger.Errorf("cannot add a child (id = %d) of type %T to a subChannel", id, e)
+		grpclog.Errorf("cannot add a child (id = %d) of type %T to a subChannel", id, e)
 	}
 }
 
@@ -492,11 +493,11 @@ type listenSocket struct {
 }
 
 func (ls *listenSocket) addChild(id int64, e entry) {
-	logger.Errorf("cannot add a child (id = %d) of type %T to a listen socket", id, e)
+	grpclog.Errorf("cannot add a child (id = %d) of type %T to a listen socket", id, e)
 }
 
 func (ls *listenSocket) deleteChild(id int64) {
-	logger.Errorf("cannot delete a child (id = %d) from a listen socket", id)
+	grpclog.Errorf("cannot delete a child (id = %d) from a listen socket", id)
 }
 
 func (ls *listenSocket) triggerDelete() {
@@ -505,7 +506,7 @@ func (ls *listenSocket) triggerDelete() {
 }
 
 func (ls *listenSocket) deleteSelfIfReady() {
-	logger.Errorf("cannot call deleteSelfIfReady on a listen socket")
+	grpclog.Errorf("cannot call deleteSelfIfReady on a listen socket")
 }
 
 func (ls *listenSocket) getParentID() int64 {
@@ -521,11 +522,11 @@ type normalSocket struct {
 }
 
 func (ns *normalSocket) addChild(id int64, e entry) {
-	logger.Errorf("cannot add a child (id = %d) of type %T to a normal socket", id, e)
+	grpclog.Errorf("cannot add a child (id = %d) of type %T to a normal socket", id, e)
 }
 
 func (ns *normalSocket) deleteChild(id int64) {
-	logger.Errorf("cannot delete a child (id = %d) from a normal socket", id)
+	grpclog.Errorf("cannot delete a child (id = %d) from a normal socket", id)
 }
 
 func (ns *normalSocket) triggerDelete() {
@@ -534,7 +535,7 @@ func (ns *normalSocket) triggerDelete() {
 }
 
 func (ns *normalSocket) deleteSelfIfReady() {
-	logger.Errorf("cannot call deleteSelfIfReady on a normal socket")
+	grpclog.Errorf("cannot call deleteSelfIfReady on a normal socket")
 }
 
 func (ns *normalSocket) getParentID() int64 {
@@ -593,7 +594,7 @@ func (s *server) addChild(id int64, e entry) {
 	case *listenSocket:
 		s.listenSockets[id] = v.refName
 	default:
-		logger.Errorf("cannot add a child (id = %d) of type %T to a server", id, e)
+		grpclog.Errorf("cannot add a child (id = %d) of type %T to a server", id, e)
 	}
 }
 
diff --git a/vendor/google.golang.org/grpc/internal/channelz/types_nonlinux.go b/vendor/google.golang.org/grpc/internal/channelz/types_nonlinux.go
index 19c2fc52..79edbefc 100644
--- a/vendor/google.golang.org/grpc/internal/channelz/types_nonlinux.go
+++ b/vendor/google.golang.org/grpc/internal/channelz/types_nonlinux.go
@@ -22,6 +22,8 @@ package channelz
 
 import (
 	"sync"
+
+	"google.golang.org/grpc/grpclog"
 )
 
 var once sync.Once
@@ -37,6 +39,6 @@ type SocketOptionData struct {
 // Windows OS doesn't support Socket Option
 func (s *SocketOptionData) Getsockopt(fd uintptr) {
 	once.Do(func() {
-		logger.Warning("Channelz: socket options are not supported on non-linux os and appengine.")
+		grpclog.Warningln("Channelz: socket options are not supported on non-linux os and appengine.")
 	})
 }
diff --git a/vendor/google.golang.org/grpc/internal/credentials/go110.go b/vendor/google.golang.org/grpc/internal/credentials/go110.go
deleted file mode 100644
index d55b5203..00000000
--- a/vendor/google.golang.org/grpc/internal/credentials/go110.go
+++ /dev/null
@@ -1,65 +0,0 @@
-// +build go1.10
-
-/*
- *
- * Copyright 2020 gRPC authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-// Package credentials defines APIs for parsing SPIFFE ID.
-//
-// All APIs in this package are experimental.
-package credentials
-
-import (
-	"crypto/tls"
-	"net/url"
-
-	"google.golang.org/grpc/grpclog"
-)
-
-// SPIFFEIDFromState parses the SPIFFE ID from State. If the SPIFFE ID format
-// is invalid, return nil with warning.
-func SPIFFEIDFromState(state tls.ConnectionState) *url.URL {
-	if len(state.PeerCertificates) == 0 || len(state.PeerCertificates[0].URIs) == 0 {
-		return nil
-	}
-	var spiffeID *url.URL
-	for _, uri := range state.PeerCertificates[0].URIs {
-		if uri == nil || uri.Scheme != "spiffe" || uri.Opaque != "" || (uri.User != nil && uri.User.Username() != "") {
-			continue
-		}
-		// From this point, we assume the uri is intended for a SPIFFE ID.
-		if len(uri.String()) > 2048 {
-			grpclog.Warning("invalid SPIFFE ID: total ID length larger than 2048 bytes")
-			return nil
-		}
-		if len(uri.Host) == 0 || len(uri.RawPath) == 0 || len(uri.Path) == 0 {
-			grpclog.Warning("invalid SPIFFE ID: domain or workload ID is empty")
-			return nil
-		}
-		if len(uri.Host) > 255 {
-			grpclog.Warning("invalid SPIFFE ID: domain length larger than 255 characters")
-			return nil
-		}
-		// A valid SPIFFE certificate can only have exactly one URI SAN field.
-		if len(state.PeerCertificates[0].URIs) > 1 {
-			grpclog.Warning("invalid SPIFFE ID: multiple URI SANs")
-			return nil
-		}
-		spiffeID = uri
-	}
-	return spiffeID
-}
diff --git a/vendor/google.golang.org/grpc/internal/grpclog/grpclog.go b/vendor/google.golang.org/grpc/internal/grpclog/grpclog.go
index 745a166f..8c8e19fc 100644
--- a/vendor/google.golang.org/grpc/internal/grpclog/grpclog.go
+++ b/vendor/google.golang.org/grpc/internal/grpclog/grpclog.go
@@ -19,10 +19,6 @@
 // Package grpclog (internal) defines depth logging for grpc.
 package grpclog
 
-import (
-	"os"
-)
-
 // Logger is the logger used for the non-depth log functions.
 var Logger LoggerV2
 
@@ -34,7 +30,7 @@ func InfoDepth(depth int, args ...interface{}) {
 	if DepthLogger != nil {
 		DepthLogger.InfoDepth(depth, args...)
 	} else {
-		Logger.Infoln(args...)
+		Logger.Info(args...)
 	}
 }
 
@@ -43,7 +39,7 @@ func WarningDepth(depth int, args ...interface{}) {
 	if DepthLogger != nil {
 		DepthLogger.WarningDepth(depth, args...)
 	} else {
-		Logger.Warningln(args...)
+		Logger.Warning(args...)
 	}
 }
 
@@ -52,7 +48,7 @@ func ErrorDepth(depth int, args ...interface{}) {
 	if DepthLogger != nil {
 		DepthLogger.ErrorDepth(depth, args...)
 	} else {
-		Logger.Errorln(args...)
+		Logger.Error(args...)
 	}
 }
 
@@ -61,9 +57,8 @@ func FatalDepth(depth int, args ...interface{}) {
 	if DepthLogger != nil {
 		DepthLogger.FatalDepth(depth, args...)
 	} else {
-		Logger.Fatalln(args...)
+		Logger.Fatal(args...)
 	}
-	os.Exit(1)
 }
 
 // LoggerV2 does underlying logging work for grpclog.
diff --git a/vendor/google.golang.org/grpc/internal/grpclog/prefixLogger.go b/vendor/google.golang.org/grpc/internal/grpclog/prefixLogger.go
index 82af70e9..f6e0dc1d 100644
--- a/vendor/google.golang.org/grpc/internal/grpclog/prefixLogger.go
+++ b/vendor/google.golang.org/grpc/internal/grpclog/prefixLogger.go
@@ -18,15 +18,10 @@
 
 package grpclog
 
-import (
-	"fmt"
-)
-
 // PrefixLogger does logging with a prefix.
 //
 // Logging method on a nil logs without any prefix.
 type PrefixLogger struct {
-	logger DepthLoggerV2
 	prefix string
 }
 
@@ -35,47 +30,34 @@ func (pl *PrefixLogger) Infof(format string, args ...interface{}) {
 	if pl != nil {
 		// Handle nil, so the tests can pass in a nil logger.
 		format = pl.prefix + format
-		pl.logger.InfoDepth(1, fmt.Sprintf(format, args...))
-		return
 	}
-	InfoDepth(1, fmt.Sprintf(format, args...))
+	Logger.Infof(format, args...)
 }
 
 // Warningf does warning logging.
 func (pl *PrefixLogger) Warningf(format string, args ...interface{}) {
 	if pl != nil {
 		format = pl.prefix + format
-		pl.logger.WarningDepth(1, fmt.Sprintf(format, args...))
-		return
 	}
-	WarningDepth(1, fmt.Sprintf(format, args...))
+	Logger.Warningf(format, args...)
 }
 
 // Errorf does error logging.
 func (pl *PrefixLogger) Errorf(format string, args ...interface{}) {
 	if pl != nil {
 		format = pl.prefix + format
-		pl.logger.ErrorDepth(1, fmt.Sprintf(format, args...))
-		return
 	}
-	ErrorDepth(1, fmt.Sprintf(format, args...))
+	Logger.Errorf(format, args...)
 }
 
 // Debugf does info logging at verbose level 2.
 func (pl *PrefixLogger) Debugf(format string, args ...interface{}) {
-	if !Logger.V(2) {
-		return
-	}
-	if pl != nil {
-		// Handle nil, so the tests can pass in a nil logger.
-		format = pl.prefix + format
-		pl.logger.InfoDepth(1, fmt.Sprintf(format, args...))
-		return
+	if Logger.V(2) {
+		pl.Infof(format, args...)
 	}
-	InfoDepth(1, fmt.Sprintf(format, args...))
 }
 
 // NewPrefixLogger creates a prefix logger with the given prefix.
-func NewPrefixLogger(logger DepthLoggerV2, prefix string) *PrefixLogger {
-	return &PrefixLogger{logger: logger, prefix: prefix}
+func NewPrefixLogger(prefix string) *PrefixLogger {
+	return &PrefixLogger{prefix: prefix}
 }
diff --git a/vendor/google.golang.org/grpc/internal/grpcutil/encode_duration.go b/vendor/google.golang.org/grpc/internal/grpcutil/encode_duration.go
deleted file mode 100644
index b25b0bae..00000000
--- a/vendor/google.golang.org/grpc/internal/grpcutil/encode_duration.go
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- *
- * Copyright 2020 gRPC authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package grpcutil
-
-import (
-	"strconv"
-	"time"
-)
-
-const maxTimeoutValue int64 = 100000000 - 1
-
-// div does integer division and round-up the result. Note that this is
-// equivalent to (d+r-1)/r but has less chance to overflow.
-func div(d, r time.Duration) int64 {
-	if d%r > 0 {
-		return int64(d/r + 1)
-	}
-	return int64(d / r)
-}
-
-// EncodeDuration encodes the duration to the format grpc-timeout header
-// accepts.
-//
-// https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#requests
-func EncodeDuration(t time.Duration) string {
-	// TODO: This is simplistic and not bandwidth efficient. Improve it.
-	if t <= 0 {
-		return "0n"
-	}
-	if d := div(t, time.Nanosecond); d <= maxTimeoutValue {
-		return strconv.FormatInt(d, 10) + "n"
-	}
-	if d := div(t, time.Microsecond); d <= maxTimeoutValue {
-		return strconv.FormatInt(d, 10) + "u"
-	}
-	if d := div(t, time.Millisecond); d <= maxTimeoutValue {
-		return strconv.FormatInt(d, 10) + "m"
-	}
-	if d := div(t, time.Second); d <= maxTimeoutValue {
-		return strconv.FormatInt(d, 10) + "S"
-	}
-	if d := div(t, time.Minute); d <= maxTimeoutValue {
-		return strconv.FormatInt(d, 10) + "M"
-	}
-	// Note that maxTimeoutValue * time.Hour > MaxInt64.
-	return strconv.FormatInt(div(t, time.Hour), 10) + "H"
-}
diff --git a/vendor/google.golang.org/grpc/internal/grpcutil/metadata.go b/vendor/google.golang.org/grpc/internal/grpcutil/metadata.go
deleted file mode 100644
index 6f22bd89..00000000
--- a/vendor/google.golang.org/grpc/internal/grpcutil/metadata.go
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- *
- * Copyright 2020 gRPC authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package grpcutil
-
-import (
-	"context"
-
-	"google.golang.org/grpc/metadata"
-)
-
-type mdExtraKey struct{}
-
-// WithExtraMetadata creates a new context with incoming md attached.
-func WithExtraMetadata(ctx context.Context, md metadata.MD) context.Context {
-	return context.WithValue(ctx, mdExtraKey{}, md)
-}
-
-// ExtraMetadata returns the incoming metadata in ctx if it exists.  The
-// returned MD should not be modified. Writing to it may cause races.
-// Modification should be made to copies of the returned MD.
-func ExtraMetadata(ctx context.Context) (md metadata.MD, ok bool) {
-	md, ok = ctx.Value(mdExtraKey{}).(metadata.MD)
-	return
-}
diff --git a/vendor/google.golang.org/grpc/internal/grpcutil/method.go b/vendor/google.golang.org/grpc/internal/grpcutil/method.go
index 4e747506..2c2ff773 100644
--- a/vendor/google.golang.org/grpc/internal/grpcutil/method.go
+++ b/vendor/google.golang.org/grpc/internal/grpcutil/method.go
@@ -38,47 +38,3 @@ func ParseMethod(methodName string) (service, method string, _ error) {
 	}
 	return methodName[:pos], methodName[pos+1:], nil
 }
-
-const baseContentType = "application/grpc"
-
-// ContentSubtype returns the content-subtype for the given content-type.  The
-// given content-type must be a valid content-type that starts with
-// "application/grpc". A content-subtype will follow "application/grpc" after a
-// "+" or ";". See
-// https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#requests for
-// more details.
-//
-// If contentType is not a valid content-type for gRPC, the boolean
-// will be false, otherwise true. If content-type == "application/grpc",
-// "application/grpc+", or "application/grpc;", the boolean will be true,
-// but no content-subtype will be returned.
-//
-// contentType is assumed to be lowercase already.
-func ContentSubtype(contentType string) (string, bool) {
-	if contentType == baseContentType {
-		return "", true
-	}
-	if !strings.HasPrefix(contentType, baseContentType) {
-		return "", false
-	}
-	// guaranteed since != baseContentType and has baseContentType prefix
-	switch contentType[len(baseContentType)] {
-	case '+', ';':
-		// this will return true for "application/grpc+" or "application/grpc;"
-		// which the previous validContentType function tested to be valid, so we
-		// just say that no content-subtype is specified in this case
-		return contentType[len(baseContentType)+1:], true
-	default:
-		return "", false
-	}
-}
-
-// ContentType builds full content type with the given sub-type.
-//
-// contentSubtype is assumed to be lowercase
-func ContentType(contentSubtype string) string {
-	if contentSubtype == "" {
-		return baseContentType
-	}
-	return baseContentType + "+" + contentSubtype
-}
diff --git a/vendor/google.golang.org/grpc/internal/resolver/dns/dns_resolver.go b/vendor/google.golang.org/grpc/internal/resolver/dns/dns_resolver.go
index 30423556..9d08dd8a 100644
--- a/vendor/google.golang.org/grpc/internal/resolver/dns/dns_resolver.go
+++ b/vendor/google.golang.org/grpc/internal/resolver/dns/dns_resolver.go
@@ -44,8 +44,6 @@ import (
 // addresses from SRV records.  Must not be changed after init time.
 var EnableSRVLookups = false
 
-var logger = grpclog.Component("dns")
-
 func init() {
 	resolver.Register(NewBuilder())
 }
@@ -274,7 +272,7 @@ func handleDNSError(err error, lookupType string) error {
 	err = filterError(err)
 	if err != nil {
 		err = fmt.Errorf("dns: %v record lookup error: %v", lookupType, err)
-		logger.Info(err)
+		grpclog.Infoln(err)
 	}
 	return err
 }
@@ -297,7 +295,7 @@ func (d *dnsResolver) lookupTXT() *serviceconfig.ParseResult {
 
 	// TXT record must have "grpc_config=" attribute in order to be used as service config.
 	if !strings.HasPrefix(res, txtAttribute) {
-		logger.Warningf("dns: TXT record %v missing %v attribute", res, txtAttribute)
+		grpclog.Warningf("dns: TXT record %v missing %v attribute", res, txtAttribute)
 		// This is not an error; it is the equivalent of not having a service config.
 		return nil
 	}
@@ -423,12 +421,12 @@ func canaryingSC(js string) string {
 	var rcs []rawChoice
 	err := json.Unmarshal([]byte(js), &rcs)
 	if err != nil {
-		logger.Warningf("dns: error parsing service config json: %v", err)
+		grpclog.Warningf("dns: error parsing service config json: %v", err)
 		return ""
 	}
 	cliHostname, err := os.Hostname()
 	if err != nil {
-		logger.Warningf("dns: error getting client hostname: %v", err)
+		grpclog.Warningf("dns: error getting client hostname: %v", err)
 		return ""
 	}
 	var sc string
diff --git a/vendor/google.golang.org/grpc/internal/syscall/syscall_linux.go b/vendor/google.golang.org/grpc/internal/syscall/syscall_linux.go
index c50468a0..43281a3e 100644
--- a/vendor/google.golang.org/grpc/internal/syscall/syscall_linux.go
+++ b/vendor/google.golang.org/grpc/internal/syscall/syscall_linux.go
@@ -32,13 +32,11 @@ import (
 	"google.golang.org/grpc/grpclog"
 )
 
-var logger = grpclog.Component("core")
-
 // GetCPUTime returns the how much CPU time has passed since the start of this process.
 func GetCPUTime() int64 {
 	var ts unix.Timespec
 	if err := unix.ClockGettime(unix.CLOCK_PROCESS_CPUTIME_ID, &ts); err != nil {
-		logger.Fatal(err)
+		grpclog.Fatal(err)
 	}
 	return ts.Nano()
 }
diff --git a/vendor/google.golang.org/grpc/internal/syscall/syscall_nonlinux.go b/vendor/google.golang.org/grpc/internal/syscall/syscall_nonlinux.go
index adae60d6..ae0a9117 100644
--- a/vendor/google.golang.org/grpc/internal/syscall/syscall_nonlinux.go
+++ b/vendor/google.golang.org/grpc/internal/syscall/syscall_nonlinux.go
@@ -31,11 +31,10 @@ import (
 )
 
 var once sync.Once
-var logger = grpclog.Component("core")
 
 func log() {
 	once.Do(func() {
-		logger.Info("CPU time info is unavailable on non-linux or appengine environment.")
+		grpclog.Info("CPU time info is unavailable on non-linux or appengine environment.")
 	})
 }
 
diff --git a/vendor/google.golang.org/grpc/internal/transport/controlbuf.go b/vendor/google.golang.org/grpc/internal/transport/controlbuf.go
index 40ef2392..d4bb19c3 100644
--- a/vendor/google.golang.org/grpc/internal/transport/controlbuf.go
+++ b/vendor/google.golang.org/grpc/internal/transport/controlbuf.go
@@ -505,9 +505,7 @@ func (l *loopyWriter) run() (err error) {
 			// 1. When the connection is closed by some other known issue.
 			// 2. User closed the connection.
 			// 3. A graceful close of connection.
-			if logger.V(logLevel) {
-				logger.Infof("transport: loopyWriter.run returning. %v", err)
-			}
+			infof("transport: loopyWriter.run returning. %v", err)
 			err = nil
 		}
 	}()
@@ -607,9 +605,7 @@ func (l *loopyWriter) headerHandler(h *headerFrame) error {
 	if l.side == serverSide {
 		str, ok := l.estdStreams[h.streamID]
 		if !ok {
-			if logger.V(logLevel) {
-				logger.Warningf("transport: loopy doesn't recognize the stream: %d", h.streamID)
-			}
+			warningf("transport: loopy doesn't recognize the stream: %d", h.streamID)
 			return nil
 		}
 		// Case 1.A: Server is responding back with headers.
@@ -662,9 +658,7 @@ func (l *loopyWriter) writeHeader(streamID uint32, endStream bool, hf []hpack.He
 	l.hBuf.Reset()
 	for _, f := range hf {
 		if err := l.hEnc.WriteField(f); err != nil {
-			if logger.V(logLevel) {
-				logger.Warningf("transport: loopyWriter.writeHeader encountered error while encoding headers: %v", err)
-			}
+			warningf("transport: loopyWriter.writeHeader encountered error while encoding headers:", err)
 		}
 	}
 	var (
diff --git a/vendor/google.golang.org/grpc/internal/transport/handler_server.go b/vendor/google.golang.org/grpc/internal/transport/handler_server.go
index 05d3871e..fc44e976 100644
--- a/vendor/google.golang.org/grpc/internal/transport/handler_server.go
+++ b/vendor/google.golang.org/grpc/internal/transport/handler_server.go
@@ -39,7 +39,6 @@ import (
 	"golang.org/x/net/http2"
 	"google.golang.org/grpc/codes"
 	"google.golang.org/grpc/credentials"
-	"google.golang.org/grpc/internal/grpcutil"
 	"google.golang.org/grpc/metadata"
 	"google.golang.org/grpc/peer"
 	"google.golang.org/grpc/stats"
@@ -58,7 +57,7 @@ func NewServerHandlerTransport(w http.ResponseWriter, r *http.Request, stats sta
 	}
 	contentType := r.Header.Get("Content-Type")
 	// TODO: do we assume contentType is lowercase? we did before
-	contentSubtype, validContentType := grpcutil.ContentSubtype(contentType)
+	contentSubtype, validContentType := contentSubtype(contentType)
 	if !validContentType {
 		return nil, errors.New("invalid gRPC request content-type")
 	}
diff --git a/vendor/google.golang.org/grpc/internal/transport/http2_client.go b/vendor/google.golang.org/grpc/internal/transport/http2_client.go
index e7f23211..b43e21ff 100644
--- a/vendor/google.golang.org/grpc/internal/transport/http2_client.go
+++ b/vendor/google.golang.org/grpc/internal/transport/http2_client.go
@@ -32,7 +32,6 @@ import (
 
 	"golang.org/x/net/http2"
 	"golang.org/x/net/http2/hpack"
-	"google.golang.org/grpc/internal/grpcutil"
 
 	"google.golang.org/grpc/codes"
 	"google.golang.org/grpc/credentials"
@@ -355,9 +354,7 @@ func newHTTP2Client(connectCtx, ctx context.Context, addr resolver.Address, opts
 		t.loopy = newLoopyWriter(clientSide, t.framer, t.controlBuf, t.bdpEst)
 		err := t.loopy.run()
 		if err != nil {
-			if logger.V(logLevel) {
-				logger.Errorf("transport: loopyWriter.run returning. Err: %v", err)
-			}
+			errorf("transport: loopyWriter.run returning. Err: %v", err)
 		}
 		// If it's a connection error, let reader goroutine handle it
 		// since there might be data in the buffers.
@@ -437,7 +434,7 @@ func (t *http2Client) createHeaderFields(ctx context.Context, callHdr *CallHdr)
 	headerFields = append(headerFields, hpack.HeaderField{Name: ":scheme", Value: t.scheme})
 	headerFields = append(headerFields, hpack.HeaderField{Name: ":path", Value: callHdr.Method})
 	headerFields = append(headerFields, hpack.HeaderField{Name: ":authority", Value: callHdr.Host})
-	headerFields = append(headerFields, hpack.HeaderField{Name: "content-type", Value: grpcutil.ContentType(callHdr.ContentSubtype)})
+	headerFields = append(headerFields, hpack.HeaderField{Name: "content-type", Value: contentType(callHdr.ContentSubtype)})
 	headerFields = append(headerFields, hpack.HeaderField{Name: "user-agent", Value: t.userAgent})
 	headerFields = append(headerFields, hpack.HeaderField{Name: "te", Value: "trailers"})
 	if callHdr.PreviousAttempts > 0 {
@@ -452,7 +449,7 @@ func (t *http2Client) createHeaderFields(ctx context.Context, callHdr *CallHdr)
 		// Send out timeout regardless its value. The server can detect timeout context by itself.
 		// TODO(mmukhi): Perhaps this field should be updated when actually writing out to the wire.
 		timeout := time.Until(dl)
-		headerFields = append(headerFields, hpack.HeaderField{Name: "grpc-timeout", Value: grpcutil.EncodeDuration(timeout)})
+		headerFields = append(headerFields, hpack.HeaderField{Name: "grpc-timeout", Value: encodeTimeout(timeout)})
 	}
 	for k, v := range authData {
 		headerFields = append(headerFields, hpack.HeaderField{Name: k, Value: encodeMetadataHeader(k, v)})
@@ -1016,9 +1013,7 @@ func (t *http2Client) handleRSTStream(f *http2.RSTStreamFrame) {
 	}
 	statusCode, ok := http2ErrConvTab[f.ErrCode]
 	if !ok {
-		if logger.V(logLevel) {
-			logger.Warningf("transport: http2Client.handleRSTStream found no mapped gRPC status for the received http2 error %v", f.ErrCode)
-		}
+		warningf("transport: http2Client.handleRSTStream found no mapped gRPC status for the received http2 error %v", f.ErrCode)
 		statusCode = codes.Unknown
 	}
 	if statusCode == codes.Canceled {
@@ -1100,9 +1095,7 @@ func (t *http2Client) handleGoAway(f *http2.GoAwayFrame) {
 		return
 	}
 	if f.ErrCode == http2.ErrCodeEnhanceYourCalm {
-		if logger.V(logLevel) {
-			logger.Infof("Client received GoAway with http2.ErrCodeEnhanceYourCalm.")
-		}
+		infof("Client received GoAway with http2.ErrCodeEnhanceYourCalm.")
 	}
 	id := f.LastStreamID
 	if id > 0 && id%2 != 1 {
@@ -1332,9 +1325,7 @@ func (t *http2Client) reader() {
 		case *http2.WindowUpdateFrame:
 			t.handleWindowUpdate(frame)
 		default:
-			if logger.V(logLevel) {
-				logger.Errorf("transport: http2Client.reader got unhandled frame type %v.", frame)
-			}
+			errorf("transport: http2Client.reader got unhandled frame type %v.", frame)
 		}
 	}
 }
diff --git a/vendor/google.golang.org/grpc/internal/transport/http2_server.go b/vendor/google.golang.org/grpc/internal/transport/http2_server.go
index 04cbedf7..e8c75732 100644
--- a/vendor/google.golang.org/grpc/internal/transport/http2_server.go
+++ b/vendor/google.golang.org/grpc/internal/transport/http2_server.go
@@ -34,10 +34,10 @@ import (
 	"github.com/golang/protobuf/proto"
 	"golang.org/x/net/http2"
 	"golang.org/x/net/http2/hpack"
-	"google.golang.org/grpc/internal/grpcutil"
 
 	"google.golang.org/grpc/codes"
 	"google.golang.org/grpc/credentials"
+	"google.golang.org/grpc/grpclog"
 	"google.golang.org/grpc/internal/channelz"
 	"google.golang.org/grpc/internal/grpcrand"
 	"google.golang.org/grpc/keepalive"
@@ -289,9 +289,7 @@ func newHTTP2Server(conn net.Conn, config *ServerConfig) (_ ServerTransport, err
 		t.loopy = newLoopyWriter(serverSide, t.framer, t.controlBuf, t.bdpEst)
 		t.loopy.ssGoAwayHandler = t.outgoingGoAwayHandler
 		if err := t.loopy.run(); err != nil {
-			if logger.V(logLevel) {
-				logger.Errorf("transport: loopyWriter.run returning. Err: %v", err)
-			}
+			errorf("transport: loopyWriter.run returning. Err: %v", err)
 		}
 		t.conn.Close()
 		close(t.writerDone)
@@ -362,9 +360,7 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(
 		}
 		s.ctx, err = t.inTapHandle(s.ctx, info)
 		if err != nil {
-			if logger.V(logLevel) {
-				logger.Warningf("transport: http2Server.operateHeaders got an error from InTapHandle: %v", err)
-			}
+			warningf("transport: http2Server.operateHeaders got an error from InTapHandle: %v", err)
 			t.controlBuf.put(&cleanupStream{
 				streamID: s.id,
 				rst:      true,
@@ -395,9 +391,7 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(
 	if streamID%2 != 1 || streamID <= t.maxStreamID {
 		t.mu.Unlock()
 		// illegal gRPC stream id.
-		if logger.V(logLevel) {
-			logger.Errorf("transport: http2Server.HandleStreams received an illegal stream id: %v", streamID)
-		}
+		errorf("transport: http2Server.HandleStreams received an illegal stream id: %v", streamID)
 		s.cancel()
 		return true
 	}
@@ -460,9 +454,7 @@ func (t *http2Server) HandleStreams(handle func(*Stream), traceCtx func(context.
 		atomic.StoreInt64(&t.lastRead, time.Now().UnixNano())
 		if err != nil {
 			if se, ok := err.(http2.StreamError); ok {
-				if logger.V(logLevel) {
-					logger.Warningf("transport: http2Server.HandleStreams encountered http2.StreamError: %v", se)
-				}
+				warningf("transport: http2Server.HandleStreams encountered http2.StreamError: %v", se)
 				t.mu.Lock()
 				s := t.activeStreams[se.StreamID]
 				t.mu.Unlock()
@@ -482,9 +474,7 @@ func (t *http2Server) HandleStreams(handle func(*Stream), traceCtx func(context.
 				t.Close()
 				return
 			}
-			if logger.V(logLevel) {
-				logger.Warningf("transport: http2Server.HandleStreams failed to read frame: %v", err)
-			}
+			warningf("transport: http2Server.HandleStreams failed to read frame: %v", err)
 			t.Close()
 			return
 		}
@@ -507,9 +497,7 @@ func (t *http2Server) HandleStreams(handle func(*Stream), traceCtx func(context.
 		case *http2.GoAwayFrame:
 			// TODO: Handle GoAway from the client appropriately.
 		default:
-			if logger.V(logLevel) {
-				logger.Errorf("transport: http2Server.HandleStreams found unhandled frame type %v.", frame)
-			}
+			errorf("transport: http2Server.HandleStreams found unhandled frame type %v.", frame)
 		}
 	}
 }
@@ -731,9 +719,7 @@ func (t *http2Server) handlePing(f *http2.PingFrame) {
 
 	if t.pingStrikes > maxPingStrikes {
 		// Send goaway and close the connection.
-		if logger.V(logLevel) {
-			logger.Errorf("transport: Got too many pings from the client, closing the connection.")
-		}
+		errorf("transport: Got too many pings from the client, closing the connection.")
 		t.controlBuf.put(&goAway{code: http2.ErrCodeEnhanceYourCalm, debugData: []byte("too_many_pings"), closeConn: true})
 	}
 }
@@ -766,9 +752,7 @@ func (t *http2Server) checkForHeaderListSize(it interface{}) bool {
 	var sz int64
 	for _, f := range hdrFrame.hf {
 		if sz += int64(f.Size()); sz > int64(*t.maxSendHeaderListSize) {
-			if logger.V(logLevel) {
-				logger.Errorf("header list size to send violates the maximum size (%d bytes) set by client", *t.maxSendHeaderListSize)
-			}
+			errorf("header list size to send violates the maximum size (%d bytes) set by client", *t.maxSendHeaderListSize)
 			return false
 		}
 	}
@@ -805,7 +789,7 @@ func (t *http2Server) writeHeaderLocked(s *Stream) error {
 	// first and create a slice of that exact size.
 	headerFields := make([]hpack.HeaderField, 0, 2) // at least :status, content-type will be there if none else.
 	headerFields = append(headerFields, hpack.HeaderField{Name: ":status", Value: "200"})
-	headerFields = append(headerFields, hpack.HeaderField{Name: "content-type", Value: grpcutil.ContentType(s.contentSubtype)})
+	headerFields = append(headerFields, hpack.HeaderField{Name: "content-type", Value: contentType(s.contentSubtype)})
 	if s.sendCompress != "" {
 		headerFields = append(headerFields, hpack.HeaderField{Name: "grpc-encoding", Value: s.sendCompress})
 	}
@@ -855,7 +839,7 @@ func (t *http2Server) WriteStatus(s *Stream, st *status.Status) error {
 			}
 		} else { // Send a trailer only response.
 			headerFields = append(headerFields, hpack.HeaderField{Name: ":status", Value: "200"})
-			headerFields = append(headerFields, hpack.HeaderField{Name: "content-type", Value: grpcutil.ContentType(s.contentSubtype)})
+			headerFields = append(headerFields, hpack.HeaderField{Name: "content-type", Value: contentType(s.contentSubtype)})
 		}
 	}
 	headerFields = append(headerFields, hpack.HeaderField{Name: "grpc-status", Value: strconv.Itoa(int(st.Code()))})
@@ -865,7 +849,7 @@ func (t *http2Server) WriteStatus(s *Stream, st *status.Status) error {
 		stBytes, err := proto.Marshal(p)
 		if err != nil {
 			// TODO: return error instead, when callers are able to handle it.
-			logger.Errorf("transport: failed to marshal rpc status: %v, error: %v", p, err)
+			grpclog.Errorf("transport: failed to marshal rpc status: %v, error: %v", p, err)
 		} else {
 			headerFields = append(headerFields, hpack.HeaderField{Name: "grpc-status-details-bin", Value: encodeBinHeader(stBytes)})
 		}
@@ -996,9 +980,7 @@ func (t *http2Server) keepalive() {
 			select {
 			case <-ageTimer.C:
 				// Close the connection after grace period.
-				if logger.V(logLevel) {
-					logger.Infof("transport: closing server transport due to maximum connection age.")
-				}
+				infof("transport: closing server transport due to maximum connection age.")
 				t.Close()
 			case <-t.done:
 			}
@@ -1015,9 +997,7 @@ func (t *http2Server) keepalive() {
 				continue
 			}
 			if outstandingPing && kpTimeoutLeft <= 0 {
-				if logger.V(logLevel) {
-					logger.Infof("transport: closing server transport due to idleness.")
-				}
+				infof("transport: closing server transport due to idleness.")
 				t.Close()
 				return
 			}
diff --git a/vendor/google.golang.org/grpc/internal/transport/http_util.go b/vendor/google.golang.org/grpc/internal/transport/http_util.go
index 5e1e7a65..8f5f3349 100644
--- a/vendor/google.golang.org/grpc/internal/transport/http_util.go
+++ b/vendor/google.golang.org/grpc/internal/transport/http_util.go
@@ -37,8 +37,6 @@ import (
 	"golang.org/x/net/http2/hpack"
 	spb "google.golang.org/genproto/googleapis/rpc/status"
 	"google.golang.org/grpc/codes"
-	"google.golang.org/grpc/grpclog"
-	"google.golang.org/grpc/internal/grpcutil"
 	"google.golang.org/grpc/status"
 )
 
@@ -52,7 +50,7 @@ const (
 	// "proto" as a suffix after "+" or ";".  See
 	// https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#requests
 	// for more details.
-
+	baseContentType = "application/grpc"
 )
 
 var (
@@ -99,7 +97,6 @@ var (
 		// 504 Gateway timeout - UNAVAILABLE.
 		http.StatusGatewayTimeout: codes.Unavailable,
 	}
-	logger = grpclog.Component("transport")
 )
 
 type parsedHeaderData struct {
@@ -185,6 +182,46 @@ func isWhitelistedHeader(hdr string) bool {
 	}
 }
 
+// contentSubtype returns the content-subtype for the given content-type.  The
+// given content-type must be a valid content-type that starts with
+// "application/grpc". A content-subtype will follow "application/grpc" after a
+// "+" or ";". See
+// https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#requests for
+// more details.
+//
+// If contentType is not a valid content-type for gRPC, the boolean
+// will be false, otherwise true. If content-type == "application/grpc",
+// "application/grpc+", or "application/grpc;", the boolean will be true,
+// but no content-subtype will be returned.
+//
+// contentType is assumed to be lowercase already.
+func contentSubtype(contentType string) (string, bool) {
+	if contentType == baseContentType {
+		return "", true
+	}
+	if !strings.HasPrefix(contentType, baseContentType) {
+		return "", false
+	}
+	// guaranteed since != baseContentType and has baseContentType prefix
+	switch contentType[len(baseContentType)] {
+	case '+', ';':
+		// this will return true for "application/grpc+" or "application/grpc;"
+		// which the previous validContentType function tested to be valid, so we
+		// just say that no content-subtype is specified in this case
+		return contentType[len(baseContentType)+1:], true
+	default:
+		return "", false
+	}
+}
+
+// contentSubtype is assumed to be lowercase
+func contentType(contentSubtype string) string {
+	if contentSubtype == "" {
+		return baseContentType
+	}
+	return baseContentType + "+" + contentSubtype
+}
+
 func (d *decodeState) status() *status.Status {
 	if d.data.statusGen == nil {
 		// No status-details were provided; generate status using code/msg.
@@ -303,7 +340,7 @@ func (d *decodeState) addMetadata(k, v string) {
 func (d *decodeState) processHeaderField(f hpack.HeaderField) {
 	switch f.Name {
 	case "content-type":
-		contentSubtype, validContentType := grpcutil.ContentSubtype(f.Value)
+		contentSubtype, validContentType := contentSubtype(f.Value)
 		if !validContentType {
 			d.data.contentTypeErr = fmt.Sprintf("transport: received the unexpected content-type %q", f.Value)
 			return
@@ -375,9 +412,7 @@ func (d *decodeState) processHeaderField(f hpack.HeaderField) {
 		}
 		v, err := decodeMetadataHeader(f.Name, f.Value)
 		if err != nil {
-			if logger.V(logLevel) {
-				logger.Errorf("Failed to decode metadata header (%q, %q): %v", f.Name, f.Value, err)
-			}
+			errorf("Failed to decode metadata header (%q, %q): %v", f.Name, f.Value, err)
 			return
 		}
 		d.addMetadata(f.Name, v)
@@ -414,6 +449,41 @@ func timeoutUnitToDuration(u timeoutUnit) (d time.Duration, ok bool) {
 	return
 }
 
+const maxTimeoutValue int64 = 100000000 - 1
+
+// div does integer division and round-up the result. Note that this is
+// equivalent to (d+r-1)/r but has less chance to overflow.
+func div(d, r time.Duration) int64 {
+	if m := d % r; m > 0 {
+		return int64(d/r + 1)
+	}
+	return int64(d / r)
+}
+
+// TODO(zhaoq): It is the simplistic and not bandwidth efficient. Improve it.
+func encodeTimeout(t time.Duration) string {
+	if t <= 0 {
+		return "0n"
+	}
+	if d := div(t, time.Nanosecond); d <= maxTimeoutValue {
+		return strconv.FormatInt(d, 10) + "n"
+	}
+	if d := div(t, time.Microsecond); d <= maxTimeoutValue {
+		return strconv.FormatInt(d, 10) + "u"
+	}
+	if d := div(t, time.Millisecond); d <= maxTimeoutValue {
+		return strconv.FormatInt(d, 10) + "m"
+	}
+	if d := div(t, time.Second); d <= maxTimeoutValue {
+		return strconv.FormatInt(d, 10) + "S"
+	}
+	if d := div(t, time.Minute); d <= maxTimeoutValue {
+		return strconv.FormatInt(d, 10) + "M"
+	}
+	// Note that maxTimeoutValue * time.Hour > MaxInt64.
+	return strconv.FormatInt(div(t, time.Hour), 10) + "H"
+}
+
 func decodeTimeout(s string) (time.Duration, error) {
 	size := len(s)
 	if size < 2 {
diff --git a/vendor/google.golang.org/grpc/internal/credentials/gobefore110.go b/vendor/google.golang.org/grpc/internal/transport/log.go
similarity index 50%
rename from vendor/google.golang.org/grpc/internal/credentials/gobefore110.go
rename to vendor/google.golang.org/grpc/internal/transport/log.go
index 743713e1..879df80c 100644
--- a/vendor/google.golang.org/grpc/internal/credentials/gobefore110.go
+++ b/vendor/google.golang.org/grpc/internal/transport/log.go
@@ -1,8 +1,6 @@
-// +build !go1.10
-
 /*
  *
- * Copyright 2020 gRPC authors.
+ * Copyright 2017 gRPC authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -18,14 +16,29 @@
  *
  */
 
-package credentials
+// This file contains wrappers for grpclog functions.
+// The transport package only logs to verbose level 2 by default.
+
+package transport
+
+import "google.golang.org/grpc/grpclog"
+
+const logLevel = 2
 
-import (
-	"crypto/tls"
-	"net/url"
-)
+func infof(format string, args ...interface{}) {
+	if grpclog.V(logLevel) {
+		grpclog.Infof(format, args...)
+	}
+}
+
+func warningf(format string, args ...interface{}) {
+	if grpclog.V(logLevel) {
+		grpclog.Warningf(format, args...)
+	}
+}
 
-//TODO(ZhenLian): delete this file when we remove Go 1.9 tests.
-func SPIFFEIDFromState(state tls.ConnectionState) *url.URL {
-	return nil
+func errorf(format string, args ...interface{}) {
+	if grpclog.V(logLevel) {
+		grpclog.Errorf(format, args...)
+	}
 }
diff --git a/vendor/google.golang.org/grpc/internal/transport/transport.go b/vendor/google.golang.org/grpc/internal/transport/transport.go
index b74030a9..1ffd96ff 100644
--- a/vendor/google.golang.org/grpc/internal/transport/transport.go
+++ b/vendor/google.golang.org/grpc/internal/transport/transport.go
@@ -41,8 +41,6 @@ import (
 	"google.golang.org/grpc/tap"
 )
 
-const logLevel = 2
-
 type bufferPool struct {
 	pool sync.Pool
 }
diff --git a/vendor/google.golang.org/grpc/picker_wrapper.go b/vendor/google.golang.org/grpc/picker_wrapper.go
index a58174b6..7f3edaae 100644
--- a/vendor/google.golang.org/grpc/picker_wrapper.go
+++ b/vendor/google.golang.org/grpc/picker_wrapper.go
@@ -25,6 +25,7 @@ import (
 
 	"google.golang.org/grpc/balancer"
 	"google.golang.org/grpc/codes"
+	"google.golang.org/grpc/grpclog"
 	"google.golang.org/grpc/internal/channelz"
 	"google.golang.org/grpc/internal/transport"
 	"google.golang.org/grpc/status"
@@ -144,7 +145,7 @@ func (pw *pickerWrapper) pick(ctx context.Context, failfast bool, info balancer.
 
 		acw, ok := pickResult.SubConn.(*acBalancerWrapper)
 		if !ok {
-			logger.Error("subconn returned from pick is not *acBalancerWrapper")
+			grpclog.Error("subconn returned from pick is not *acBalancerWrapper")
 			continue
 		}
 		if t, ok := acw.getAddrConn().getReadyTransport(); ok {
@@ -158,7 +159,7 @@ func (pw *pickerWrapper) pick(ctx context.Context, failfast bool, info balancer.
 			// DoneInfo with default value works.
 			pickResult.Done(balancer.DoneInfo{})
 		}
-		logger.Infof("blockingPicker: the picked transport is not ready, loop back to repick")
+		grpclog.Infof("blockingPicker: the picked transport is not ready, loop back to repick")
 		// If ok == false, ac.state is not READY.
 		// A valid picker always returns READY subConn. This means the state of ac
 		// just changed, and picker will be updated shortly.
diff --git a/vendor/google.golang.org/grpc/pickfirst.go b/vendor/google.golang.org/grpc/pickfirst.go
index 56e33f6c..4b7340ad 100644
--- a/vendor/google.golang.org/grpc/pickfirst.go
+++ b/vendor/google.golang.org/grpc/pickfirst.go
@@ -24,6 +24,7 @@ import (
 
 	"google.golang.org/grpc/balancer"
 	"google.golang.org/grpc/connectivity"
+	"google.golang.org/grpc/grpclog"
 )
 
 // PickFirstBalancerName is the name of the pick_first balancer.
@@ -57,8 +58,8 @@ func (b *pickfirstBalancer) ResolverError(err error) {
 			Picker: &picker{err: fmt.Errorf("name resolver error: %v", err)},
 		})
 	}
-	if logger.V(2) {
-		logger.Infof("pickfirstBalancer: ResolverError called with error %v", err)
+	if grpclog.V(2) {
+		grpclog.Infof("pickfirstBalancer: ResolverError called with error %v", err)
 	}
 }
 
@@ -71,8 +72,8 @@ func (b *pickfirstBalancer) UpdateClientConnState(cs balancer.ClientConnState) e
 		var err error
 		b.sc, err = b.cc.NewSubConn(cs.ResolverState.Addresses, balancer.NewSubConnOptions{})
 		if err != nil {
-			if logger.V(2) {
-				logger.Errorf("pickfirstBalancer: failed to NewSubConn: %v", err)
+			if grpclog.V(2) {
+				grpclog.Errorf("pickfirstBalancer: failed to NewSubConn: %v", err)
 			}
 			b.state = connectivity.TransientFailure
 			b.cc.UpdateState(balancer.State{ConnectivityState: connectivity.TransientFailure,
@@ -91,12 +92,12 @@ func (b *pickfirstBalancer) UpdateClientConnState(cs balancer.ClientConnState) e
 }
 
 func (b *pickfirstBalancer) UpdateSubConnState(sc balancer.SubConn, s balancer.SubConnState) {
-	if logger.V(2) {
-		logger.Infof("pickfirstBalancer: UpdateSubConnState: %p, %v", sc, s)
+	if grpclog.V(2) {
+		grpclog.Infof("pickfirstBalancer: UpdateSubConnState: %p, %v", sc, s)
 	}
 	if b.sc != sc {
-		if logger.V(2) {
-			logger.Infof("pickfirstBalancer: ignored state change because sc is not recognized")
+		if grpclog.V(2) {
+			grpclog.Infof("pickfirstBalancer: ignored state change because sc is not recognized")
 		}
 		return
 	}
diff --git a/vendor/google.golang.org/grpc/resolver_conn_wrapper.go b/vendor/google.golang.org/grpc/resolver_conn_wrapper.go
index 265002a7..edfda866 100644
--- a/vendor/google.golang.org/grpc/resolver_conn_wrapper.go
+++ b/vendor/google.golang.org/grpc/resolver_conn_wrapper.go
@@ -140,7 +140,7 @@ func (ccr *ccResolverWrapper) UpdateState(s resolver.State) {
 	if ccr.done.HasFired() {
 		return
 	}
-	channelz.Infof(logger, ccr.cc.channelzID, "ccResolverWrapper: sending update to cc: %v", s)
+	channelz.Infof(ccr.cc.channelzID, "ccResolverWrapper: sending update to cc: %v", s)
 	if channelz.IsOn() {
 		ccr.addChannelzTraceEvent(s)
 	}
@@ -152,7 +152,7 @@ func (ccr *ccResolverWrapper) ReportError(err error) {
 	if ccr.done.HasFired() {
 		return
 	}
-	channelz.Warningf(logger, ccr.cc.channelzID, "ccResolverWrapper: reporting error to cc: %v", err)
+	channelz.Warningf(ccr.cc.channelzID, "ccResolverWrapper: reporting error to cc: %v", err)
 	ccr.poll(ccr.cc.updateResolverState(resolver.State{}, err))
 }
 
@@ -161,7 +161,7 @@ func (ccr *ccResolverWrapper) NewAddress(addrs []resolver.Address) {
 	if ccr.done.HasFired() {
 		return
 	}
-	channelz.Infof(logger, ccr.cc.channelzID, "ccResolverWrapper: sending new addresses to cc: %v", addrs)
+	channelz.Infof(ccr.cc.channelzID, "ccResolverWrapper: sending new addresses to cc: %v", addrs)
 	if channelz.IsOn() {
 		ccr.addChannelzTraceEvent(resolver.State{Addresses: addrs, ServiceConfig: ccr.curState.ServiceConfig})
 	}
@@ -175,14 +175,14 @@ func (ccr *ccResolverWrapper) NewServiceConfig(sc string) {
 	if ccr.done.HasFired() {
 		return
 	}
-	channelz.Infof(logger, ccr.cc.channelzID, "ccResolverWrapper: got new service config: %v", sc)
+	channelz.Infof(ccr.cc.channelzID, "ccResolverWrapper: got new service config: %v", sc)
 	if ccr.cc.dopts.disableServiceConfig {
-		channelz.Info(logger, ccr.cc.channelzID, "Service config lookups disabled; ignoring config")
+		channelz.Info(ccr.cc.channelzID, "Service config lookups disabled; ignoring config")
 		return
 	}
 	scpr := parseServiceConfig(sc)
 	if scpr.Err != nil {
-		channelz.Warningf(logger, ccr.cc.channelzID, "ccResolverWrapper: error parsing service config: %v", scpr.Err)
+		channelz.Warningf(ccr.cc.channelzID, "ccResolverWrapper: error parsing service config: %v", scpr.Err)
 		ccr.poll(balancer.ErrBadResolverState)
 		return
 	}
@@ -215,7 +215,7 @@ func (ccr *ccResolverWrapper) addChannelzTraceEvent(s resolver.State) {
 	} else if len(ccr.curState.Addresses) == 0 && len(s.Addresses) > 0 {
 		updates = append(updates, "resolver returned new addresses")
 	}
-	channelz.AddTraceEvent(logger, ccr.cc.channelzID, 0, &channelz.TraceEventDesc{
+	channelz.AddTraceEvent(ccr.cc.channelzID, 0, &channelz.TraceEventDesc{
 		Desc:     fmt.Sprintf("Resolver state updated: %+v (%v)", s, strings.Join(updates, "; ")),
 		Severity: channelz.CtINFO,
 	})
diff --git a/vendor/google.golang.org/grpc/server.go b/vendor/google.golang.org/grpc/server.go
index 1b56cc2d..c2c7cae6 100644
--- a/vendor/google.golang.org/grpc/server.go
+++ b/vendor/google.golang.org/grpc/server.go
@@ -59,7 +59,6 @@ const (
 )
 
 var statusOK = status.New(codes.OK, "")
-var logger = grpclog.Component("core")
 
 type methodHandler func(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor UnaryServerInterceptor) (interface{}, error)
 
@@ -224,7 +223,7 @@ func InitialConnWindowSize(s int32) ServerOption {
 // KeepaliveParams returns a ServerOption that sets keepalive and max-age parameters for the server.
 func KeepaliveParams(kp keepalive.ServerParameters) ServerOption {
 	if kp.Time > 0 && kp.Time < time.Second {
-		logger.Warning("Adjusting keepalive ping interval to minimum period of 1s")
+		grpclog.Warning("Adjusting keepalive ping interval to minimum period of 1s")
 		kp.Time = time.Second
 	}
 
@@ -243,12 +242,6 @@ func KeepaliveEnforcementPolicy(kep keepalive.EnforcementPolicy) ServerOption {
 // CustomCodec returns a ServerOption that sets a codec for message marshaling and unmarshaling.
 //
 // This will override any lookups by content-subtype for Codecs registered with RegisterCodec.
-//
-// Deprecated: register codecs using encoding.RegisterCodec. The server will
-// automatically use registered codecs based on the incoming requests' headers.
-// See also
-// https://github.com/grpc/grpc-go/blob/master/Documentation/encoding.md#using-a-codec.
-// Will be supported throughout 1.x.
 func CustomCodec(codec Codec) ServerOption {
 	return newFuncServerOption(func(o *serverOptions) {
 		o.codec = codec
@@ -261,8 +254,7 @@ func CustomCodec(codec Codec) ServerOption {
 // default, server messages will be sent using the same compressor with which
 // request messages were sent.
 //
-// Deprecated: use encoding.RegisterCompressor instead. Will be supported
-// throughout 1.x.
+// Deprecated: use encoding.RegisterCompressor instead.
 func RPCCompressor(cp Compressor) ServerOption {
 	return newFuncServerOption(func(o *serverOptions) {
 		o.cp = cp
@@ -273,8 +265,7 @@ func RPCCompressor(cp Compressor) ServerOption {
 // messages.  It has higher priority than decompressors registered via
 // encoding.RegisterCompressor.
 //
-// Deprecated: use encoding.RegisterCompressor instead. Will be supported
-// throughout 1.x.
+// Deprecated: use encoding.RegisterCompressor instead.
 func RPCDecompressor(dc Decompressor) ServerOption {
 	return newFuncServerOption(func(o *serverOptions) {
 		o.dc = dc
@@ -284,7 +275,7 @@ func RPCDecompressor(dc Decompressor) ServerOption {
 // MaxMsgSize returns a ServerOption to set the max message size in bytes the server can receive.
 // If this is not set, gRPC uses the default limit.
 //
-// Deprecated: use MaxRecvMsgSize instead. Will be supported throughout 1.x.
+// Deprecated: use MaxRecvMsgSize instead.
 func MaxMsgSize(m int) ServerOption {
 	return MaxRecvMsgSize(m)
 }
@@ -546,7 +537,7 @@ func (s *Server) RegisterService(sd *ServiceDesc, ss interface{}) {
 	ht := reflect.TypeOf(sd.HandlerType).Elem()
 	st := reflect.TypeOf(ss)
 	if !st.Implements(ht) {
-		logger.Fatalf("grpc: Server.RegisterService found the handler of type %v that does not satisfy %v", st, ht)
+		grpclog.Fatalf("grpc: Server.RegisterService found the handler of type %v that does not satisfy %v", st, ht)
 	}
 	s.register(sd, ss)
 }
@@ -556,10 +547,10 @@ func (s *Server) register(sd *ServiceDesc, ss interface{}) {
 	defer s.mu.Unlock()
 	s.printf("RegisterService(%q)", sd.ServiceName)
 	if s.serve {
-		logger.Fatalf("grpc: Server.RegisterService after Server.Serve for %q", sd.ServiceName)
+		grpclog.Fatalf("grpc: Server.RegisterService after Server.Serve for %q", sd.ServiceName)
 	}
 	if _, ok := s.m[sd.ServiceName]; ok {
-		logger.Fatalf("grpc: Server.RegisterService found duplicate service registration for %q", sd.ServiceName)
+		grpclog.Fatalf("grpc: Server.RegisterService found duplicate service registration for %q", sd.ServiceName)
 	}
 	srv := &service{
 		server: ss,
@@ -765,7 +756,7 @@ func (s *Server) handleRawConn(rawConn net.Conn) {
 			s.mu.Lock()
 			s.errorf("ServerHandshake(%q) failed: %v", rawConn.RemoteAddr(), err)
 			s.mu.Unlock()
-			channelz.Warningf(logger, s.channelzID, "grpc: Server.Serve failed to complete security handshake from %q: %v", rawConn.RemoteAddr(), err)
+			channelz.Warningf(s.channelzID, "grpc: Server.Serve failed to complete security handshake from %q: %v", rawConn.RemoteAddr(), err)
 			rawConn.Close()
 		}
 		rawConn.SetDeadline(time.Time{})
@@ -812,7 +803,7 @@ func (s *Server) newHTTP2Transport(c net.Conn, authInfo credentials.AuthInfo) tr
 		s.errorf("NewServerTransport(%q) failed: %v", c.RemoteAddr(), err)
 		s.mu.Unlock()
 		c.Close()
-		channelz.Warning(logger, s.channelzID, "grpc: Server.Serve failed to create ServerTransport: ", err)
+		channelz.Warning(s.channelzID, "grpc: Server.Serve failed to create ServerTransport: ", err)
 		return nil
 	}
 
@@ -966,12 +957,12 @@ func (s *Server) incrCallsFailed() {
 func (s *Server) sendResponse(t transport.ServerTransport, stream *transport.Stream, msg interface{}, cp Compressor, opts *transport.Options, comp encoding.Compressor) error {
 	data, err := encode(s.getCodec(stream.ContentSubtype()), msg)
 	if err != nil {
-		channelz.Error(logger, s.channelzID, "grpc: server failed to encode response: ", err)
+		channelz.Error(s.channelzID, "grpc: server failed to encode response: ", err)
 		return err
 	}
 	compData, err := compress(data, cp, comp)
 	if err != nil {
-		channelz.Error(logger, s.channelzID, "grpc: server failed to compress response: ", err)
+		channelz.Error(s.channelzID, "grpc: server failed to compress response: ", err)
 		return err
 	}
 	hdr, payload := msgHeader(data, compData)
@@ -1145,7 +1136,7 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.
 	if err != nil {
 		if st, ok := status.FromError(err); ok {
 			if e := t.WriteStatus(stream, st); e != nil {
-				channelz.Warningf(logger, s.channelzID, "grpc: Server.processUnaryRPC failed to write status %v", e)
+				channelz.Warningf(s.channelzID, "grpc: Server.processUnaryRPC failed to write status %v", e)
 			}
 		}
 		return err
@@ -1190,7 +1181,7 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.
 			trInfo.tr.SetError()
 		}
 		if e := t.WriteStatus(stream, appStatus); e != nil {
-			channelz.Warningf(logger, s.channelzID, "grpc: Server.processUnaryRPC failed to write status: %v", e)
+			channelz.Warningf(s.channelzID, "grpc: Server.processUnaryRPC failed to write status: %v", e)
 		}
 		if binlog != nil {
 			if h, _ := stream.Header(); h.Len() > 0 {
@@ -1219,7 +1210,7 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.
 		}
 		if sts, ok := status.FromError(err); ok {
 			if e := t.WriteStatus(stream, sts); e != nil {
-				channelz.Warningf(logger, s.channelzID, "grpc: Server.processUnaryRPC failed to write status: %v", e)
+				channelz.Warningf(s.channelzID, "grpc: Server.processUnaryRPC failed to write status: %v", e)
 			}
 		} else {
 			switch st := err.(type) {
@@ -1487,7 +1478,7 @@ func (s *Server) handleStream(t transport.ServerTransport, stream *transport.Str
 				trInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true)
 				trInfo.tr.SetError()
 			}
-			channelz.Warningf(logger, s.channelzID, "grpc: Server.handleStream failed to write status: %v", err)
+			channelz.Warningf(s.channelzID, "grpc: Server.handleStream failed to write status: %v", err)
 		}
 		if trInfo != nil {
 			trInfo.tr.Finish()
@@ -1528,7 +1519,7 @@ func (s *Server) handleStream(t transport.ServerTransport, stream *transport.Str
 			trInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true)
 			trInfo.tr.SetError()
 		}
-		channelz.Warningf(logger, s.channelzID, "grpc: Server.handleStream failed to write status: %v", err)
+		channelz.Warningf(s.channelzID, "grpc: Server.handleStream failed to write status: %v", err)
 	}
 	if trInfo != nil {
 		trInfo.tr.Finish()
diff --git a/vendor/google.golang.org/grpc/service_config.go b/vendor/google.golang.org/grpc/service_config.go
index 5e434ca7..3132a66c 100644
--- a/vendor/google.golang.org/grpc/service_config.go
+++ b/vendor/google.golang.org/grpc/service_config.go
@@ -20,7 +20,6 @@ package grpc
 
 import (
 	"encoding/json"
-	"errors"
 	"fmt"
 	"reflect"
 	"strconv"
@@ -28,6 +27,7 @@ import (
 	"time"
 
 	"google.golang.org/grpc/codes"
+	"google.golang.org/grpc/grpclog"
 	"google.golang.org/grpc/internal"
 	internalserviceconfig "google.golang.org/grpc/internal/serviceconfig"
 	"google.golang.org/grpc/serviceconfig"
@@ -225,27 +225,19 @@ func parseDuration(s *string) (*time.Duration, error) {
 }
 
 type jsonName struct {
-	Service string
-	Method  string
+	Service *string
+	Method  *string
 }
 
-var (
-	errDuplicatedName             = errors.New("duplicated name")
-	errEmptyServiceNonEmptyMethod = errors.New("cannot combine empty 'service' and non-empty 'method'")
-)
-
-func (j jsonName) generatePath() (string, error) {
-	if j.Service == "" {
-		if j.Method != "" {
-			return "", errEmptyServiceNonEmptyMethod
-		}
-		return "", nil
+func (j jsonName) generatePath() (string, bool) {
+	if j.Service == nil {
+		return "", false
 	}
-	res := "/" + j.Service + "/"
-	if j.Method != "" {
-		res += j.Method
+	res := "/" + *j.Service + "/"
+	if j.Method != nil {
+		res += *j.Method
 	}
-	return res, nil
+	return res, true
 }
 
 // TODO(lyuxuan): delete this struct after cleaning up old service config implementation.
@@ -277,7 +269,7 @@ func parseServiceConfig(js string) *serviceconfig.ParseResult {
 	var rsc jsonSC
 	err := json.Unmarshal([]byte(js), &rsc)
 	if err != nil {
-		logger.Warningf("grpc: parseServiceConfig error unmarshaling %s due to %v", js, err)
+		grpclog.Warningf("grpc: parseServiceConfig error unmarshaling %s due to %v", js, err)
 		return &serviceconfig.ParseResult{Err: err}
 	}
 	sc := ServiceConfig{
@@ -297,15 +289,13 @@ func parseServiceConfig(js string) *serviceconfig.ParseResult {
 	if rsc.MethodConfig == nil {
 		return &serviceconfig.ParseResult{Config: &sc}
 	}
-
-	paths := map[string]struct{}{}
 	for _, m := range *rsc.MethodConfig {
 		if m.Name == nil {
 			continue
 		}
 		d, err := parseDuration(m.Timeout)
 		if err != nil {
-			logger.Warningf("grpc: parseServiceConfig error unmarshaling %s due to %v", js, err)
+			grpclog.Warningf("grpc: parseServiceConfig error unmarshaling %s due to %v", js, err)
 			return &serviceconfig.ParseResult{Err: err}
 		}
 
@@ -314,7 +304,7 @@ func parseServiceConfig(js string) *serviceconfig.ParseResult {
 			Timeout:      d,
 		}
 		if mc.retryPolicy, err = convertRetryPolicy(m.RetryPolicy); err != nil {
-			logger.Warningf("grpc: parseServiceConfig error unmarshaling %s due to %v", js, err)
+			grpclog.Warningf("grpc: parseServiceConfig error unmarshaling %s due to %v", js, err)
 			return &serviceconfig.ParseResult{Err: err}
 		}
 		if m.MaxRequestMessageBytes != nil {
@@ -331,20 +321,10 @@ func parseServiceConfig(js string) *serviceconfig.ParseResult {
 				mc.MaxRespSize = newInt(int(*m.MaxResponseMessageBytes))
 			}
 		}
-		for i, n := range *m.Name {
-			path, err := n.generatePath()
-			if err != nil {
-				logger.Warningf("grpc: parseServiceConfig error unmarshaling %s due to methodConfig[%d]: %v", js, i, err)
-				return &serviceconfig.ParseResult{Err: err}
-			}
-
-			if _, ok := paths[path]; ok {
-				err = errDuplicatedName
-				logger.Warningf("grpc: parseServiceConfig error unmarshaling %s due to methodConfig[%d]: %v", js, i, err)
-				return &serviceconfig.ParseResult{Err: err}
+		for _, n := range *m.Name {
+			if path, valid := n.generatePath(); valid {
+				sc.Methods[path] = mc
 			}
-			paths[path] = struct{}{}
-			sc.Methods[path] = mc
 		}
 	}
 
@@ -377,7 +357,7 @@ func convertRetryPolicy(jrp *jsonRetryPolicy) (p *retryPolicy, err error) {
 		*mb <= 0 ||
 		jrp.BackoffMultiplier <= 0 ||
 		len(jrp.RetryableStatusCodes) == 0 {
-		logger.Warningf("grpc: ignoring retry policy %v due to illegal configuration", jrp)
+		grpclog.Warningf("grpc: ignoring retry policy %v due to illegal configuration", jrp)
 		return nil, nil
 	}
 
diff --git a/vendor/google.golang.org/grpc/stream.go b/vendor/google.golang.org/grpc/stream.go
index fbc3fb11..629af76b 100644
--- a/vendor/google.golang.org/grpc/stream.go
+++ b/vendor/google.golang.org/grpc/stream.go
@@ -35,7 +35,6 @@ import (
 	"google.golang.org/grpc/internal/binarylog"
 	"google.golang.org/grpc/internal/channelz"
 	"google.golang.org/grpc/internal/grpcrand"
-	"google.golang.org/grpc/internal/grpcutil"
 	"google.golang.org/grpc/internal/transport"
 	"google.golang.org/grpc/metadata"
 	"google.golang.org/grpc/peer"
@@ -347,16 +346,7 @@ func (cs *clientStream) newAttemptLocked(sh stats.Handler, trInfo *traceInfo) (r
 	if err := cs.ctx.Err(); err != nil {
 		return toRPCErr(err)
 	}
-
-	ctx := cs.ctx
-	if cs.cc.parsedTarget.Scheme == "xds" {
-		// Add extra metadata (metadata that will be added by transport) to context
-		// so the balancer can see them.
-		ctx = grpcutil.WithExtraMetadata(cs.ctx, metadata.Pairs(
-			"content-type", grpcutil.ContentType(cs.callHdr.ContentSubtype),
-		))
-	}
-	t, done, err := cs.cc.getTransport(ctx, cs.callInfo.failFast, cs.callHdr.Method)
+	t, done, err := cs.cc.getTransport(cs.ctx, cs.callInfo.failFast, cs.callHdr.Method)
 	if err != nil {
 		return err
 	}
@@ -520,13 +510,13 @@ func (cs *clientStream) shouldRetry(err error) error {
 		if len(sps) == 1 {
 			var e error
 			if pushback, e = strconv.Atoi(sps[0]); e != nil || pushback < 0 {
-				channelz.Infof(logger, cs.cc.channelzID, "Server retry pushback specified to abort (%q).", sps[0])
+				channelz.Infof(cs.cc.channelzID, "Server retry pushback specified to abort (%q).", sps[0])
 				cs.retryThrottler.throttle() // This counts as a failure for throttling.
 				return err
 			}
 			hasPushback = true
 		} else if len(sps) > 1 {
-			channelz.Warningf(logger, cs.cc.channelzID, "Server retry pushback specified multiple values (%q); not retrying.", sps)
+			channelz.Warningf(cs.cc.channelzID, "Server retry pushback specified multiple values (%q); not retrying.", sps)
 			cs.retryThrottler.throttle() // This counts as a failure for throttling.
 			return err
 		}
diff --git a/vendor/google.golang.org/grpc/version.go b/vendor/google.golang.org/grpc/version.go
index fb878dda..c7e67b84 100644
--- a/vendor/google.golang.org/grpc/version.go
+++ b/vendor/google.golang.org/grpc/version.go
@@ -19,4 +19,4 @@
 package grpc
 
 // Version is the current grpc version.
-const Version = "1.31.0"
+const Version = "1.30.0"
diff --git a/vendor/google.golang.org/grpc/vet.sh b/vendor/google.golang.org/grpc/vet.sh
index 8b7dff19..f0a67298 100644
--- a/vendor/google.golang.org/grpc/vet.sh
+++ b/vendor/google.golang.org/grpc/vet.sh
@@ -108,10 +108,10 @@ if [[ -z "${VET_SKIP_PROTO}" ]]; then
     (git status; git --no-pager diff; exit 1)
 fi
 
-# - Check that our modules are tidy.
+# - Check that our module is tidy.
 if go help mod >& /dev/null; then
-  find . -name 'go.mod' | xargs -IXXX bash -c 'cd $(dirname XXX); go mod tidy'
-  git status --porcelain 2>&1 | fail_on_output || \
+  go mod tidy && \
+    git status --porcelain 2>&1 | fail_on_output || \
     (git status; git --no-pager diff; exit 1)
 fi
 
@@ -135,7 +135,6 @@ balancer.Picker
 grpc.CallCustomCodec
 grpc.Code
 grpc.Compressor
-grpc.CustomCodec
 grpc.Decompressor
 grpc.MaxMsgSize
 grpc.MethodConfig
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 23813811..24ab14b0 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -181,7 +181,7 @@ github.com/sam-kamerer/go-plister
 github.com/skratchdot/open-golang/open
 # github.com/soheilhy/cmux v0.1.4
 github.com/soheilhy/cmux
-# github.com/spf13/afero v1.3.3
+# github.com/spf13/afero v1.3.2
 github.com/spf13/afero
 github.com/spf13/afero/mem
 # github.com/spf13/cast v1.3.0
@@ -192,7 +192,7 @@ github.com/spf13/cobra
 github.com/spf13/jwalterweatherman
 # github.com/spf13/pflag v1.0.5
 github.com/spf13/pflag
-# github.com/spf13/viper v1.7.1
+# github.com/spf13/viper v1.7.0
 github.com/spf13/viper
 # github.com/stretchr/testify v1.6.1
 github.com/stretchr/testify/assert
@@ -301,7 +301,7 @@ google.golang.org/appengine/internal/urlfetch
 google.golang.org/appengine/urlfetch
 # google.golang.org/genproto v0.0.0-20200624020401-64a14ca9d1ad
 google.golang.org/genproto/googleapis/rpc/status
-# google.golang.org/grpc v1.31.0
+# google.golang.org/grpc v1.30.0
 google.golang.org/grpc
 google.golang.org/grpc/attributes
 google.golang.org/grpc/backoff
@@ -325,7 +325,6 @@ google.golang.org/grpc/internal/balancerload
 google.golang.org/grpc/internal/binarylog
 google.golang.org/grpc/internal/buffer
 google.golang.org/grpc/internal/channelz
-google.golang.org/grpc/internal/credentials
 google.golang.org/grpc/internal/envconfig
 google.golang.org/grpc/internal/grpclog
 google.golang.org/grpc/internal/grpcrand
diff --git a/web/package-lock.json b/web/package-lock.json
index 35c73cd1..6a7d56ea 100644
--- a/web/package-lock.json
+++ b/web/package-lock.json
@@ -14332,9 +14332,9 @@
       }
     },
     "elliptic": {
-      "version": "6.5.3",
-      "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz",
-      "integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==",
+      "version": "6.5.2",
+      "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.2.tgz",
+      "integrity": "sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw==",
       "dev": true,
       "requires": {
         "bn.js": "^4.4.0",
diff --git a/web/src/app/modules/shared/components/presentation/content-switcher/content-switcher.component.html b/web/src/app/modules/shared/components/presentation/content-switcher/content-switcher.component.html
index ce62b315..c9554baa 100644
--- a/web/src/app/modules/shared/components/presentation/content-switcher/content-switcher.component.html
+++ b/web/src/app/modules/shared/components/presentation/content-switcher/content-switcher.component.html
@@ -98,6 +98,9 @@
     <ng-container *ngSwitchCase="'terminal'">
       <app-terminal [view]="view"></app-terminal>
     </ng-container>
+    <ng-container *ngSwitchCase="'stepper'">
+      <app-stepper [view]="view"></app-stepper>
+    </ng-container>
     <div class="alert alert-danger static" *ngSwitchDefault>
       <span class="alert-icon-wrapper">
         <clr-icon class="alert-icon" shape="exclamation-circle"></clr-icon>
diff --git a/web/src/app/modules/shared/components/presentation/stepper/stepper.component.html b/web/src/app/modules/shared/components/presentation/stepper/stepper.component.html
new file mode 100644
index 00000000..a57c8fe7
--- /dev/null
+++ b/web/src/app/modules/shared/components/presentation/stepper/stepper.component.html
@@ -0,0 +1,114 @@
+<form
+  clrStepper
+  [formGroup]="formGroup"
+>
+  <div
+    class="step-item"
+    *ngFor="let step of steps; index as i; trackBy: trackByFn"
+  >
+    <clr-stepper-panel [formGroupName]="step.name">
+      <clr-step-title>{{ step.title }}</clr-step-title>
+      <clr-step-description>{{ step.description }}</clr-step-description>
+      <clr-step-content>
+        <div *ngFor="let field of step.form?.fields; trackBy: trackByFn">
+            <ng-container [ngSwitch]="field.type">
+                <ng-container *ngSwitchCase="'checkbox'">
+                  <clr-checkbox-container>
+                    <label [for]="field.name">{{ field.label }}</label>
+                    <clr-checkbox-wrapper
+                      *ngFor="let opt of fieldChoices(field); trackBy: trackByFn"
+                    >
+                      <input
+                        type="checkbox"
+                        clrCheckbox
+                        [formControlName]="field.name"
+                        [value]="opt.value"
+                        [checked]="opt.checked"
+                      />
+                      <label>{{ opt.label }}</label>
+                    </clr-checkbox-wrapper>
+                    <clr-control-error>{{ field.error }}</clr-control-error>
+                  </clr-checkbox-container>
+                </ng-container>
+                <ng-container *ngSwitchCase="'radio'">
+                  <clr-radio-container>
+                    <label [for]="field.name">{{ field.label }}</label>
+                    <clr-radio-wrapper
+                      *ngFor="let opt of fieldChoices(field); trackBy: trackByFn"
+                    >
+                      <input
+                        type="radio"
+                        clrRadio
+                        [formControlName]="field.name"
+                        [value]="opt.value"
+                      />
+                      <label>{{ opt.label }}</label>
+                    </clr-radio-wrapper>
+                    <clr-control-error>{{ field.error }}</clr-control-error>
+                  </clr-radio-container>
+                </ng-container>
+                <ng-container *ngSwitchCase="'text'">
+                  <clr-input-container>
+                    <label [for]="field.name">{{ field.label }}</label>
+                    <input clrInput type="text" [name]="field.name" [formControlName]="field.name" [placeholder]="field.placeholder ? field.placeholder : ''" />
+                    <clr-control-error>{{ field.error }}</clr-control-error>
+                  </clr-input-container>
+                </ng-container>
+                <ng-container *ngSwitchCase="'number'">
+                  <clr-input-container>
+                    <label [for]="field.name">{{ field.label }}</label>
+                    <input clrInput type="number" [formControlName]="field.name" />
+                    <clr-control-error>{{ field.error }}</clr-control-error>
+                  </clr-input-container>
+                </ng-container>
+                <ng-container *ngSwitchCase="'password'">
+                  <clr-input-container>
+                    <label [for]="field.name">{{ field.label }}</label>
+                    <input clrInput type="password" [formControlName]="field.name" />
+                    <clr-control-error>{{ field.error }}</clr-control-error>
+                  </clr-input-container>
+                </ng-container>
+                <ng-container *ngSwitchCase="'select'">
+                  <clr-select-container>
+                    <label [for]="field.name">{{ field.label }}</label>
+                    <select
+                      clrSelect
+                      [formControlName]="field.name"
+                      [multiple]="field.configuration.multiple"
+                    >
+                      <option
+                        *ngFor="let opt of fieldChoices(field); trackBy: trackByFn"
+                        [value]="opt.value"
+                      >
+                        {{ opt.label }}
+                      </option>
+                    </select>
+                    <clr-control-error>{{ field.error }}</clr-control-error>
+                  </clr-select-container>
+                </ng-container>
+                <ng-container *ngSwitchCase="'textarea'">
+                  <clr-textarea-container>
+                    <label [for]="field.name">{{ field.label }}</label>
+                    <textarea clrTextarea [formControlName]="field.name"></textarea>
+                    <clr-control-error>{{ field.error }}</clr-control-error>
+                  </clr-textarea-container>
+                </ng-container>
+                <ng-container *ngSwitchCase="'hidden'"> </ng-container>
+                <ng-container *ngSwitchDefault>
+                  Unable to display form field type {{ field.type }}
+                </ng-container>
+              </ng-container>
+        </div>
+        <button
+          *ngIf="v.config.steps.length - 1 === i; else elseBlock"
+          clrStepButton="submit"
+        >
+          submit
+        </button>
+        <ng-template #elseBlock
+          ><button clrStepButton="next">next</button></ng-template
+        >
+      </clr-step-content>
+    </clr-stepper-panel>
+  </div>
+</form>
diff --git a/web/src/app/modules/shared/components/presentation/stepper/stepper.component.sass b/web/src/app/modules/shared/components/presentation/stepper/stepper.component.sass
new file mode 100644
index 00000000..e69de29b
diff --git a/web/src/app/modules/shared/components/presentation/stepper/stepper.component.spec.ts b/web/src/app/modules/shared/components/presentation/stepper/stepper.component.spec.ts
new file mode 100644
index 00000000..87c91d77
--- /dev/null
+++ b/web/src/app/modules/shared/components/presentation/stepper/stepper.component.spec.ts
@@ -0,0 +1,54 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { StepperComponent } from './stepper.component';
+import { FormBuilder, ReactiveFormsModule } from '@angular/forms';
+import { StepperView } from '../../../models/content';
+import {
+  BrowserAnimationsModule,
+  NoopAnimationsModule,
+} from '@angular/platform-browser/animations';
+
+describe('StepperComponent', () => {
+  let component: StepperComponent;
+  let fixture: ComponentFixture<StepperComponent>;
+  const formBuilder: FormBuilder = new FormBuilder();
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      imports: [
+        ReactiveFormsModule,
+        BrowserAnimationsModule,
+        NoopAnimationsModule,
+      ],
+      providers: [{ provide: FormBuilder, useValue: formBuilder }],
+    }).compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(StepperComponent);
+    component = fixture.componentInstance;
+
+    const view: StepperView = {
+      metadata: {
+        type: 'stepper',
+      },
+      config: {
+        action: 'action.octant.dev/test',
+        steps: [
+          {
+            name: 'step name',
+            form: { fields: [] },
+            title: 'step title',
+            description: 'step description',
+          },
+        ],
+      },
+    };
+    component.view = view;
+
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/web/src/app/modules/shared/components/presentation/stepper/stepper.component.ts b/web/src/app/modules/shared/components/presentation/stepper/stepper.component.ts
new file mode 100644
index 00000000..e6ab4ed7
--- /dev/null
+++ b/web/src/app/modules/shared/components/presentation/stepper/stepper.component.ts
@@ -0,0 +1,114 @@
+import {
+  Component,
+  OnInit,
+  Input,
+  SimpleChanges,
+  EventEmitter,
+  Output,
+  OnChanges,
+  OnDestroy,
+  isDevMode,
+} from '@angular/core';
+import {
+  StepperView,
+  View,
+  ActionField,
+  StepItem,
+} from '../../../models/content';
+import {
+  FormGroup,
+  FormBuilder,
+  Validators,
+  ValidatorFn,
+} from '@angular/forms';
+import { WebsocketService } from '../../../services/websocket/websocket.service';
+
+interface Choice {
+  label: string;
+  value: string;
+  checked: boolean;
+}
+
+@Component({
+  selector: 'app-stepper',
+  templateUrl: './stepper.component.html',
+  styleUrls: ['./stepper.component.sass'],
+})
+export class StepperComponent implements OnInit {
+  private v: StepperView;
+
+  @Input() set view(v: View) {
+    this.v = v as StepperView;
+  }
+  get view() {
+    return this.v;
+  }
+
+  @Output()
+  submit: EventEmitter<FormGroup> = new EventEmitter(true);
+
+  @Output()
+  cancel: EventEmitter<boolean> = new EventEmitter(true);
+
+  formGroup: FormGroup;
+  action: string;
+  steps: StepItem[];
+
+  constructor(
+    private formBuilder: FormBuilder,
+    private websocketService: WebsocketService
+  ) {
+    this.steps = [] as StepItem[];
+  }
+
+  ngOnInit() {
+    if (this.v) {
+      this.action = this.v.config.action;
+      this.steps = this.v.config.steps;
+
+      const stepGroups: { [name: string]: any } = {};
+      this.steps?.forEach(step => {
+        const controls: { [name: string]: any } = {};
+        step.form?.fields?.forEach(field => {
+          controls[field.name] = [
+            field.value,
+            this.getValidators(field.validators),
+          ];
+        });
+        stepGroups[step.name] = this.formBuilder.group(controls);
+      });
+      this.formGroup = this.formBuilder.group(stepGroups);
+    }
+  }
+
+  onFormSubmit() {
+    const form = JSON.stringify(this.formGroup);
+    this.websocketService.sendMessage(this.action, form);
+    if (isDevMode()) {
+      console.log('stepper form: ' + form);
+    }
+  }
+
+  onFormCancel() {
+    this.cancel.emit(true);
+  }
+
+  trackByFn(index, item) {
+    return index;
+  }
+
+  getValidators(validators: string[]): ValidatorFn[] {
+    if (validators) {
+      const vFn: ValidatorFn[] = [];
+      validators.forEach(v => {
+        vFn.push(Validators[v]);
+      });
+      return vFn;
+    }
+    return [];
+  }
+
+  fieldChoices(field: ActionField) {
+    return field.configuration.choices as Choice[];
+  }
+}
diff --git a/web/src/app/modules/shared/models/content.ts b/web/src/app/modules/shared/models/content.ts
index 91d29e1b..e69819c8 100644
--- a/web/src/app/modules/shared/models/content.ts
+++ b/web/src/app/modules/shared/models/content.ts
@@ -272,6 +272,9 @@ export interface ActionField {
   name: string;
   type: string;
   value: any;
+  placeholder: string;
+  error: string;
+  validators: string[];
 }
 
 export interface ActionForm {
@@ -494,3 +497,17 @@ export interface CodeView extends View {
     value: string;
   };
 }
+
+export interface StepItem {
+  name: string;
+  form: ActionForm;
+  title: string;
+  description: string;
+}
+
+export interface StepperView extends View {
+  config: {
+    action: string;
+    steps: StepItem[];
+  };
+}
diff --git a/web/src/app/modules/shared/shared.module.ts b/web/src/app/modules/shared/shared.module.ts
index 7e60b0c9..4ccf7d75 100644
--- a/web/src/app/modules/shared/shared.module.ts
+++ b/web/src/app/modules/shared/shared.module.ts
@@ -71,6 +71,7 @@ import { HelperComponent } from './components/smart/helper/helper.component';
 import { FilterDeletedDatagridRowPipe } from './pipes/filterDeletedDatagridRow/filter-deleted-datagrid-row.pipe';
 import { ContentTextFilterComponent } from './components/presentation/content-text-filter/content-text-filter.component';
 import { TruncatePipe } from './pipes/truncate/truncate.pipe';
+import { StepperComponent } from './components/presentation/stepper/stepper.component';
 
 @NgModule({
   declarations: [
@@ -137,6 +138,7 @@ import { TruncatePipe } from './pipes/truncate/truncate.pipe';
     PreferencesComponent,
     HelperComponent,
     ContentTextFilterComponent,
+    StepperComponent,
   ],
   imports: [
     ClarityModule,
@@ -210,6 +212,7 @@ import { TruncatePipe } from './pipes/truncate/truncate.pipe';
     OverflowLabelsComponent,
     PreferencesComponent,
     HelperComponent,
+    StepperComponent,
   ],
 })
 export class SharedModule {}
diff --git a/web/src/stories/stepper.stories.mdx b/web/src/stories/stepper.stories.mdx
new file mode 100644
index 00000000..a6e7d1d1
--- /dev/null
+++ b/web/src/stories/stepper.stories.mdx
@@ -0,0 +1,95 @@
+import { moduleMetadata } from '@storybook/angular';
+import { Meta, Story, Preview, Props } from '@storybook/addon-docs/blocks';
+import { object, withKnobs } from '@storybook/addon-knobs';
+import { StepperComponent } from '../app/modules/shared/components/presentation/stepper/stepper.component';
+
+export const stepperDocs = {
+  source: { code: `title = "Example Stepper" component.NewStepper()` },
+};
+
+export const stepperView = {
+  metadata: {
+    type: 'stepper',
+  },
+  config: {
+    action: 'octant.dev/test-action',
+    steps: [
+      {
+        title: 'Contact Information',
+        description: 'Personal contact information.',
+        name: 'contact',
+        form: { fields: [
+          {
+            placeholder: 'First name',
+            label: 'First Name',
+            name: 'firstName',
+            type: 'text',
+            value: '',
+            error: 'First name is required.',
+            validators: ['required'],
+          },
+          {
+            label: 'Last Name',
+            name: 'lastName',
+            type: 'text',
+            value: '',
+          },
+        ],
+        },
+      },
+      {
+        title: 'More Information',
+        description: 'Extended information.',
+        name: 'extended',
+        form: {fields: [
+          {
+            label: 'City',
+            name: 'city',
+            type: 'text',
+            value: '',
+          },
+          {
+            label: 'State',
+            name: 'state',
+            type: 'select',
+            error: 'State is required.',
+            validators: ['required'],
+            configuration: {
+              choices: [
+                { label: '', value: '' },
+                { label: 'North Carolina', value: 'NC' },
+              ],
+            },
+          },
+        ],
+        },
+      },
+    ],
+  },
+};
+
+<h1>Stepper component</h1>
+<h2>Description</h2>
+
+<p>The Stepper component provides guided steps to a user</p>
+<h2>Example</h2>
+
+<Meta title="Components/Stepper" component={StepperComponent} />
+
+<Preview withToolbar>
+  <Story
+    name="Stepper component"
+    parameters={{ docs: stepperDocs }}
+    height="450px"
+  >
+    {{
+      props: {
+        view: object('View', stepperView),
+      },
+      component: StepperComponent,
+    }}
+  </Story>
+</Preview>
+
+<h2>Props</h2>
+<Props of={StepperComponent} />
diff --git a/web/tsconfig.compodoc.json b/web/tsconfig.compodoc.json
index 8c5c1dab..a3751745 100644
--- a/web/tsconfig.compodoc.json
+++ b/web/tsconfig.compodoc.json
@@ -178,6 +178,8 @@
     "src//app/modules/shared/components/presentation/summary/summary.component.spec.ts",
     "src//app/modules/shared/components/presentation/selectors/selectors.component.spec.ts",
     "src//app/modules/shared/components/presentation/selectors/selectors.component.ts",
+    "src//app/modules/shared/components/presentation/stepper/stepper.component.spec.ts",
+    "src//app/modules/shared/components/presentation/stepper/stepper.component.ts",
     "src//app/modules/shared/components/presentation/button-group/button-group.component.spec.ts",
     "src//app/modules/shared/components/presentation/button-group/button-group.component.ts",
     "src//app/modules/shared/components/presentation/donut-chart/donut-chart.component.spec.ts",


Test patch:
diff --git a/pkg/dash/dash_test.go b/pkg/dash/dash_test.go
deleted file mode 100644
index b6df94b4..00000000
--- a/pkg/dash/dash_test.go
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (c) 2020 the Octant contributors. All Rights Reserved.
- * SPDX-License-Identifier: Apache-2.0
- *
- */
-
-package dash
-
-import (
-	"path/filepath"
-	"testing"
-
-	"github.com/spf13/afero"
-	"github.com/stretchr/testify/assert"
-	"github.com/stretchr/testify/require"
-
-	"github.com/vmware-tanzu/octant/internal/log"
-)
-
-func TestRunner_ValidateKubeconfig(t *testing.T) {
-	fs := afero.NewMemMapFs()
-	afero.WriteFile(fs, "/test1", []byte(""), 0755)
-	afero.WriteFile(fs, "/test2", []byte(""), 0755)
-
-	separator := string(filepath.ListSeparator)
-
-	tests := []struct {
-		name     string
-		fileList string
-		expected string
-		isErr    bool
-	}{
-		{
-			name:     "single path",
-			fileList: "/test1",
-			expected: "/test1",
-			isErr:    false,
-		},
-		{
-			name:     "multiple paths",
-			fileList: "/test1" + separator + "/test2",
-			expected: "/test1" + separator + "/test2",
-			isErr:    false,
-		},
-		{
-			name:     "single path not found",
-			fileList: "/unknown",
-			expected: "",
-			isErr:    true,
-		},
-		{
-			name:     "multiple paths not found",
-			fileList: "/unknown" + separator + "/unknown2",
-			expected: "",
-			isErr:    true,
-		},
-		{
-			name:     "multiple file path; missing a config",
-			fileList: "/test1" + separator + "/unknown",
-			expected: "/test1",
-			isErr:    false,
-		},
-		{
-			name:     "invalid path",
-			fileList: "not a filepath",
-			expected: "",
-			isErr:    true,
-		},
-	}
-
-	for _, test := range tests {
-		t.Run(test.name, func(t *testing.T) {
-			logger := log.NopLogger()
-			path, err := ValidateKubeConfig(logger, test.fileList, fs)
-			if test.isErr {
-				require.Error(t, err)
-			} else {
-				require.NoError(t, err)
-			}
-			assert.Equal(t, path, test.expected)
-		})
-	}
-}
diff --git a/pkg/view/component/stepper_test.go b/pkg/view/component/stepper_test.go
new file mode 100644
index 00000000..b5a0035e
--- /dev/null
+++ b/pkg/view/component/stepper_test.go
@@ -0,0 +1,62 @@
+package component
+
+import (
+	"encoding/json"
+	"io/ioutil"
+	"path"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+)
+
+func Test_stepper_Marshal(t *testing.T) {
+	form := Form{}
+	form.Fields = append(form.Fields, NewFormFieldText("test", "test", "test"))
+
+	tests := []struct {
+		name         string
+		input        Component
+		expectedPath string
+		isErr        bool
+	}{
+		{
+			name: "general",
+			input: &Stepper{
+				base: newBase(typeStepper, TitleFromString("my stepper")),
+				Config: StepperConfig{
+					Action: "action.octant.dev/stepperTest",
+					Steps: []StepConfig{
+						{
+							Name:        "Step 1",
+							Title:       "First Step",
+							Description: "Setup step",
+							Form:        form,
+						},
+						{
+							Name:        "Step 2",
+							Title:       "Second Step",
+							Description: "Confirmation step",
+							Form:        form,
+						},
+					},
+				},
+			},
+			expectedPath: "stepper.json",
+		},
+	}
+
+	for _, tc := range tests {
+		t.Run(tc.name, func(t *testing.T) {
+			actual, err := json.Marshal(tc.input)
+			isErr := err != nil
+			if isErr != tc.isErr {
+				t.Fatalf("Unexpected error: %v", err)
+			}
+
+			expected, err := ioutil.ReadFile(path.Join("testdata", tc.expectedPath))
+			require.NoError(t, err, "reading test fixtures")
+			assert.JSONEq(t, string(expected), string(actual))
+		})
+	}
+}
diff --git a/pkg/view/component/testdata/config_stepper.json b/pkg/view/component/testdata/config_stepper.json
new file mode 100644
index 00000000..f9f7ca09
--- /dev/null
+++ b/pkg/view/component/testdata/config_stepper.json
@@ -0,0 +1,27 @@
+{
+    "action": "action.octant.dev/stepperTest",
+    "steps": [
+      {
+        "name": "Step 1",
+        "title": "First Step",
+        "description": "Setup step",
+        "form": {
+          "fields": [
+            {
+              "type": "text",
+              "label": "test",
+              "name": "test",
+              "value": "test",
+              "configuration": {}
+            }
+          ]
+        }
+      },
+      {
+        "name": "Step 2",
+        "title": "Second Step",
+        "description": "Confirmation step",
+        "form": {}
+      }
+    ]
+  }
diff --git a/pkg/view/component/testdata/stepper.json b/pkg/view/component/testdata/stepper.json
new file mode 100644
index 00000000..c695f5eb
--- /dev/null
+++ b/pkg/view/component/testdata/stepper.json
@@ -0,0 +1,48 @@
+{
+  "metadata": {
+    "type": "stepper",
+    "title": [
+      {
+        "config": { "value": "my stepper" },
+        "metadata": { "type": "text" }
+      }
+    ]
+  },
+  "config": {
+    "action": "action.octant.dev/stepperTest",
+    "steps": [
+      {
+        "name": "Step 1",
+        "title": "First Step",
+        "description": "Setup step",
+        "form": {
+          "fields": [
+            {
+              "type": "text",
+              "label": "test",
+              "name": "test",
+              "value": "test",
+              "configuration": {}
+            }
+          ]
+        }
+      },
+      {
+        "name": "Step 2",
+        "title": "Second Step",
+        "description": "Confirmation step",
+        "form": {
+          "fields": [
+            {
+              "type": "text",
+              "label": "test",
+              "name": "test",
+              "value": "test",
+              "configuration": {}
+            }
+          ]
+        }
+      }
+    ]
+  }
+}
diff --git a/pkg/view/component/unmarshal_test.go b/pkg/view/component/unmarshal_test.go
index 189fb32a..2e09c3ac 100644
--- a/pkg/view/component/unmarshal_test.go
+++ b/pkg/view/component/unmarshal_test.go
@@ -353,6 +353,28 @@ func Test_unmarshal(t *testing.T) {
 				base: newBase(typeSingleStat, nil),
 			},
 		},
+		{
+			name:       "stepper",
+			configFile: "config_stepper.json",
+			objectType: "stepper",
+			expected: &Stepper{
+				Config: StepperConfig{
+					Action: "action.octant.dev/stepperTest",
+					Steps: []StepConfig{{
+						Name:        "Step 1",
+						Title:       "First Step",
+						Description: "Setup step",
+						Form:        Form{Fields: []FormField{NewFormFieldText("test", "test", "test")}},
+					}, {
+						Name:        "Step 2",
+						Title:       "Second Step",
+						Description: "Confirmation step",
+						Form:        Form{},
+					}},
+				},
+				base: newBase(typeStepper, nil),
+			},
+		},
 		{
 			name:       "summary",
 			configFile: "config_summary.json",