Skip to content

Commit

Permalink
Call an optional webhook before killing a pod
Browse files Browse the repository at this point in the history
  • Loading branch information
Miouge1 committed Sep 24, 2019
1 parent bd9b6e8 commit a08884a
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 15 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ spec:
- --timezone=Europe/Berlin
# exclude all pods that haven't been running for at least one hour
- --minimum-age=1h
# check with a webhook before killing a pod
- --webhook=https://httpbin.org/post
# terminate pods for real: this disables dry-run mode which is on by default
# - --no-dry-run
```
Expand Down Expand Up @@ -234,6 +236,7 @@ Use `UTC`, `Local` or pick a timezone name from the [(IANA) tz database](https:/
| `--dry-run` | don't kill pods, only log what would have been done | true |
| `--log-format` | specify the format of the log messages. Options are text and json | text |
| `--log-caller` | include the calling function name and location in the log messages | false |
| `--webhook` | filter pods by a POST webhook, if non HTTP 200 returned, exclude pod | no webhook calls |

## Related work

Expand Down
39 changes: 38 additions & 1 deletion chaoskube/chaoskube.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
package chaoskube

import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"math/rand"
"net/http"
"net/url"
"regexp"
"time"

Expand Down Expand Up @@ -63,6 +67,8 @@ type Chaoskube struct {
EventRecorder record.EventRecorder
// a function to retrieve the current time
Now func() time.Time
// Webhook
Webhook url.URL
}

var (
Expand All @@ -86,7 +92,7 @@ var (
// * a logger implementing logrus.FieldLogger to send log output to
// * what specific terminator to use to imbue chaos on victim pods
// * whether to enable/disable dry-run mode
func New(client kubernetes.Interface, labels, annotations, namespaces, namespaceLabels labels.Selector, includedPodNames, excludedPodNames *regexp.Regexp, excludedWeekdays []time.Weekday, excludedTimesOfDay []util.TimePeriod, excludedDaysOfYear []time.Time, timezone *time.Location, minimumAge time.Duration, logger log.FieldLogger, dryRun bool, terminator terminator.Terminator) *Chaoskube {
func New(client kubernetes.Interface, labels, annotations, namespaces, namespaceLabels labels.Selector, includedPodNames, excludedPodNames *regexp.Regexp, excludedWeekdays []time.Weekday, excludedTimesOfDay []util.TimePeriod, excludedDaysOfYear []time.Time, timezone *time.Location, minimumAge time.Duration, logger log.FieldLogger, dryRun bool, terminator terminator.Terminator, Webhook url.URL) *Chaoskube {
broadcaster := record.NewBroadcaster()
broadcaster.StartRecordingToSink(&typedcorev1.EventSinkImpl{Interface: client.CoreV1().Events(v1.NamespaceAll)})
recorder := broadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: "chaoskube"})
Expand All @@ -109,6 +115,7 @@ func New(client kubernetes.Interface, labels, annotations, namespaces, namespace
Terminator: terminator,
EventRecorder: recorder,
Now: time.Now,
Webhook: Webhook,
}
}

Expand Down Expand Up @@ -212,6 +219,7 @@ func (c *Chaoskube) Candidates() ([]v1.Pod, error) {
pods = filterByPhase(pods, v1.PodRunning)
pods = filterByMinimumAge(pods, c.MinimumAge, c.Now())
pods = filterByPodName(pods, c.IncludedPodNames, c.ExcludedPodNames)
pods = filterByWebhook(pods, c.Webhook)

return pods, nil
}
Expand Down Expand Up @@ -409,3 +417,32 @@ func filterByPodName(pods []v1.Pod, includedPodNames, excludedPodNames *regexp.R

return filteredList
}

// filterByWebhook filters pods by a POST webhook. Only pods where the webhooks returns an
// HTTP 200 are returned
func filterByWebhook(pods []v1.Pod, url url.URL) []v1.Pod {
// return early if url is not given
if url.String() == "" {
return pods
}

filteredList := []v1.Pod{}

for _, pod := range pods {
postData := new(bytes.Buffer)
err := json.NewEncoder(postData).Encode(pod)
if err != nil {
continue
}
resp, err := http.Post(url.String(), "application/json", postData)
if err != nil {
continue
}

if resp.StatusCode == http.StatusOK {
filteredList = append(filteredList, pod)
}
}

return filteredList
}
50 changes: 36 additions & 14 deletions chaoskube/chaoskube_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package chaoskube
import (
"context"
"math/rand"
"net/url"
"regexp"
"testing"
"time"
Expand Down Expand Up @@ -46,13 +47,14 @@ func (suite *Suite) TestNew() {
includedPodNames = regexp.MustCompile("foo")
excludedPodNames = regexp.MustCompile("bar")
excludedWeekdays = []time.Weekday{time.Friday}
excludedTimesOfDay = []util.TimePeriod{util.TimePeriod{}}
excludedTimesOfDay = []util.TimePeriod{{}}
excludedDaysOfYear = []time.Time{time.Now()}
minimumAge = time.Duration(42)
dryRun = true
terminator = terminator.NewDeletePodTerminator(client, logger, 10*time.Second)
)

webhook, _ := url.Parse("")
chaoskube := New(
client,
labelSelector,
Expand All @@ -69,6 +71,7 @@ func (suite *Suite) TestNew() {
logger,
dryRun,
terminator,
*webhook,
)
suite.Require().NotNil(chaoskube)

Expand Down Expand Up @@ -105,6 +108,7 @@ func (suite *Suite) TestRunContextCanceled() {
time.Duration(0),
false,
10,
url.URL{},
)

ctx, cancel := context.WithCancel(context.Background())
Expand All @@ -122,19 +126,22 @@ func (suite *Suite) TestCandidates() {
labelSelector string
annotationSelector string
namespaceSelector string
webhook string
pods []map[string]string
}{
{"", "", "", []map[string]string{foo, bar}},
{"app=foo", "", "", []map[string]string{foo}},
{"app!=foo", "", "", []map[string]string{bar}},
{"", "chaos=foo", "", []map[string]string{foo}},
{"", "chaos!=foo", "", []map[string]string{bar}},
{"", "", "default", []map[string]string{foo}},
{"", "", "default,testing", []map[string]string{foo, bar}},
{"", "", "!testing", []map[string]string{foo}},
{"", "", "!default,!testing", []map[string]string{}},
{"", "", "default,!testing", []map[string]string{foo}},
{"", "", "default,!default", []map[string]string{}},
{"", "", "", "", []map[string]string{foo, bar}},
{"app=foo", "", "", "", []map[string]string{foo}},
{"app!=foo", "", "", "", []map[string]string{bar}},
{"", "chaos=foo", "", "", []map[string]string{foo}},
{"", "chaos!=foo", "", "", []map[string]string{bar}},
{"", "", "default", "", []map[string]string{foo}},
{"", "", "default,testing", "", []map[string]string{foo, bar}},
{"", "", "!testing", "", []map[string]string{foo}},
{"", "", "!default,!testing", "", []map[string]string{}},
{"", "", "default,!testing", "", []map[string]string{foo}},
{"", "", "default,!default", "", []map[string]string{}},
{"", "", "", "https://httpbin.org/status/404", []map[string]string{}},
{"", "", "", "https://httpbin.org/status/200", []map[string]string{foo, bar}},
} {
labelSelector, err := labels.Parse(tt.labelSelector)
suite.Require().NoError(err)
Expand All @@ -145,6 +152,9 @@ func (suite *Suite) TestCandidates() {
namespaceSelector, err := labels.Parse(tt.namespaceSelector)
suite.Require().NoError(err)

webhook, err := url.Parse(tt.webhook)
suite.Require().NoError(err)

chaoskube := suite.setupWithPods(
labelSelector,
annotationSelector,
Expand All @@ -159,6 +169,7 @@ func (suite *Suite) TestCandidates() {
time.Duration(0),
false,
10,
*webhook,
)

suite.assertCandidates(chaoskube, tt.pods)
Expand Down Expand Up @@ -203,6 +214,7 @@ func (suite *Suite) TestCandidatesNamespaceLabels() {
time.Duration(0),
false,
10,
url.URL{},
)

suite.assertCandidates(chaoskube, tt.pods)
Expand Down Expand Up @@ -245,6 +257,7 @@ func (suite *Suite) TestCandidatesPodNameRegexp() {
time.Duration(0),
false,
10,
url.URL{},
)

suite.assertCandidates(chaoskube, tt.pods)
Expand Down Expand Up @@ -284,6 +297,7 @@ func (suite *Suite) TestVictim() {
time.Duration(0),
false,
10,
url.URL{},
)

suite.assertVictim(chaoskube, tt.victim)
Expand All @@ -306,6 +320,7 @@ func (suite *Suite) TestNoVictimReturnsError() {
time.Duration(0),
false,
10,
url.URL{},
)

_, err := chaoskube.Victim()
Expand Down Expand Up @@ -339,6 +354,7 @@ func (suite *Suite) TestDeletePod() {
time.Duration(0),
tt.dryRun,
10,
url.URL{},
)

victim := util.NewPod("default", "foo", v1.PodRunning)
Expand Down Expand Up @@ -367,6 +383,7 @@ func (suite *Suite) TestDeletePodNotFound() {
time.Duration(0),
false,
10,
url.URL{},
)

victim := util.NewPod("default", "foo", v1.PodRunning)
Expand Down Expand Up @@ -597,6 +614,7 @@ func (suite *Suite) TestTerminateVictim() {
time.Duration(0),
false,
10,
url.URL{},
)
chaoskube.Now = tt.now

Expand Down Expand Up @@ -626,6 +644,7 @@ func (suite *Suite) TestTerminateNoVictimLogsInfo() {
time.Duration(0),
false,
10,
url.URL{},
)

err := chaoskube.TerminateVictim()
Expand All @@ -650,7 +669,7 @@ func (suite *Suite) assertVictim(chaoskube *Chaoskube, expected map[string]strin
suite.AssertPod(victim, expected)
}

func (suite *Suite) setupWithPods(labelSelector labels.Selector, annotations labels.Selector, namespaces labels.Selector, namespaceLabels labels.Selector, includedPodNames *regexp.Regexp, excludedPodNames *regexp.Regexp, excludedWeekdays []time.Weekday, excludedTimesOfDay []util.TimePeriod, excludedDaysOfYear []time.Time, timezone *time.Location, minimumAge time.Duration, dryRun bool, gracePeriod time.Duration) *Chaoskube {
func (suite *Suite) setupWithPods(labelSelector labels.Selector, annotations labels.Selector, namespaces labels.Selector, namespaceLabels labels.Selector, includedPodNames *regexp.Regexp, excludedPodNames *regexp.Regexp, excludedWeekdays []time.Weekday, excludedTimesOfDay []util.TimePeriod, excludedDaysOfYear []time.Time, timezone *time.Location, minimumAge time.Duration, dryRun bool, gracePeriod time.Duration, webhook url.URL) *Chaoskube {
chaoskube := suite.setup(
labelSelector,
annotations,
Expand All @@ -665,6 +684,7 @@ func (suite *Suite) setupWithPods(labelSelector labels.Selector, annotations lab
minimumAge,
dryRun,
gracePeriod,
webhook,
)

for _, namespace := range []v1.Namespace{
Expand All @@ -689,7 +709,7 @@ func (suite *Suite) setupWithPods(labelSelector labels.Selector, annotations lab
return chaoskube
}

func (suite *Suite) setup(labelSelector labels.Selector, annotations labels.Selector, namespaces labels.Selector, namespaceLabels labels.Selector, includedPodNames *regexp.Regexp, excludedPodNames *regexp.Regexp, excludedWeekdays []time.Weekday, excludedTimesOfDay []util.TimePeriod, excludedDaysOfYear []time.Time, timezone *time.Location, minimumAge time.Duration, dryRun bool, gracePeriod time.Duration) *Chaoskube {
func (suite *Suite) setup(labelSelector labels.Selector, annotations labels.Selector, namespaces labels.Selector, namespaceLabels labels.Selector, includedPodNames *regexp.Regexp, excludedPodNames *regexp.Regexp, excludedWeekdays []time.Weekday, excludedTimesOfDay []util.TimePeriod, excludedDaysOfYear []time.Time, timezone *time.Location, minimumAge time.Duration, dryRun bool, gracePeriod time.Duration, webhook url.URL) *Chaoskube {
logOutput.Reset()

client := fake.NewSimpleClientset()
Expand All @@ -711,6 +731,7 @@ func (suite *Suite) setup(labelSelector labels.Selector, annotations labels.Sele
logger,
dryRun,
terminator.NewDeletePodTerminator(client, nullLogger, gracePeriod),
webhook,
)
}

Expand Down Expand Up @@ -814,6 +835,7 @@ func (suite *Suite) TestMinimumAge() {
tt.minimumAge,
false,
10,
url.URL{},
)
chaoskube.Now = tt.now

Expand Down
9 changes: 9 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0=
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/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=
Expand All @@ -37,12 +38,14 @@ github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4er
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
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-20160524151835-7d79101e329e/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
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/gofuzz v0.0.0-20170612174753-24818f796faf h1:+RRA9JqSOZFfKrOeqr2z77+8R2RKyh8PG66dcu1V0ck=
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d h1:7XGaL1e6bYS1yIonGp9761ExpPPV1ui0SAC59Yube9k=
Expand All @@ -60,6 +63,7 @@ github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJ
github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
Expand Down Expand Up @@ -93,10 +97,12 @@ github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.4.1 h1:K0MGApIoQvMw27RTdJkPbr3JZ7DNbtxQNyi5STVM6Kw=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.6.0 h1:kRhiuYSXR3+uv2IbVbZhUxK5zVD/2pp3Gd2PpvPkpEo=
github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2 h1:6LJUbpNm42llc4HRCuvApCSWB/WfhuNo9K98Q9sNGfs=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.3 h1:CTwfnzjQ+8dS6MhHHu4YswVAD99sL2wjPqP+VkURmKE=
github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
Expand All @@ -116,13 +122,15 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnf
golang.org/x/crypto v0.0.0-20181025213731-e84da0312774/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67 h1:ng3VDlRp5/DHpSWl02R4rM9I+8M2rhmsuLwAMmkLQWE=
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/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-20190206173232-65e2d4e15006 h1:bfLnR+k0tq5Lqt6dflRLcZiz6UaXCMt3vhYJ1l4FQ80=
golang.org/x/net v0.0.0-20190206173232-65e2d4e15006/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980 h1:dfGZHvZk057jK2MCeWus/TowKpJ8y4AmooUzdBSR9GU=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
Expand All @@ -138,6 +146,7 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3 h1:4y9KwBHBgBNwDbtu44R5o1fdOCQUEXhbk/P4A9WmJq0=
golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/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.20181227161524-e6919f6577db h1:6/JqlYfC1CCaLnGceQTI+sDGhC9UBSPAsBqI0Gun6kU=
Expand Down
Loading

0 comments on commit a08884a

Please sign in to comment.