cli: format/state refactor for new state format

format/state now requires provider schemas to properly format output
state. command/show has been modified to pass a context to format.State.
pull/18949/head
Kristin Laemmert 8 years ago
parent 764a7d61c8
commit 10e58dfabb

@ -6,10 +6,13 @@ import (
"sort"
"strings"
"github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/plans"
"github.com/mitchellh/colorstring"
"github.com/hashicorp/terraform/states"
"github.com/hashicorp/terraform/terraform"
// "github.com/hashicorp/terraform/terraform"
)
// StateOpts are the options for formatting a state.
@ -19,10 +22,6 @@ type StateOpts struct {
// Color is the colorizer. This is optional.
Color *colorstring.Colorize
// ModuleDepth is the depth of the modules to expand. By default this
// is zero which will not expand modules at all.
ModuleDepth int
}
// State takes a state and returns a string
@ -36,65 +35,58 @@ func State(opts *StateOpts) string {
return "The state file is empty. No resources are represented."
}
// FIXME: State formatter not yet updated for new state types
return "FIXME: State formatter not yet updated for new state types"
/*var buf bytes.Buffer
var buf bytes.Buffer
buf.WriteString("[reset]")
p := blockBodyDiffPrinter{
buf: &buf,
color: opts.Color,
action: plans.NoOp,
}
// Format all the modules
for _, m := range s.Modules {
if len(m.Path)-1 <= opts.ModuleDepth || opts.ModuleDepth == -1 {
formatStateModuleExpand(&buf, m, opts)
} else {
formatStateModuleSingle(&buf, m, opts)
}
formatStateModule(&buf, m, opts)
}
// Write the outputs for the root module
m := s.RootModule()
if len(m.Outputs) > 0 {
buf.WriteString("\nOutputs:\n\n")
if m.OutputValues != nil {
if len(m.OutputValues) > 0 {
buf.WriteString("\nOutputs:\n\n")
}
// Sort the outputs
ks := make([]string, 0, len(m.Outputs))
for k, _ := range m.Outputs {
ks := make([]string, 0, len(m.OutputValues))
for k := range m.OutputValues {
ks = append(ks, k)
}
sort.Strings(ks)
// Output each output k/v pair
for _, k := range ks {
v := m.Outputs[k]
switch output := v.Value.(type) {
case string:
buf.WriteString(fmt.Sprintf("%s = %s", k, output))
buf.WriteString("\n")
case []interface{}:
buf.WriteString(formatListOutput("", k, output))
buf.WriteString("\n")
case map[string]interface{}:
buf.WriteString(formatMapOutput("", k, output))
buf.WriteString("\n")
}
v := m.OutputValues[k]
buf.WriteString(fmt.Sprintf("%s = ", k))
p.writeValue(v.Value, plans.NoOp, 0)
}
}
return opts.Color.Color(strings.TrimSpace(buf.String()))
*/
}
func formatStateModuleExpand(
buf *bytes.Buffer, m *terraform.ModuleState, opts *StateOpts) {
func formatStateModule(
buf *bytes.Buffer, m *states.Module, opts *StateOpts) {
var moduleName string
if !m.IsRoot() {
moduleName = fmt.Sprintf("module.%s", strings.Join(m.Path[1:], "."))
if !m.Addr.IsRoot() {
moduleName = fmt.Sprintf("module.%s", m.Addr.String())
}
// First get the names of all the resources so we can show them
// in alphabetical order.
names := make([]string, 0, len(m.Resources))
for name, _ := range m.Resources {
for name := range m.Resources {
names = append(names, name)
}
sort.Strings(names)
@ -106,55 +98,69 @@ func formatStateModuleExpand(
name = moduleName + "." + name
}
rs := m.Resources[k]
is := rs.Primary
var id string
if is != nil {
id = is.ID
}
if id == "" {
id = "<not created>"
addr := m.Resources[k].Addr
switch addr.Mode {
case addrs.ManagedResourceMode:
buf.WriteString(fmt.Sprintf(
"resource %q %q",
addr.Type,
addr.Name,
))
case addrs.DataResourceMode:
buf.WriteString(fmt.Sprintf(
"data %q %q ",
addr.Type,
addr.Name,
))
default:
// should never happen, since the above is exhaustive
buf.WriteString(addr.String())
}
taintStr := ""
if rs.Primary != nil && rs.Primary.Tainted {
taintStr = " (tainted)"
}
buf.WriteString(" { attrs go here! }\n")
buf.WriteString(fmt.Sprintf("%s:%s\n", name, taintStr))
buf.WriteString(fmt.Sprintf(" id = %s\n", id))
if is != nil {
// Sort the attributes
attrKeys := make([]string, 0, len(is.Attributes))
for ak, _ := range is.Attributes {
// Skip the id attribute since we just show the id directly
if ak == "id" {
continue
}
attrKeys = append(attrKeys, ak)
}
sort.Strings(attrKeys)
// Output each attribute
for _, ak := range attrKeys {
av := is.Attributes[ak]
buf.WriteString(fmt.Sprintf(" %s = %s\n", ak, av))
}
}
}
buf.WriteString("[reset]\n")
}
// rs := m.Resources[k]
// is := rs.Instance
// var id string
// if is != nil {
// id = is
// }
// if id == "" {
// id = "<not created>"
// }
// taintStr := ""
// // if rs.Primary != nil && rs.Primary.Tainted {
// // taintStr = " (tainted)"
// // }
// buf.WriteString(fmt.Sprintf("%s:%s\n", name, taintStr))
// buf.WriteString(fmt.Sprintf(" id = %s\n", id))
// if is != nil {
// // Sort the attributes
// attrKeys := make([]string, 0, len(is.Attributes))
// for ak, _ := range is.Attributes {
// // Skip the id attribute since we just show the id directly
// if ak == "id" {
// continue
// }
// attrKeys = append(attrKeys, ak)
// }
// sort.Strings(attrKeys)
// // Output each attribute
// for _, ak := range attrKeys {
// av := is.Attributes[ak]
// buf.WriteString(fmt.Sprintf(" %s = %s\n", ak, av))
// }
// }
// }
func formatStateModuleSingle(
buf *bytes.Buffer, m *terraform.ModuleState, opts *StateOpts) {
// Header with the module name
buf.WriteString(fmt.Sprintf("module.%s\n", strings.Join(m.Path[1:], ".")))
// Now just write how many resources are in here.
buf.WriteString(fmt.Sprintf(" %d resource(s)\n", len(m.Resources)))
buf.WriteString("[reset]\n")
}
func formatNestedList(indent string, outputList []interface{}) string {

@ -0,0 +1,82 @@
package format
import (
"fmt"
"testing"
"github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/states"
"github.com/mitchellh/colorstring"
"github.com/zclconf/go-cty/cty"
)
var disabledColorize = &colorstring.Colorize{
Colors: colorstring.DefaultColors,
Disable: true,
}
func TestState(t *testing.T) {
state := states.NewState()
rootModule := state.RootModule()
if rootModule == nil {
t.Errorf("root module is nil; want valid object")
}
rootModule.SetLocalValue("foo", cty.StringVal("foo value"))
rootModule.SetOutputValue("bar", cty.StringVal("bar value"), false)
rootModule.SetResourceInstanceCurrent(
addrs.Resource{
Mode: addrs.ManagedResourceMode,
Type: "test_thing",
Name: "baz",
}.Instance(addrs.IntKey(0)),
&states.ResourceInstanceObjectSrc{
Status: states.ObjectReady,
SchemaVersion: 1,
AttrsJSON: []byte(`{"woozles":"confuzles"}`),
},
addrs.ProviderConfig{
Type: "test",
}.Absolute(addrs.RootModuleInstance),
)
tests := []struct {
State *StateOpts
Want string
}{
{
&StateOpts{
State: &states.State{},
Color: disabledColorize,
},
"The state file is empty. No resources are represented.",
},
{
&StateOpts{
State: state,
Color: disabledColorize,
},
"module.test_module.test_resource.foo",
},
}
for _, tt := range tests {
got := State(tt.State)
if got != tt.Want {
t.Errorf(
"wrong result\ninput: %v\ngot: %s\nwant: %s",
tt.State.State, got, tt.Want,
)
}
}
}
func mustParseModuleInstanceStr(s string) addrs.ModuleInstance {
addr, err := addrs.ParseModuleInstanceStr(s)
if err != nil {
fmt.Printf(err.Err().Error())
panic(err)
}
return addr
}

@ -21,7 +21,6 @@ type ShowCommand struct {
}
func (c *ShowCommand) Run(args []string) int {
var moduleDepth int
args, err := c.Meta.process(args, false)
if err != nil {
@ -29,7 +28,7 @@ func (c *ShowCommand) Run(args []string) int {
}
cmdFlags := flag.NewFlagSet("show", flag.ContinueOnError)
c.addModuleDepthFlag(cmdFlags, &moduleDepth)
cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
if err := cmdFlags.Parse(args); err != nil {
return 1
@ -121,7 +120,6 @@ func (c *ShowCommand) Run(args []string) int {
c.Ui.Output(format.State(&format.StateOpts{
State: state,
Color: c.Colorize(),
ModuleDepth: moduleDepth,
}))
return 0
}

@ -2,6 +2,7 @@ module github.com/hashicorp/terraform
require (
cloud.google.com/go v0.15.0
contrib.go.opencensus.io/exporter/stackdriver v0.6.0 // indirect
github.com/Azure/azure-sdk-for-go v10.3.0-beta+incompatible
github.com/Azure/go-autorest v8.3.1+incompatible
github.com/Azure/go-ntlmssp v0.0.0-20170803034930-c92175d54006 // indirect
@ -19,7 +20,6 @@ require (
github.com/armon/go-radix v0.0.0-20160115234725-4239b77079c7 // indirect
github.com/aws/aws-sdk-go v1.12.75
github.com/beevik/etree v0.0.0-20171015221209-af219c0c7ea1 // indirect
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 // indirect
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
github.com/bgentry/speakeasy v0.0.0-20161015143505-675b82c74c0e // indirect
github.com/blang/semver v0.0.0-20170202183821-4a1e882c79dc
@ -94,7 +94,6 @@ require (
github.com/mattn/go-colorable v0.0.0-20160220075935-9cbef7c35391
github.com/mattn/go-isatty v0.0.0-20161123143637-30a891c33c7c // indirect
github.com/mattn/go-shellwords v1.0.1
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/miekg/dns v1.0.8 // indirect
github.com/mitchellh/cli v0.0.0-20171129193617-33edc47170b5
github.com/mitchellh/colorstring v0.0.0-20150917214807-8631ce90f286
@ -116,10 +115,6 @@ require (
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c // indirect
github.com/pkg/errors v0.8.0 // indirect
github.com/posener/complete v0.0.0-20171219111128-6bee943216c8
github.com/prometheus/client_golang v0.8.0 // indirect
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 // indirect
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e // indirect
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273 // indirect
github.com/satori/go.uuid v0.0.0-20160927100844-b061729afc07 // indirect
github.com/satori/uuid v0.0.0-20160927100844-b061729afc07 // indirect
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect
@ -139,15 +134,13 @@ require (
github.com/xanzy/ssh-agent v0.1.0
github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18 // indirect
github.com/xlab/treeprint v0.0.0-20161029104018-1d6e34225557
github.com/zclconf/go-cty v0.0.0-20180925180032-d9b87d891d0b
golang.org/x/crypto v0.0.0-20180910181607-0e37d006457b
golang.org/x/net v0.0.0-20180925072008-f04abc6bdfa7
golang.org/x/oauth2 v0.0.0-20170928010508-bb50c06baba3
golang.org/x/sys v0.0.0-20180925112736-b09afc3d579e // indirect
github.com/zclconf/go-cty v0.0.0-20180907002636-07dee8a1cfd4
go.opencensus.io v0.17.0 // indirect
golang.org/x/crypto v0.0.0-20180816225734-aabede6cba87
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2 // indirect
google.golang.org/api v0.0.0-20171005000305-7a7376eff6a5
google.golang.org/appengine v1.2.0 // indirect
google.golang.org/genproto v0.0.0-20171002232614-f676e0f3ac63 // indirect
google.golang.org/api v0.0.0-20180921000521-920bb1beccf7
google.golang.org/grpc v1.14.0
gopkg.in/airbrake/gobrake.v2 v2.0.9 // indirect
gopkg.in/fsnotify.v1 v1.4.7 // indirect

@ -1,5 +1,8 @@
cloud.google.com/go v0.15.0 h1:/e2wXYguItvFu4fJCvhMRPIwwrimuUxI+aCVx/ahLjg=
cloud.google.com/go v0.15.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
contrib.go.opencensus.io/exporter/stackdriver v0.6.0 h1:U0FQWsZU3aO8W+BrZc88T8fdd24qe3Phawa9V9oaVUE=
contrib.go.opencensus.io/exporter/stackdriver v0.6.0/go.mod h1:QeFzMJDAw8TXt5+aRaSuE8l5BwaMIOIlaVkBOPRuMuw=
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
github.com/Azure/azure-sdk-for-go v10.3.0-beta+incompatible h1:TP+nmGmOP7psi7CvIq/1pCliRBRj73vmMTDjaPrTnr8=
github.com/Azure/azure-sdk-for-go v10.3.0-beta+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/go-autorest v8.3.1+incompatible h1:1+jMCOJcCh3GmI7FGJVOo8AlfPWDyjS7fLbbkZGzEGY=
@ -239,6 +242,7 @@ github.com/onsi/ginkgo v1.6.0 h1:Ix8l273rp3QzYgXSR+c8d1fTG7UPgYkOSELPhiY/YGw=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.1 h1:PZSj/UFNaVp3KxrzHOcS7oyuWA7LoOY/77yCTEFu21U=
github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
github.com/packer-community/winrmcp v0.0.0-20180102160824-81144009af58 h1:m3CEgv3ah1Rhy82L+c0QG/U3VyY1UsvsIdkh0/rU97Y=
github.com/packer-community/winrmcp v0.0.0-20180102160824-81144009af58/go.mod h1:f6Izs6JvFTdnRbziASagjZ2vmf55NSIkC/weStxCHqk=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c h1:Lgl0gzECD8GnQ5QCWA8o6BtfL6mDH5rQgM4/fX3avOs=
@ -305,8 +309,12 @@ github.com/xlab/treeprint v0.0.0-20161029104018-1d6e34225557 h1:Jpn2j6wHkC9wJv5i
github.com/xlab/treeprint v0.0.0-20161029104018-1d6e34225557/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
github.com/zclconf/go-cty v0.0.0-20180815031001-58bb2bc0302a h1:x70ZZ4caA8eY4abjpcCnf6uvIPY3cpgRFrXE47JF4Sc=
github.com/zclconf/go-cty v0.0.0-20180815031001-58bb2bc0302a/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s=
github.com/zclconf/go-cty v0.0.0-20180925180032-d9b87d891d0b h1:9rQAtgrPBuyPjmPEcx4pqJs6D+u41FYbbVE/hhdsrtk=
github.com/zclconf/go-cty v0.0.0-20180925180032-d9b87d891d0b/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s=
github.com/zclconf/go-cty v0.0.0-20180831220647-752f6a689f5e h1:6R+foJ4aZJ3r0v9GajwCV7WuYCJ4J0H2ySXGUs093qc=
github.com/zclconf/go-cty v0.0.0-20180831220647-752f6a689f5e/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s=
github.com/zclconf/go-cty v0.0.0-20180907002636-07dee8a1cfd4 h1:C02D0gjAVFMKqFUaZvaZK2YWGK1HAQwVTZWDAENYDjA=
github.com/zclconf/go-cty v0.0.0-20180907002636-07dee8a1cfd4/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s=
go.opencensus.io v0.17.0 h1:2Cu88MYg+1LU+WVD+NWwYhyP0kKgRlN9QjWGaX0jKTE=
go.opencensus.io v0.17.0/go.mod h1:mp1VrMQxhlqqDpKvH4UcQUa4YwlzNmymAjPrDdfxNpI=
golang.org/x/crypto v0.0.0-20180816225734-aabede6cba87 h1:gCHhzI+1R9peHIMyiWVxoVaWlk1cYK7VThX5ptLtbXY=
golang.org/x/crypto v0.0.0-20180816225734-aabede6cba87/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20180910181607-0e37d006457b h1:2b9XGzhjiYsYPnKXoEfL7klWZQIt8IfyRCz62gCqqlQ=
@ -318,24 +326,30 @@ golang.org/x/net v0.0.0-20180925072008-f04abc6bdfa7 h1:zKzVgSQ8WOSHzD7I4k8LQjrHU
golang.org/x/net v0.0.0-20180925072008-f04abc6bdfa7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/oauth2 v0.0.0-20170928010508-bb50c06baba3 h1:YGx0PRKSN/2n/OcdFycCC0JUA/Ln+i5lPcN8VoNDus0=
golang.org/x/oauth2 v0.0.0-20170928010508-bb50c06baba3/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180816055513-1c9583448a9c h1:uHnKXcvx6SNkuwC+nrzxkJ+TpPwZOtumbhWrrOYN5YA=
golang.org/x/sys v0.0.0-20180816055513-1c9583448a9c/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180925112736-b09afc3d579e h1:LSlw/Dbj0MkNvPYAAkGinYmGliq+aqS7eKPYlE4oWC4=
golang.org/x/sys v0.0.0-20180925112736-b09afc3d579e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2 h1:+DCIGbF/swA92ohVg0//6X2IVY3KZs6p9mix0ziNYJM=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
google.golang.org/api v0.0.0-20171005000305-7a7376eff6a5 h1:PDkJGYjSvxJyevtZRGmBSO+HjbIKuqYEEc8gB51or4o=
google.golang.org/api v0.0.0-20171005000305-7a7376eff6a5/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.0.0-20180921000521-920bb1beccf7 h1:XKT3Wlpn+o6Car1ot74Z4R+R9CeRfITCLZb0Q9/mpx4=
google.golang.org/api v0.0.0-20180921000521-920bb1beccf7/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/appengine v1.1.0 h1:igQkv0AAhEIvTEpD5LIpAfav2eeVO9HBTjvKHVJPRSs=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.2.0 h1:S0iUepdCWODXRvtE+gcRDd15L+k+k1AiHlMiMjefH24=
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20171002232614-f676e0f3ac63 h1:yNBw5bwywOTguAu+h6SkCUaWdEZ7ZXgfiwb2YTN1eQw=
google.golang.org/genproto v0.0.0-20171002232614-f676e0f3ac63/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b h1:lohp5blsw53GBXtLyLNaTXPXS9pJ1tiTw61ZHUoE9Qw=
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/grpc v1.14.0 h1:ArxJuB1NWfPY6r9Gp9gqwplT0Ge7nqv9msgu03lHLmo=
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
gopkg.in/airbrake/gobrake.v2 v2.0.9 h1:7z2uVWwn7oVeeugY1DtlPAy5H+KYgB1KeKTnqjNatLo=

Loading…
Cancel
Save