Skip to content

Commit d62a095

Browse files
Support AWS MSK IAM (#57)
* Update modules * Fill out MSK SASL support * Fix lint errors * Make config parsing strict * Update README * Remove unnecessary import * Remove unnecessary log message * Update config * Fix config * Update tests * Update directories * Update README * Bump version
1 parent c87c02d commit d62a095

File tree

21 files changed

+343
-74
lines changed

21 files changed

+343
-74
lines changed

README.md

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -281,10 +281,10 @@ spec:
281281
# SASL settings (optional, not supported if using ZooKeeper)
282282
sasl:
283283
enabled: true # Whether SASL is enabled
284-
mechanism: SCRAM-SHA-512 # Mechanism to use;
285-
# choices are PLAIN, SCRAM-SHA-256, and SCRAM-SHA-512
286-
username: my-username # SASL username
287-
password: my-password # SASL password
284+
mechanism: SCRAM-SHA-512 # Mechanism to use; choices are AWS-MSK-IAM, PLAIN,
285+
# SCRAM-SHA-256, and SCRAM-SHA-512
286+
username: my-username # SASL username; ignored for AWS-MSK-IAM
287+
password: my-password # SASL password; ignored for AWS-MSK-IAM
288288
```
289289
290290
Note that the `name`, `environment`, `region`, and `description` fields are used
@@ -477,17 +477,21 @@ command-line or in a cluster config. See [this config](examples/auth/cluster.yam
477477
### SASL
478478

479479
`topicctl` supports SASL authentication when running in the exclusive broker API mode. To use this,
480-
either set the `--sasl-mechanism`, `--sasl-username`, and `--sasl-password` flags on the command
481-
line or fill out the `SASL` section of the cluster config.
480+
either set the `--sasl-mechanism` and other appropriate `--sasl-*` flags on the command line or
481+
fill out the `SASL` section of the cluster config.
482482

483-
If using the cluster config, the username and password can still be set on the command-line
484-
or via the `TOPICCTL_SASL_USERNAME` and `TOPICCTL_SASL_PASSWORD` environment variables.
483+
The following mechanisms can be used:
485484

486-
The tool currently supports the following SASL mechanisms:
485+
1. `AWS-MSK-IAM`
486+
2. `PLAIN`
487+
3. `SCRAM-SHA-256`
488+
4. `SCRAM-SHA-512`
487489

488-
1. `PLAIN`
489-
2. `SCRAM-SHA-256`
490-
3. `SCRAM-SHA-512`
490+
If using `AWS-MSK-IAM`, then `topicctl` will attempt to discover your AWS credentials in the
491+
locations and order described [here](https://docs.aws.amazon.com/sdk-for-go/api/aws/session/).
492+
The other mechanisms require a username and password to be set in either the cluster config
493+
or on the command-line. See the cluster configs in the [examples/auth](/examples/auth) and
494+
[examples/msk](/examples/msk) directories for some specific examples.
491495

492496
Note that SASL can be run either with or without TLS, although the former is generally more
493497
secure.

cmd/topicctl/subcmd/shared.go

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,25 @@ func (s sharedOptions) validate() error {
4040
)
4141
}
4242

43+
if s.clusterConfig != "" {
44+
clusterConfig, clusterConfigErr := config.LoadClusterFile(s.clusterConfig, s.expandEnv)
45+
if clusterConfigErr != nil {
46+
err = multierror.Append(
47+
err,
48+
clusterConfigErr,
49+
)
50+
} else {
51+
clusterConfigValidateErr := clusterConfig.Validate()
52+
53+
if clusterConfigValidateErr != nil {
54+
err = multierror.Append(
55+
err,
56+
clusterConfigValidateErr,
57+
)
58+
}
59+
}
60+
}
61+
4362
if s.zkAddr != "" && s.brokerAddr != "" {
4463
err = multierror.Append(
4564
err,
@@ -67,9 +86,15 @@ func (s sharedOptions) validate() error {
6786
}
6887

6988
if useSASL {
70-
if saslErr := admin.ValidateSASLMechanism(s.saslMechanism); saslErr != nil {
89+
saslMechanism, saslErr := admin.SASLNameToMechanism(s.saslMechanism)
90+
if saslErr != nil {
7191
err = multierror.Append(err, saslErr)
7292
}
93+
94+
if saslMechanism == admin.SASLMechanismAWSMSKIAM &&
95+
(s.saslUsername != "" || s.saslPassword != "") {
96+
log.Warn("Username and password are ignored if using SASL AWS-MSK-IAM")
97+
}
7398
}
7499

75100
return err
@@ -100,6 +125,17 @@ func (s sharedOptions) getAdminClient(
100125
saslEnabled := (s.saslMechanism != "" ||
101126
s.saslPassword != "" ||
102127
s.saslUsername != "")
128+
129+
var saslMechanism admin.SASLMechanism
130+
var err error
131+
132+
if s.saslMechanism != "" {
133+
saslMechanism, err = admin.SASLNameToMechanism(s.saslMechanism)
134+
if err != nil {
135+
return nil, err
136+
}
137+
}
138+
103139
return admin.NewBrokerAdminClient(
104140
ctx,
105141
admin.BrokerAdminClientConfig{
@@ -115,7 +151,7 @@ func (s sharedOptions) getAdminClient(
115151
},
116152
SASL: admin.SASLConfig{
117153
Enabled: saslEnabled,
118-
Mechanism: s.saslMechanism,
154+
Mechanism: saslMechanism,
119155
Password: s.saslPassword,
120156
Username: s.saslUsername,
121157
},
@@ -161,7 +197,7 @@ func addSharedFlags(cmd *cobra.Command, options *sharedOptions) {
161197
&options.saslMechanism,
162198
"sasl-mechanism",
163199
"",
164-
"SASL mechanism if using SASL (choices: PLAIN, SCRAM-SHA-256, or SCRAM-SHA-512)",
200+
"SASL mechanism if using SASL (choices: AWS-MSK-IAM, PLAIN, SCRAM-SHA-256, or SCRAM-SHA-512)",
165201
)
166202
cmd.Flags().StringVar(
167203
&options.saslPassword,

examples/auth/cluster.yaml

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,13 @@ spec:
2121
enabled: true
2222
mechanism: SCRAM-SHA-512
2323

24-
# As an alternative to storing these in the config (probably not super-secure), these can be
25-
# set by using the --sasl-username and --sasl-password flags or the
26-
# TOPICCTL_SASL_USERNAME and TOPICCTL_SASL_PASSWORD environment variables when running
27-
# topicctl.
24+
# As an alternative to storing these in plain text in the config (probably not super-secure),
25+
# these can also be set via:
26+
#
27+
# 1. The --sasl-username and --sasl-password command-line flags,
28+
# 2. The TOPICCTL_SASL_USERNAME and TOPICCTL_SASL_PASSWORD environment variables, or
29+
# 3. Putting placeholder strings in the config and running with the --expand-env flag as
30+
# described in the README.
31+
#
2832
username: adminscram
2933
password: admin-secret-512

examples/msk/cluster.yaml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
meta:
2+
name: msk-cluster
3+
environment: aws-env
4+
region: aws-region
5+
description: |
6+
Example of config for AWS MSK cluster with IAM authentication enabled.
7+
8+
spec:
9+
bootstrapAddrs:
10+
# These are dummy placeholders; replace them with the broker addresses for your MSK cluster.
11+
- "b-1.my-cluster.kafka.aws-region.amazonaws.com:9098"
12+
- "b-2.my-cluster.kafka.aws-region.amazonaws.com:9098"
13+
- "b-3.my-cluster.kafka.aws-region.amazonaws.com:9098"
14+
15+
tls:
16+
# TLS is enabled on the IAM-authenticated broker endpoints by default
17+
enabled: true
18+
sasl:
19+
# No credentials are set here; instead, they'll be pulled from
20+
# the environment, a shared credentials file, a shared configuration file, or the EC2 metadata
21+
# service as described here: https://docs.aws.amazon.com/sdk-for-go/api/aws/session/.
22+
enabled: true
23+
mechanism: AWS-MSK-IAM
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
meta:
2+
name: topic-default
3+
cluster: msk-cluster
4+
environment: aws-env
5+
region: aws-region
6+
description: |
7+
Topic that uses default (any) strategy for assigning partition brokers.
8+
9+
spec:
10+
partitions: 3
11+
replicationFactor: 3
12+
retentionMinutes: 100
13+
placement:
14+
strategy: any
15+
settings:
16+
cleanup.policy: delete
17+
max.message.bytes: 5542880

go.mod

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,16 @@ go 1.17
66
// replace github.com/segmentio/kafka-go => /Users/benjamin.yolken/dev/src/github.com/segmentio/kafka-go
77

88
require (
9-
github.com/aws/aws-sdk-go v1.20.6
9+
github.com/aws/aws-sdk-go v1.41.3
1010
github.com/briandowns/spinner v1.11.1
1111
github.com/c-bata/go-prompt v0.2.3
1212
github.com/fatih/color v1.9.0
1313
github.com/ghodss/yaml v1.0.0
1414
github.com/hashicorp/go-multierror v1.1.0
1515
github.com/olekukonko/tablewriter v0.0.4
1616
github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da
17-
github.com/segmentio/kafka-go v0.4.21-0.20211001205616-c03923d67699
17+
github.com/segmentio/kafka-go v0.4.25
18+
github.com/segmentio/kafka-go/sasl/aws_msk_iam v0.0.0-20211124042555-e88d48aa0b68
1819
github.com/sirupsen/logrus v1.2.0
1920
github.com/spf13/cobra v1.0.0
2021
github.com/stretchr/testify v1.6.1
@@ -27,7 +28,7 @@ require (
2728
github.com/golang/snappy v0.0.1 // indirect
2829
github.com/hashicorp/errwrap v1.0.0 // indirect
2930
github.com/inconshreveable/mousetrap v1.0.0 // indirect
30-
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af // indirect
31+
github.com/jmespath/go-jmespath v0.4.0 // indirect
3132
github.com/klauspost/compress v1.9.8 // indirect
3233
github.com/konsorten/go-windows-terminal-sequences v1.0.1 // indirect
3334
github.com/mattn/go-colorable v0.1.6 // indirect
@@ -43,9 +44,8 @@ require (
4344
github.com/spf13/pflag v1.0.3 // indirect
4445
github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c // indirect
4546
github.com/xdg/stringprep v1.0.0 // indirect
46-
golang.org/x/net v0.0.0-20200202094626-16171245cfb2 // indirect
47-
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae // indirect
48-
golang.org/x/text v0.3.0 // indirect
47+
golang.org/x/sys v0.0.0-20210423082822-04245dca01da // indirect
48+
golang.org/x/text v0.3.6 // indirect
4949
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
5050
gopkg.in/yaml.v2 v2.2.8 // indirect
5151
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c // indirect

go.sum

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAE
44
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
55
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
66
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
7-
github.com/aws/aws-sdk-go v1.20.6 h1:kmy4Gvdlyez1fV4kw5RYxZzWKVyuHZHgPWeU/YvRsV4=
8-
github.com/aws/aws-sdk-go v1.20.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
7+
github.com/aws/aws-sdk-go v1.41.3 h1:deglLZ1jjHdhkd6Rbad1MZM4gL+1pfnTfjuFk6CGJFM=
8+
github.com/aws/aws-sdk-go v1.41.3/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q=
99
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
1010
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
1111
github.com/briandowns/spinner v1.11.1 h1:OixPqDEcX3juo5AjQZAnFPbeUA0jvkp2qzB5gOZJ/L0=
@@ -65,8 +65,10 @@ github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
6565
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
6666
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
6767
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
68-
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
69-
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
68+
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
69+
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
70+
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
71+
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
7072
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
7173
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
7274
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
@@ -114,6 +116,7 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9
114116
github.com/pierrec/lz4 v2.6.0+incompatible h1:Ix9yFKn1nSPBLFl/yZknTp8TU5G4Ps0JDmguYK6iH1A=
115117
github.com/pierrec/lz4 v2.6.0+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
116118
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
119+
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
117120
github.com/pkg/term v0.0.0-20200520122047-c3ffed290a03 h1:pd4YKIqCB0U7O2I4gWHgEUA2mCEOENmco0l/bM957bU=
118121
github.com/pkg/term v0.0.0-20200520122047-c3ffed290a03/go.mod h1:Z9+Ul5bCbBKnbCvdOWbLqTHhJiYV414CURZJba6L8qA=
119122
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -131,8 +134,11 @@ github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6So
131134
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
132135
github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da h1:p3Vo3i64TCLY7gIfzeQaUJ+kppEO5WQG3cL8iE8tGHU=
133136
github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
134-
github.com/segmentio/kafka-go v0.4.21-0.20211001205616-c03923d67699 h1:DM1XDA47wY0myfsik7hUz+pkmI2uhkfKsa6ogOTNLxw=
135-
github.com/segmentio/kafka-go v0.4.21-0.20211001205616-c03923d67699/go.mod h1:XzMcoMjSzDGHcIwpWUI7GB43iKZ2fTVmryPSGLf/MPg=
137+
github.com/segmentio/kafka-go v0.4.24/go.mod h1:XzMcoMjSzDGHcIwpWUI7GB43iKZ2fTVmryPSGLf/MPg=
138+
github.com/segmentio/kafka-go v0.4.25 h1:QVx9yz12syKBFkxR+dVDDwTO0ItHgnjjhIdBfqizj+8=
139+
github.com/segmentio/kafka-go v0.4.25/go.mod h1:XzMcoMjSzDGHcIwpWUI7GB43iKZ2fTVmryPSGLf/MPg=
140+
github.com/segmentio/kafka-go/sasl/aws_msk_iam v0.0.0-20211124042555-e88d48aa0b68 h1:pRkq2+tKNc1aICn0L2bkmI1orIpYeexyqiC5ka+dK4o=
141+
github.com/segmentio/kafka-go/sasl/aws_msk_iam v0.0.0-20211124042555-e88d48aa0b68/go.mod h1:ytmdJBnHdZJOGxs17aNWHO5JQVmaKQIhmpn57IuxEm0=
136142
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
137143
github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo=
138144
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
@@ -179,8 +185,8 @@ golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73r
179185
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
180186
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
181187
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
182-
golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI=
183-
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
188+
golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q=
189+
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
184190
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
185191
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
186192
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -198,12 +204,17 @@ golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7w
198204
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
199205
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
200206
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
201-
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8=
202207
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
203-
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
208+
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
209+
golang.org/x/sys v0.0.0-20210423082822-04245dca01da h1:b3NXsE2LusjYGGjL5bxEVZZORm/YEFFrWFjR8eFrw/c=
210+
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
211+
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
204212
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
213+
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
214+
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
205215
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
206216
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
217+
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
207218
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
208219
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
209220
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=

0 commit comments

Comments
 (0)