Skip to content

Commit 5264168

Browse files
committed
feat(route): support multi backend
1 parent 03e0fc3 commit 5264168

8 files changed

Lines changed: 150 additions & 98 deletions

File tree

cmd/cmd.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ type Commander interface {
5656
GatewaysAdd(string, string, int, string) error
5757
GatewaysList(string, int) error
5858
GatewaysRemove(string, string, int, string) error
59-
RoutesCreate(string, string, string, string, int) error
59+
RoutesCreate(string, string, string, ...api.BackendRefRequest) error
6060
RoutesList(string, int) error
6161
RoutesGet(string, string) error
6262
RoutesSet(string, string, string) error

cmd/routes.go

Lines changed: 25 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@ package cmd
33
import (
44
"fmt"
55
"os"
6+
"strings"
67

8+
"github.com/drycc/controller-sdk-go/api"
79
"github.com/drycc/controller-sdk-go/routes"
810
"sigs.k8s.io/yaml"
911
)
1012

1113
// RoutesCreate create a route to an app.
12-
func (d *DryccCmd) RoutesCreate(appID, name string, ptype string, kind string, port int) error {
14+
func (d *DryccCmd) RoutesCreate(appID, name string, kind string, backendRefs ...api.BackendRefRequest) error {
1315
s, appID, err := load(d.ConfigFile, appID)
1416

1517
if err != nil {
@@ -18,7 +20,7 @@ func (d *DryccCmd) RoutesCreate(appID, name string, ptype string, kind string, p
1820
d.Printf("Adding route %s to %s... ", name, appID)
1921

2022
quit := progress(d.WOut)
21-
err = routes.New(s.Client, appID, name, ptype, kind, port)
23+
err = routes.New(s.Client, appID, name, kind, backendRefs...)
2224
quit <- true
2325
<-quit
2426
if d.checkAPICompatibility(s.Client, err) != nil {
@@ -47,31 +49,30 @@ func (d *DryccCmd) RoutesList(appID string, results int) error {
4749
if count == 0 {
4850
d.Println(fmt.Sprintf("No routes found in %s app.", appID))
4951
} else {
50-
table := d.getDefaultFormatTable([]string{"NAME", "OWNER", "PTYPE", "KIND", "SERVICE-PORT", "GATEWAY", "LISTENER-PORT"})
52+
table := d.getDefaultFormatTable([]string{"NAME", "OWNER", "KIND", "SERVICE", "GATEWAY"})
5153
for _, route := range routes {
52-
if len(route.ParentRefs) > 0 {
53-
for _, gateway := range route.ParentRefs {
54-
table.Append([]string{
55-
route.Name,
56-
route.Owner,
57-
route.Ptype,
58-
route.Kind,
59-
fmt.Sprint(route.Port),
60-
gateway.Name,
61-
fmt.Sprint(gateway.Port),
62-
})
54+
var services []string
55+
for _, rule := range route.Rules {
56+
if backends, ok := rule["backendRefs"].([]interface{}); ok {
57+
for _, backend := range backends {
58+
if service, ok := backend.(map[string]interface{}); ok {
59+
services = append(services, fmt.Sprintf("%v:%v", service["name"], service["port"]))
60+
}
61+
}
6362
}
64-
} else {
65-
table.Append([]string{
66-
route.Name,
67-
route.Owner,
68-
route.Ptype,
69-
route.Kind,
70-
fmt.Sprint(route.Port),
71-
"",
72-
"",
73-
})
7463
}
64+
var gateways []string
65+
for _, gateway := range route.ParentRefs {
66+
gateways = append(gateways, fmt.Sprintf("%s:%d", gateway.Name, gateway.Port))
67+
}
68+
table.Append([]string{
69+
route.Name,
70+
route.Owner,
71+
route.Kind,
72+
strings.Join(services, "\n"),
73+
strings.Join(gateways, "\n"),
74+
})
75+
fmt.Println("==============================", services, services)
7576
}
7677
table.Render()
7778
}

cmd/routes_test.go

Lines changed: 57 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,21 @@ func TestRoutesCreate(t *testing.T) {
2323
cmdr := DryccCmd{WOut: &b, ConfigFile: cf}
2424

2525
server.Mux.HandleFunc("/v2/apps/foo/routes/", func(w http.ResponseWriter, r *http.Request) {
26-
testutil.AssertBody(t, api.RouteCreateRequest{Name: "example-go", Ptype: "web", Port: 443, Kind: "HTTPRoute"}, r)
26+
request := api.RouteCreateRequest{
27+
Name: "example-go",
28+
Kind: "HTTPRoute",
29+
Rules: []api.RequestRouteRule{{
30+
BackendRefs: []api.BackendRefRequest{{Name: "example-go", Port: 443}},
31+
}},
32+
}
33+
testutil.AssertBody(t, request, r)
2734
testutil.SetHeaders(w)
2835
w.WriteHeader(http.StatusCreated)
2936
// Body isn't used by CLI, so it isn't set.
3037
w.Write([]byte("{}"))
3138
})
3239

33-
err = cmdr.RoutesCreate("foo", "example-go", "web", "HTTPRoute", 443)
40+
err = cmdr.RoutesCreate("foo", "example-go", "HTTPRoute", api.BackendRefRequest{Name: "example-go", Port: 443})
3441
assert.NoError(t, err)
3542

3643
assert.Equal(t, testutil.StripProgress(b.String()), "Adding route example-go to foo... done\n", "output")
@@ -66,8 +73,28 @@ func TestRoutesList(t *testing.T) {
6673
{
6774
"name": "example-go",
6875
"port": 80
76+
},
77+
{
78+
"name": "example-go",
79+
"port": 8080
6980
}
70-
]
81+
],
82+
"rules": [{
83+
"backendRefs": [
84+
{
85+
"kind": "Service",
86+
"name": "yygl-nextcloud",
87+
"port": 80,
88+
"weight": 100
89+
},
90+
{
91+
"kind": "Service",
92+
"name": "yygl-nextcloud",
93+
"port": 8080,
94+
"weight": 100
95+
}
96+
]
97+
}]
7198
}
7299
]
73100
}`)
@@ -76,8 +103,9 @@ func TestRoutesList(t *testing.T) {
76103
err = cmdr.RoutesList("foo", -1)
77104
assert.NoError(t, err)
78105

79-
assert.Equal(t, b.String(), `NAME OWNER PTYPE KIND SERVICE-PORT GATEWAY LISTENER-PORT
80-
example-go test web HTTPRoute 80 example-go 80
106+
assert.Equal(t, b.String(), `NAME OWNER KIND SERVICE GATEWAY
107+
example-go test HTTPRoute yygl-nextcloud:80 example-go:80
108+
yygl-nextcloud:8080 example-go:8080
81109
`, "output")
82110
}
83111

@@ -92,7 +120,7 @@ func TestRoutesAttach(t *testing.T) {
92120
cmdr := DryccCmd{WOut: &b, ConfigFile: cf}
93121

94122
server.Mux.HandleFunc("/v2/apps/foo/routes/example-go/attach/", func(w http.ResponseWriter, r *http.Request) {
95-
testutil.AssertBody(t, api.RouteAttackRequest{Port: 4443, Gateway: "example-go"}, r)
123+
testutil.AssertBody(t, api.RouteAttachRequest{Port: 4443, Gateway: "example-go"}, r)
96124
testutil.SetHeaders(w)
97125
w.WriteHeader(http.StatusCreated)
98126
// Body isn't used by CLI, so it isn't set.
@@ -115,7 +143,7 @@ func TestRoutesDetach(t *testing.T) {
115143
cmdr := DryccCmd{WOut: &b, ConfigFile: cf}
116144

117145
server.Mux.HandleFunc("/v2/apps/foo/routes/example-go/detach/", func(w http.ResponseWriter, r *http.Request) {
118-
testutil.AssertBody(t, api.RouteDetackRequest{Port: 4443, Gateway: "example-go"}, r)
146+
testutil.AssertBody(t, api.RouteDetachRequest{Port: 4443, Gateway: "example-go"}, r)
119147
testutil.SetHeaders(w)
120148
w.WriteHeader(http.StatusCreated)
121149
// Body isn't used by CLI, so it isn't set.
@@ -140,36 +168,33 @@ func TestRouteGet(t *testing.T) {
140168

141169
server.Mux.HandleFunc("/v2/apps/foo/routes/example-go/rules/", func(w http.ResponseWriter, _ *http.Request) {
142170
testutil.SetHeaders(w)
143-
fmt.Fprintf(w, `{
144-
"stable": [
145-
{
146-
"backendRefs": [
147-
{
148-
"group": "",
149-
"kind": "Service",
150-
"name": "example-go",
151-
"port": 1234,
152-
"weight": 1
153-
}
154-
],
155-
"matches": [
156-
{
157-
"path": {
158-
"type": "PathPrefix",
159-
"value": "/get"
160-
}
161-
}
162-
]
163-
}
164-
]
165-
}`)
171+
fmt.Fprintf(w, `
172+
[{
173+
"backendRefs": [
174+
{
175+
"group": "",
176+
"kind": "Service",
177+
"name": "example-go",
178+
"port": 1234,
179+
"weight": 1
180+
}
181+
],
182+
"matches": [
183+
{
184+
"path": {
185+
"type": "PathPrefix",
186+
"value": "/get"
187+
}
188+
}
189+
]
190+
}
191+
]`)
166192
})
167193

168194
err = cmdr.RoutesGet("foo", "example-go")
169195
assert.NoError(t, err)
170196

171-
assert.Equal(t, b.String(), `stable:
172-
- backendRefs:
197+
assert.Equal(t, b.String(), `- backendRefs:
173198
- group: ""
174199
kind: Service
175200
name: example-go
@@ -200,7 +225,6 @@ func TestRoutesSet(t *testing.T) {
200225
})
201226
ruleFile, err := os.CreateTemp("", "rules.yaml")
202227
rules := `
203-
stable:
204228
- backendRefs:
205229
- group: ""
206230
kind: Service

cmd/services.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,11 @@ func (d *DryccCmd) ServicesList(appID string) error {
2121
return err
2222
}
2323
if len(services) > 0 {
24-
table := d.getDefaultFormatTable([]string{"PTYPE", "PORT", "PROTOCOL", "TARGET-PORT", "DOMAIN"})
24+
table := d.getDefaultFormatTable([]string{"NAME", "PTYPE", "PORT", "PROTOCOL", "TARGET-PORT", "DOMAIN"})
2525
for _, service := range services {
2626
for _, port := range service.Ports {
2727
table.Append([]string{
28+
service.Name,
2829
service.Ptype,
2930
fmt.Sprint(port.Port),
3031
port.Protocol,

go.mod

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,30 @@
11
module github.com/drycc/workflow-cli
22

3-
go 1.22
3+
go 1.22.0
44

55
require (
66
github.com/containerd/console v1.0.4
77
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815
8-
github.com/drycc/controller-sdk-go v0.0.0-20240920130625-ca004f117997
8+
github.com/drycc/controller-sdk-go v0.0.0-20241014021330-60aa12d26766
99
github.com/drycc/pkg v0.0.0-20240225112316-78fc9239f51f
1010
github.com/minio/selfupdate v0.6.0
1111
github.com/olekukonko/tablewriter v0.0.5
1212
github.com/schollz/progressbar/v3 v3.14.4
1313
github.com/stretchr/testify v1.9.0
1414
golang.org/x/exp v0.0.0-20240213143201-ec583247a57a
15-
golang.org/x/net v0.23.0
15+
golang.org/x/net v0.28.0
1616
gopkg.in/yaml.v3 v3.0.1
1717
sigs.k8s.io/yaml v1.4.0
1818
)
1919

2020
require (
2121
aead.dev/minisign v0.2.0 // indirect
22-
github.com/davecgh/go-spew v1.1.1 // indirect
22+
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
2323
github.com/mattn/go-runewidth v0.0.9 // indirect
2424
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
25-
github.com/pmezard/go-difflib v1.0.0 // indirect
25+
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
2626
github.com/rivo/uniseg v0.4.7 // indirect
27-
golang.org/x/crypto v0.21.0 // indirect
28-
golang.org/x/sys v0.20.0 // indirect
29-
golang.org/x/term v0.20.0 // indirect
27+
golang.org/x/crypto v0.26.0 // indirect
28+
golang.org/x/sys v0.23.0 // indirect
29+
golang.org/x/term v0.23.0 // indirect
3030
)

go.sum

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,13 @@ aead.dev/minisign v0.2.0/go.mod h1:zdq6LdSd9TbuSxchxwhpA9zEb9YXcVGoE8JakuiGaIQ=
33
github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn4ro=
44
github.com/containerd/console v1.0.4/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
55
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
6-
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
76
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
7+
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
8+
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
89
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815 h1:bWDMxwH3px2JBh6AyO7hdCn/PkvCZXii8TGj7sbtEbQ=
910
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
10-
github.com/drycc/controller-sdk-go v0.0.0-20240920130625-ca004f117997 h1:VYsycfvaJlxpUcal1+e5uXfDy0006XKr1Tb3FnR9ALo=
11-
github.com/drycc/controller-sdk-go v0.0.0-20240920130625-ca004f117997/go.mod h1:n6eQe1irJqjwLo/7t9+Dhdv6faSESQN+ATnZRBP3/Uc=
11+
github.com/drycc/controller-sdk-go v0.0.0-20241014021330-60aa12d26766 h1:6Wda7jbd3htqcbd8j6JgZTr84YS2fQe5g5NGHTCb2Ag=
12+
github.com/drycc/controller-sdk-go v0.0.0-20241014021330-60aa12d26766/go.mod h1:XOfsqEM0rZ9UOT0jtx2czBntSq9Nko80yGMVGyREzDg=
1213
github.com/drycc/pkg v0.0.0-20240225112316-78fc9239f51f h1:kgjvUQJeAszDoU1Vo4vTTE92KI8Av3JPb6Qn890niXg=
1314
github.com/drycc/pkg v0.0.0-20240225112316-78fc9239f51f/go.mod h1:n+QxGif6ha9CEoxVnlipxb9IdmerybcUSzTEDFkvjiA=
1415
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
@@ -23,8 +24,9 @@ github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2Em
2324
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
2425
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
2526
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
26-
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
2727
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
28+
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
29+
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
2830
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
2931
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
3032
github.com/schollz/progressbar/v3 v3.14.4 h1:W9ZrDSJk7eqmQhd3uxFNNcTr0QL+xuGNI9dEMrw0r74=
@@ -36,14 +38,14 @@ github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8
3638
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
3739
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
3840
golang.org/x/crypto v0.0.0-20211209193657-4570a0811e8b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
39-
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
40-
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
41+
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
42+
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
4143
golang.org/x/exp v0.0.0-20240213143201-ec583247a57a h1:HinSgX1tJRX3KsL//Gxynpw5CTOAIPhgL4W8PNiIpVE=
4244
golang.org/x/exp v0.0.0-20240213143201-ec583247a57a/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc=
4345
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
4446
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
45-
golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
46-
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
47+
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
48+
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
4749
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
4850
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
4951
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -52,12 +54,14 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
5254
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
5355
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
5456
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
55-
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
5657
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
58+
golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM=
59+
golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
5760
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
5861
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
59-
golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
6062
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
63+
golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU=
64+
golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk=
6165
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
6266
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
6367
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

0 commit comments

Comments
 (0)