From 86951ad14dcf618adab920e2c7cbdc6005e1f586 Mon Sep 17 00:00:00 2001 From: Haytham AbuelFutuh Date: Wed, 3 Jun 2020 09:56:01 -0700 Subject: [PATCH] Initial Commit --- Makefile | 2 + cmd/root.go | 55 ++++ cmd/testdata/config.yaml | 2 + cmd/testdata/expected.png | Bin 0 -> 62189 bytes cmd/testdata/listNodeExecutions.pb | 1 + cmd/timeline.go | 197 +++++++++++++ cmd/timeline_test.go | 178 ++++++++++++ config.yaml | 5 + go.mod | 29 ++ go.sum | 377 +++++++++++++++++++++++++ graph/timeline_chart.go | 436 +++++++++++++++++++++++++++++ main.go | 9 + 12 files changed, 1291 insertions(+) create mode 100644 Makefile create mode 100644 cmd/root.go create mode 100644 cmd/testdata/config.yaml create mode 100644 cmd/testdata/expected.png create mode 100755 cmd/testdata/listNodeExecutions.pb create mode 100644 cmd/timeline.go create mode 100644 cmd/timeline_test.go create mode 100644 config.yaml create mode 100644 go.mod create mode 100644 go.sum create mode 100644 graph/timeline_chart.go create mode 100644 main.go diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..73c7355a --- /dev/null +++ b/Makefile @@ -0,0 +1,2 @@ +generate: + go test github.com/lyft/flytectl/cmd --update \ No newline at end of file diff --git a/cmd/root.go b/cmd/root.go new file mode 100644 index 00000000..3953bd7f --- /dev/null +++ b/cmd/root.go @@ -0,0 +1,55 @@ +package cmd + +import ( + "context" + + "github.com/lyft/flytestdlib/config" + "github.com/lyft/flytestdlib/config/viper" + "github.com/spf13/cobra" +) + +var ( + cfgFile string + configAccessor = viper.NewAccessor(config.Options{StrictMode: true}) +) + +type persistentFlags struct { + Project *string + Domain *string +} + +func newRootCmd() *cobra.Command { + persistentFlags := persistentFlags{} + rootCmd := &cobra.Command{ + PersistentPreRunE: initConfig, + } + + rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", + "config file (default is $HOME/config.yaml)") + + configAccessor.InitializePflags(rootCmd.PersistentFlags()) + + persistentFlags.Project = rootCmd.PersistentFlags().String("project", "", "Specifies the Flyte project.") + persistentFlags.Domain = rootCmd.PersistentFlags().String("domain", "", "Specifies the Flyte project's domain.") + + rootCmd.AddCommand(newTimelineCmd(persistentFlags)) + return rootCmd +} + +func initConfig(_ *cobra.Command, _ []string) error { + configAccessor = viper.NewAccessor(config.Options{ + StrictMode: true, + SearchPaths: []string{cfgFile}, + }) + + err := configAccessor.UpdateConfig(context.TODO()) + if err != nil { + return err + } + + return nil +} + +func ExecuteCmd() error { + return newRootCmd().Execute() +} diff --git a/cmd/testdata/config.yaml b/cmd/testdata/config.yaml new file mode 100644 index 00000000..202261b1 --- /dev/null +++ b/cmd/testdata/config.yaml @@ -0,0 +1,2 @@ +admin: + endpoint: dns:///flyte.lyft.net diff --git a/cmd/testdata/expected.png b/cmd/testdata/expected.png new file mode 100644 index 0000000000000000000000000000000000000000..76c2086c5cf07908bcfad224daf36306c64bc271 GIT binary patch literal 62189 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 0 { + // return fmt.Errorf(result.Message) + //} + // + ////save to file + //if flags.OutputPath != nil { + // return result.Context.SavePNG(*flags.OutputPath) + //} + // +} diff --git a/cmd/timeline_test.go b/cmd/timeline_test.go new file mode 100644 index 00000000..047d12be --- /dev/null +++ b/cmd/timeline_test.go @@ -0,0 +1,178 @@ +package cmd + +import ( + "bytes" + "context" + "flag" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "testing" + + "github.com/golang/protobuf/jsonpb" + + "github.com/stretchr/testify/mock" + + adminIdl "github.com/lyft/flyteidl/gen/pb-go/flyteidl/admin" + coreIdl "github.com/lyft/flyteidl/gen/pb-go/flyteidl/core" + + "github.com/lyft/flyteidl/clients/go/admin" + "github.com/lyft/flytestdlib/config" + "github.com/lyft/flytestdlib/config/viper" + + "github.com/lyft/flyteidl/clients/go/admin/mocks" + + "github.com/stretchr/testify/assert" +) + +var update = flag.Bool("update", false, "Updates testdata") + +func refStr(s string) *string { + return &s +} + +func copyFile(src, dst string) error { + sourceFileStat, err := os.Stat(src) + if err != nil { + return err + } + + if !sourceFileStat.Mode().IsRegular() { + return fmt.Errorf("%s is not a regular file", src) + } + + source, err := os.Open(src) + if err != nil { + return err + } + + destination, err := os.Create(dst) + if err != nil { + err2 := source.Close() + if err2 != nil { + return config.ErrorCollection([]error{err, err2}) + } + + return err + } + + errs := make([]error, 0) + _, err = io.Copy(destination, source) + if err != nil { + errs = append(errs, err) + } + + err2 := source.Close() + if err2 != nil { + errs = append(errs, err2) + } + + err3 := destination.Close() + if err3 != nil { + errs = append(errs, err3) + } + + if len(errs) > 0 { + return config.ErrorCollection(errs) + } + + return nil +} + +func Test_updateAdminResponse(t *testing.T) { + if !*update { + t.SkipNow() + } + + accessor := viper.NewAccessor(config.Options{ + SearchPaths: []string{filepath.Join("testdata", "config.yaml")}, + }) + + ctx := context.Background() + assert.NoError(t, accessor.UpdateConfig(ctx)) + c := admin.InitializeAdminClient(ctx, *admin.GetConfig(ctx)) + resp, err := c.ListNodeExecutions(ctx, &adminIdl.NodeExecutionListRequest{ + WorkflowExecutionId: &coreIdl.WorkflowExecutionIdentifier{ + Project: "priceoptimizeroffline", + Domain: "production", + Name: "eqwdb3jwg7", + }, + Limit: 100, + }) + + assert.NoError(t, err) + + if err != nil { + t.FailNow() + } + + m := &jsonpb.Marshaler{} + var buf bytes.Buffer + err = m.Marshal(&buf, resp) + assert.NoError(t, err) + assert.NoError(t, ioutil.WriteFile(filepath.Join("testdata", "listNodeExecutions.pb"), buf.Bytes(), os.ModePerm)) +} + +func Test_visualizeTimeline(t *testing.T) { + ctx := context.Background() + + respBytes, err := ioutil.ReadFile(filepath.Join("testdata", "listNodeExecutions.pb")) + assert.NoError(t, err) + + resp := &adminIdl.NodeExecutionList{} + assert.NoError(t, jsonpb.Unmarshal(bytes.NewReader(respBytes), resp)) + + m := &mocks.AdminServiceClient{} + m.OnListNodeExecutionsMatch(mock.Anything, mock.Anything, mock.Anything).Return(resp, nil) + assert.NoError(t, visualizeTimeline(ctx, m, timelineFlags{ + persistentFlags: persistentFlags{ + Project: refStr("priceoptimizeroffline"), + Domain: refStr("production"), + }, + ExecutionName: refStr("eqwdb3jwg7"), + })) +} + +func Test_visualizeTimeline_test_output(t *testing.T) { + ctx := context.Background() + + respBytes, err := ioutil.ReadFile(filepath.Join("testdata", "listNodeExecutions.pb")) + assert.NoError(t, err) + + resp := &adminIdl.NodeExecutionList{} + assert.NoError(t, jsonpb.Unmarshal(bytes.NewReader(respBytes), resp)) + + tmpLoc, err := ioutil.TempFile(os.TempDir(), "visualize_time_line.png") + assert.NoError(t, err) + assert.NoError(t, tmpLoc.Close()) + + m := &mocks.AdminServiceClient{} + m.OnListNodeExecutionsMatch(mock.Anything, mock.Anything, mock.Anything).Return(resp, nil) + assert.NoError(t, visualizeTimeline(ctx, m, timelineFlags{ + persistentFlags: persistentFlags{ + Project: refStr("priceoptimizeroffline"), + Domain: refStr("production"), + }, + ExecutionName: refStr("eqwdb3jwg7"), + OutputPath: refStr(tmpLoc.Name()), + })) + + expectedPath := filepath.Join("testdata", "expected.png") + if *update { + assert.NoError(t, copyFile(tmpLoc.Name(), expectedPath)) + } + + expectedBytes, err := ioutil.ReadFile(expectedPath) + assert.NoError(t, err) + + actualBytes, err := ioutil.ReadFile(tmpLoc.Name()) + assert.NoError(t, err) + + if assert.Equal(t, expectedBytes, actualBytes) { + assert.NoError(t, os.Remove(tmpLoc.Name())) + } else { + t.Logf("Files are different, expected file [%v] vs actual file [%v]", expectedPath, tmpLoc.Name()) + } +} diff --git a/config.yaml b/config.yaml new file mode 100644 index 00000000..30f9deca --- /dev/null +++ b/config.yaml @@ -0,0 +1,5 @@ +admin: + endpoint: dns:///flyte.lyft.net +logger: + show-source: true + level: 5 diff --git a/go.mod b/go.mod new file mode 100644 index 00000000..1091e73a --- /dev/null +++ b/go.mod @@ -0,0 +1,29 @@ +module github.com/lyft/flytectl + +go 1.13 + +require ( + github.com/blend/go-sdk v2.0.0+incompatible // indirect + github.com/coreos/go-oidc v2.1.0+incompatible // indirect + github.com/enghabu/timeline v0.0.0-00010101000000-000000000000 + github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 + github.com/golang/protobuf v1.3.2 + github.com/grpc-ecosystem/go-grpc-middleware v1.1.0 // indirect + github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect + github.com/grpc-ecosystem/grpc-gateway v1.12.1 // indirect + github.com/lyft/flyteidl v0.16.3 + github.com/lyft/flytestdlib v0.2.31 + github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect + github.com/spf13/cobra v0.0.5 + github.com/stretchr/objx v0.2.0 // indirect + github.com/stretchr/testify v1.4.0 + github.com/wcharczuk/go-chart v2.0.1+incompatible + golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6 // indirect + google.golang.org/genproto v0.0.0-20191223191004-3caeed10a8bf // indirect + google.golang.org/grpc v1.26.0 // indirect + gopkg.in/square/go-jose.v2 v2.4.1 // indirect +) + +replace github.com/gerald1248/timeline => /Users/hamabuelfutuh/src/go/src/github.com/enghabu/timeline + +replace github.com/enghabu/timeline => /Users/hamabuelfutuh/src/go/src/github.com/enghabu/timeline diff --git a/go.sum b/go.sum new file mode 100644 index 00000000..bea78978 --- /dev/null +++ b/go.sum @@ -0,0 +1,377 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.47.0/go.mod h1:5p3Ky/7f3N10VBkhuR5LFtddroTiMyjZV/Kj5qOQFxU= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/Azure/azure-sdk-for-go v10.2.1-beta+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/go-autorest/autorest v0.9.2/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= +github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= +github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= +github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= +github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/antihax/optional v0.0.0-20180407024304-ca021399b1a6/go.mod h1:V8iCPQYkqmusNa815XgQio277wI47sdRh1dUOLdyC6Q= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/aws/aws-sdk-go v1.25.16/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/benlaurie/objecthash v0.0.0-20180202135721-d1e3d6079fc1/go.mod h1:jvdWlw8vowVGnZqSDC7yhPd7AifQeQbRDkZcQXV2nRg= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/blend/go-sdk v2.0.0+incompatible h1:FL9X/of4ZYO5D2JJNI4vHrbXPfuSDbUa7h8JP9+E92w= +github.com/blend/go-sdk v2.0.0+incompatible/go.mod h1:3GUb0YsHFNTJ6hsJTpzdmCUl05o8HisKjx5OAlzYKdw= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927/go.mod h1:h/aW8ynjgkuj+NQRlZcDbAbM1ORAbXjXX77sX7T289U= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/coocood/freecache v1.1.0/go.mod h1:ePwxCDzOYvARfHdr1pByNct1at3CoKnsipOHwKlNbzI= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-oidc v2.1.0+incompatible h1:sdJrfw8akMnCuUlaZU3tE/uYXFgfqom8DBE9so9EBsM= +github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= +github.com/elazarl/go-bindata-assetfs v1.0.0 h1:G/bYguwHIzWq9ZoyUQqrjTmJbbYn3j3CKKpKinvZLFk= +github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/ernesto-jimenez/gogen v0.0.0-20180125220232-d7d4131e6607/go.mod h1:Cg4fM0vhYWOZdgM7RIOSTRNIc8/VT7CXClC3Ni86lu4= +github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/structtag v1.1.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= +github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8= +github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.8-0.20191012010759-4bf2d1fec783 h1:SmsgwFZy9pdTk/k8BZz40D3P5umP5+Ejt3hAi0paBNQ= +github.com/fsnotify/fsnotify v1.4.8-0.20191012010759-4bf2d1fec783/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191002201903-404acd9df4cc/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/graymeta/stow v0.0.0-20190522170649-903027f87de7/go.mod h1:B24dekNjtWVeREK+dyMHtI22d85VzCT+sX5bVWDtjoA= +github.com/grpc-ecosystem/go-grpc-middleware v1.1.0 h1:THDBEeQ9xZ8JEaCLyLQqXMMdRqNr0QAUJTIkQAUtFjg= +github.com/grpc-ecosystem/go-grpc-middleware v1.1.0/go.mod h1:f5nM7jw/oeRSadq3xCzHAvxcr8HZnzsqU6ILg/0NiiE= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.12.1 h1:zCy2xE9ablevUOrUZc3Dl72Dt+ya2FNAvC2yLYMHzi4= +github.com/grpc-ecosystem/grpc-gateway v1.12.1/go.mod h1:8XEsbTttt/W+VvjtQhLACqCisSPWTxCZ7sBRjU6iH9c= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kabukky/httpscerts v0.0.0-20150320125433-617593d7dcb3 h1:Iy7Ifq2ysilWU4QlCx/97OoI4xT1IV7i8byT/EyIT/M= +github.com/kabukky/httpscerts v0.0.0-20150320125433-617593d7dcb3/go.mod h1:BYpt4ufZiIGv2nXn4gMxnfKV306n3mWXgNu/d2TqdTU= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/lyft/flyteidl v0.16.3 h1:Qs7qIcx8nfRlf5W8A0Asel+KJPx2TAgyz5X63Fmw/Qg= +github.com/lyft/flyteidl v0.16.3/go.mod h1:T7xTtpDpVHUmQ6D6RYTVA/258gjlg/pM1E1FXSF3GmE= +github.com/lyft/flytestdlib v0.2.31 h1:JAOSGwy/wLprhq1KR9zxekBqnKdSlAQQG1x4KQe+hlI= +github.com/lyft/flytestdlib v0.2.31/go.mod h1:/fqNXKCGChEvMzcRapVq6vDM69Vlusl+bCj7foToaUQ= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= +github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW10= +github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= +github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/ncw/swift v1.0.49-0.20190728102658-a24ef33bc9b7/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pelletier/go-toml v1.5.0 h1:5BakdOZdtKJ1FFk6QdL8iSGrMWsXgchNJcrnarjbmJQ= +github.com/pelletier/go-toml v1.5.0/go.mod h1:5N711Q9dKgbdkxHL+MEfF31hpT7l0S0s/t2kKREewys= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 h1:J9b7z+QKAmPf4YLrFg6oQUotqHQeUNWwkvo7jZp1GLU= +github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.0.0 h1:vrDKnkGzuGvhNAL56c7DBz29ZL+KxnoR0x7enabFceM= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.7.0 h1:L+1lyG48J1zAQXA3RBX/nG/B3gjlHq0zTt2tlbJLyCY= +github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.5 h1:3+auTFlqw+ZaQYJARz6ArODtkaIwtvBTx3N2NehQlL8= +github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/satori/uuid v1.2.0/go.mod h1:B8HLsPLik/YNn6KKWVMDJ8nzCL8RP5WyfsnmvnAEwIU= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +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 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= +github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +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.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +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 h1:VUFqw5KcqRf7i70GOzW7N+Q7+gxVBkSSqiXB12+JQ4M= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/wcharczuk/go-chart v2.0.1+incompatible h1:0pz39ZAycJFF7ju/1mepnk26RLVLBCWz1STcD3doU0A= +github.com/wcharczuk/go-chart v2.0.1+incompatible/go.mod h1:PF5tmL4EIx/7Wf+hEkpCqYi5He4u90sw+0+6FhrryuE= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.1/go.mod h1:Ap50jQcDJrx6rB6VgeeFPtuPIf3wMRvRfrfYDO6+BmA= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5 h1:58fnuSXlxZmFdJyvtTFVmVhcMLU6v5fEb/ok4wyqtNU= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20191214001246-9130b4cfad52 h1:2fktqPPvDiVEEVT/vSTeoUPXfmRxRaGy6GU8jypvEn0= +golang.org/x/image v0.0.0-20191214001246-9130b4cfad52/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191021144547-ec77196f6094 h1:5O4U9trLjNpuhpynaDsqwCk+Tw6seqJz1EbqbnzHrc8= +golang.org/x/net v0.0.0-20191021144547-ec77196f6094/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6 h1:pE8b58s1HRDMi8RDc79m0HISf9D4TzseP40cEA6IGfs= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191020212454-3e7259c5e7c2 h1:nq114VpM8lsSlP+lyUbANecYHYiFcSNFtqcBlxRV+gA= +golang.org/x/sys v0.0.0-20191020212454-3e7259c5e7c2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190921001708-c4c64cad1fd0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191010171213-8abd42400456/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191204011308-9611592c72f6/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.11.1-0.20191020000718-bf72a15fd9e9/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20190927181202-20e1ac93f88c/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191009194640-548a555dbc03/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191223191004-3caeed10a8bf h1:1x8rC5/IgdLMPbPTvlQTN28+rcy8XL9Q19UWUMDyqYs= +google.golang.org/genproto v0.0.0-20191223191004-3caeed10a8bf/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= +google.golang.org/grpc v1.26.0 h1:2dTRdpdFEEhJYQD8EMLB61nnrzSCTbG38PhqdhvOltg= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/square/go-jose.v2 v2.4.1 h1:H0TmLt7/KmzlrDOpa1F+zr0Tk90PbJYBfsVUmRLrf9Y= +gopkg.in/square/go-jose.v2 v2.4.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo= +gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +k8s.io/apimachinery v0.0.0-20181127025237-2b1284ed4c93 h1:tT6oQBi0qwLbbZSfDkdIsb23EwaLY85hoAV4SpXfdao= +k8s.io/apimachinery v0.0.0-20181127025237-2b1284ed4c93/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0= +k8s.io/client-go v0.0.0-20181213151034-8d9ed539ba31/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s= +k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= diff --git a/graph/timeline_chart.go b/graph/timeline_chart.go new file mode 100644 index 00000000..11da5973 --- /dev/null +++ b/graph/timeline_chart.go @@ -0,0 +1,436 @@ +package graph + +import ( + "errors" + "fmt" + "io" + "math" + + chart "github.com/wcharczuk/go-chart" + + "github.com/golang/freetype/truetype" + "github.com/wcharczuk/go-chart/seq" + util "github.com/wcharczuk/go-chart/util" +) + +type BarSection struct { + StartValue float64 + Length float64 +} + +// BarSections is an array of Value. +type BarSections []BarSection + +//// BarSections returns the values. +//func (vs BarSections) Values() []float64 { +// values := make([]float64, len(vs)) +// for index, v := range vs { +// values[index] = v.Length +// } +// +// return values +//} + +//// ValuesNormalized returns normalized values. +//func (vs BarSections) ValuesNormalized() []float64 { +// return util.Math.Normalize(vs.Values()...) +//} + +// Normalize returns the values normalized. +func (vs BarSections) Normalize(minValue, maxValue float64) []BarSection { + var output []BarSection + for _, v := range vs { + if v.Length > 0 { + output = append(output, BarSection{ + StartValue: util.Math.RoundUp((v.StartValue-minValue)/(maxValue-minValue), 0.0001), + Length: util.Math.RoundUp(v.Length/(maxValue-minValue), 0.0001), + }) + } + } + + return output +} + +// Bar is a bar within a TimelineChart. +type Bar struct { + Name string + Style chart.Style + Sections BarSections +} + +// GetHeight returns the width of the bar. +func (sb Bar) GetHeight() int { + return 15 +} + +// TimelineChart is a chart that draws sections of a bar based on percentages. +type TimelineChart struct { + Title string + TitleStyle chart.Style + + ColorPalette chart.ColorPalette + + Width int + Height int + DPI float64 + + Background chart.Style + Canvas chart.Style + + XAxis chart.Style + YAxis chart.Style + + BarSpacing int + + Font *truetype.Font + defaultFont *truetype.Font + + Bars []Bar + Elements []chart.Renderable +} + +// GetDPI returns the dpi for the chart. +func (sbc TimelineChart) GetDPI(defaults ...float64) float64 { + if sbc.DPI == 0 { + if len(defaults) > 0 { + return defaults[0] + } + return chart.DefaultDPI + } + return sbc.DPI +} + +// GetFont returns the text font. +func (sbc TimelineChart) GetFont() *truetype.Font { + if sbc.Font == nil { + return sbc.defaultFont + } + return sbc.Font +} + +// GetHeight returns the chart width or the default value. +func (sbc TimelineChart) GetWidth() int { + if sbc.Width == 0 { + return chart.DefaultChartWidth + } + return sbc.Width +} + +// GetHeight returns the chart height or the default value. +func (sbc TimelineChart) GetHeight() int { + if sbc.Height == 0 { + return chart.DefaultChartHeight + } + return sbc.Height +} + +// GetBarSpacing returns the spacing between bars. +func (sbc TimelineChart) GetBarSpacing() int { + if sbc.BarSpacing == 0 { + return 100 + } + return sbc.BarSpacing +} + +// Render renders the chart with the given renderer to the given io.Writer. +func (sbc TimelineChart) Render(rp chart.RendererProvider, w io.Writer) error { + if len(sbc.Bars) == 0 { + return errors.New("please provide at least one bar") + } + + r, err := rp(sbc.GetWidth(), sbc.GetHeight()) + if err != nil { + return err + } + + if sbc.Font == nil { + defaultFont, err := chart.GetDefaultFont() + if err != nil { + return err + } + sbc.defaultFont = defaultFont + } + r.SetDPI(sbc.GetDPI(chart.DefaultDPI)) + + canvasBox := sbc.getAdjustedCanvasBox(r, sbc.getDefaultCanvasBox()) + sbc.drawCanvas(r, canvasBox) + sbc.drawBars(r, canvasBox) + sbc.drawXAxis(r, canvasBox) + sbc.drawYAxis(r, canvasBox) + + sbc.drawTitle(r) + for _, a := range sbc.Elements { + a(r, canvasBox, sbc.styleDefaultsElements()) + } + + return r.Save(w) +} + +func (sbc TimelineChart) drawCanvas(r chart.Renderer, canvasBox chart.Box) { + chart.Draw.Box(r, canvasBox, sbc.getCanvasStyle()) +} + +func (sbc TimelineChart) drawBars(r chart.Renderer, canvasBox chart.Box) { + yoffset := canvasBox.Bottom + for _, bar := range sbc.Bars { + sbc.drawBar(r, canvasBox, sbc.getMinValue(), sbc.getMaxValue(), yoffset, bar) + yoffset -= sbc.GetBarSpacing() - bar.GetHeight() + } +} + +func (sbc TimelineChart) getMaxValue() float64 { + maxValue := float64(0) + for _, bar := range sbc.Bars { + for _, section := range bar.Sections { + maxValue = math.Max(maxValue, section.Length+section.StartValue) + } + } + + return maxValue +} + +func (sbc TimelineChart) getMinValue() float64 { + maxValue := math.MaxFloat64 + for _, bar := range sbc.Bars { + for _, section := range bar.Sections { + maxValue = math.Min(maxValue, section.StartValue) + } + } + + return maxValue +} + +func (sbc TimelineChart) drawBar(r chart.Renderer, canvasBox chart.Box, minValue, maxValue float64, yoffset int, bar Bar) int { + barSpacing2 := sbc.GetBarSpacing() >> 1 + bxl := yoffset + barSpacing2 + bxr := bxl + bar.GetHeight() + + normalizedBarComponents := bar.Sections.Normalize(minValue, maxValue) + for index, bv := range normalizedBarComponents { + barWidth := int(math.Ceil(bv.Length * float64(canvasBox.Width()))) + barStart := int(math.Ceil(bv.StartValue * float64(canvasBox.Width()))) + barBox := chart.Box{ + Top: bxl, + Left: util.Math.MinInt(barStart, canvasBox.Right-chart.DefaultStrokeWidth), + Right: util.Math.MinInt(barStart+barWidth, canvasBox.Right-chart.DefaultStrokeWidth), + Bottom: bxr, + } + + chart.Draw.Box(r, barBox, bar.Style.InheritFrom(sbc.styleDefaultsStackedBarValue(index))) + } + + return bxr +} + +func (sbc TimelineChart) drawXAxis(r chart.Renderer, canvasBox chart.Box) { + if sbc.XAxis.Show { + axisStyle := sbc.XAxis.InheritFrom(sbc.styleDefaultsAxes()) + axisStyle.WriteToRenderer(r) + + r.MoveTo(canvasBox.Left, canvasBox.Bottom) + r.LineTo(canvasBox.Right, canvasBox.Bottom) + r.Stroke() + + r.MoveTo(canvasBox.Left, canvasBox.Bottom) + r.LineTo(canvasBox.Left, canvasBox.Bottom+chart.DefaultVerticalTickHeight) + r.Stroke() + + cursor := canvasBox.Left + for _, bar := range sbc.Bars { + + barLabelBox := chart.Box{ + Top: canvasBox.Bottom + chart.DefaultXAxisMargin, + Left: cursor, + Right: cursor + bar.GetHeight() + sbc.GetBarSpacing(), + Bottom: sbc.GetHeight(), + } + if len(bar.Name) > 0 { + chart.Draw.TextWithin(r, bar.Name, barLabelBox, axisStyle) + } + axisStyle.WriteToRenderer(r) + r.MoveTo(barLabelBox.Right, canvasBox.Bottom) + r.LineTo(barLabelBox.Right, canvasBox.Bottom+chart.DefaultVerticalTickHeight) + r.Stroke() + cursor += bar.GetHeight() + sbc.GetBarSpacing() + } + } +} + +func (sbc TimelineChart) drawXAxis(r chart.Renderer, canvasBox chart.Box) { + if sbc.XAxis.Show { + axisStyle := sbc.XAxis.InheritFrom(sbc.styleDefaultsAxes()) + axisStyle.WriteToRenderer(r) + r.MoveTo(canvasBox.Right, canvasBox.Top) + r.LineTo(canvasBox.Right, canvasBox.Bottom) + r.Stroke() + + r.MoveTo(canvasBox.Right, canvasBox.Bottom) + r.LineTo(canvasBox.Right+chart.DefaultHorizontalTickWidth, canvasBox.Bottom) + r.Stroke() + + ticks := seq.RangeWithStep(0.0, 1.0, 0.2) + for _, t := range ticks { + axisStyle.GetStrokeOptions().WriteToRenderer(r) + ty := canvasBox.Bottom - int(t*float64(canvasBox.Height())) + r.MoveTo(canvasBox.Right, ty) + r.LineTo(canvasBox.Right+chart.DefaultHorizontalTickWidth, ty) + r.Stroke() + + axisStyle.GetTextOptions().WriteToRenderer(r) + text := fmt.Sprintf("%0.0f%%", t*100) + + tb := r.MeasureText(text) + chart.Draw.Text(r, text, canvasBox.Right+chart.DefaultYAxisMargin+5, ty+(tb.Height()>>1), axisStyle) + } + + } +} + +func (sbc TimelineChart) drawTitle(r chart.Renderer) { + if len(sbc.Title) > 0 && sbc.TitleStyle.Show { + r.SetFont(sbc.TitleStyle.GetFont(sbc.GetFont())) + r.SetFontColor(sbc.TitleStyle.GetFontColor(sbc.GetColorPalette().TextColor())) + titleFontSize := sbc.TitleStyle.GetFontSize(chart.DefaultTitleFontSize) + r.SetFontSize(titleFontSize) + + textBox := r.MeasureText(sbc.Title) + + textWidth := textBox.Width() + textHeight := textBox.Height() + + titleX := (sbc.GetWidth() >> 1) - (textWidth >> 1) + titleY := sbc.TitleStyle.Padding.GetTop(chart.DefaultTitleTop) + textHeight + + r.Text(sbc.Title, titleX, titleY) + } +} + +func (sbc TimelineChart) getCanvasStyle() chart.Style { + return sbc.Canvas.InheritFrom(sbc.styleDefaultsCanvas()) +} + +func (sbc TimelineChart) styleDefaultsCanvas() chart.Style { + return chart.Style{ + FillColor: sbc.GetColorPalette().CanvasColor(), + StrokeColor: sbc.GetColorPalette().CanvasStrokeColor(), + StrokeWidth: chart.DefaultCanvasStrokeWidth, + } +} + +// GetColorPalette returns the color palette for the chart. +func (sbc TimelineChart) GetColorPalette() chart.ColorPalette { + if sbc.ColorPalette != nil { + return sbc.ColorPalette + } + return chart.AlternateColorPalette +} + +func (sbc TimelineChart) getDefaultCanvasBox() chart.Box { + return sbc.Box() +} + +func (sbc TimelineChart) getAdjustedCanvasBox(r chart.Renderer, canvasBox chart.Box) chart.Box { + var totalHeight int + for _, bar := range sbc.Bars { + totalHeight += bar.GetHeight() + sbc.GetBarSpacing() + } + // + //if sbc.YAxis.Show { + // yaxisHeight := chart.DefaultHorizontalTickWidth + // + // axisStyle := sbc.YAxis.InheritFrom(sbc.styleDefaultsAxes()) + // axisStyle.WriteToRenderer(r) + // + // cursor := canvasBox.Bottom + // for _, bar := range sbc.Bars { + // if len(bar.Name) > 0 { + // barLabelBox := chart.Box{ + // Top: cursor + bar.GetHeight() + sbc.GetBarSpacing(), //canvasBox.Bottom + chart.DefaultXAxisMargin, + // Left: canvasBox.Left, //cursor, + // Right: cursor + bar.GetHeight() + sbc.GetBarSpacing(), + // Bottom: sbc.GetHeight(), + // } + // lines := chart.Text.WrapFit(r, bar.Name, barLabelBox.Width(), axisStyle) + // linesBox := chart.Text.MeasureLines(r, lines, axisStyle) + // + // yaxisHeight = util.Math.MaxInt(linesBox.Height()+(2*chart.DefaultXAxisMargin), yaxisHeight) + // } + // } + // return chart.Box{ + // Top: canvasBox.Top, + // Left: canvasBox.Left, + // Right: canvasBox.Left + totalHeight, + // Bottom: sbc.GetHeight() - yaxisHeight, + // } + //} + return chart.Box{ + Top: canvasBox.Top, + Left: canvasBox.Left, + Right: canvasBox.Left + totalHeight, + Bottom: canvasBox.Bottom, + } + +} + +// Box returns the chart bounds as a box. +func (sbc TimelineChart) Box() chart.Box { + dpr := sbc.Background.Padding.GetRight(10) + dpb := sbc.Background.Padding.GetBottom(50) + + return chart.Box{ + Top: sbc.Background.Padding.GetTop(20), + Left: sbc.Background.Padding.GetLeft(20), + Right: sbc.GetWidth() - dpr, + Bottom: sbc.GetHeight() - dpb, + } +} + +func (sbc TimelineChart) styleDefaultsStackedBarValue(index int) chart.Style { + return chart.Style{ + StrokeColor: sbc.GetColorPalette().GetSeriesColor(index), + StrokeWidth: 3.0, + FillColor: sbc.GetColorPalette().GetSeriesColor(index), + } +} + +func (sbc TimelineChart) styleDefaultsTitle() chart.Style { + return sbc.TitleStyle.InheritFrom(chart.Style{ + FontColor: chart.DefaultTextColor, + Font: sbc.GetFont(), + FontSize: sbc.getTitleFontSize(), + TextHorizontalAlign: chart.TextHorizontalAlignCenter, + TextVerticalAlign: chart.TextVerticalAlignTop, + TextWrap: chart.TextWrapWord, + }) +} + +func (sbc TimelineChart) getTitleFontSize() float64 { + effectiveDimension := util.Math.MinInt(sbc.GetWidth(), sbc.GetHeight()) + if effectiveDimension >= 2048 { + return 48 + } else if effectiveDimension >= 1024 { + return 24 + } else if effectiveDimension >= 512 { + return 18 + } else if effectiveDimension >= 256 { + return 12 + } + return 10 +} + +func (sbc TimelineChart) styleDefaultsAxes() chart.Style { + return chart.Style{ + StrokeColor: chart.DefaultAxisColor, + Font: sbc.GetFont(), + FontSize: chart.DefaultAxisFontSize, + FontColor: chart.DefaultAxisColor, + TextHorizontalAlign: chart.TextHorizontalAlignCenter, + TextVerticalAlign: chart.TextVerticalAlignTop, + TextWrap: chart.TextWrapWord, + } +} +func (sbc TimelineChart) styleDefaultsElements() chart.Style { + return chart.Style{ + Font: sbc.GetFont(), + } +} diff --git a/main.go b/main.go new file mode 100644 index 00000000..d6dbf7f4 --- /dev/null +++ b/main.go @@ -0,0 +1,9 @@ +package main + +import "github.com/lyft/flytectl/cmd" + +func main() { + if err := cmd.ExecuteCmd(); err != nil { + panic(err) + } +}