From 9c8ca5c73a55e5c69cdf763981a66ecff49b056d Mon Sep 17 00:00:00 2001 From: AlteredCoder <64792091+AlteredCoder@users.noreply.github.com> Date: Wed, 29 Dec 2021 14:08:47 +0100 Subject: [PATCH 01/14] Alert inspect improvement / Use correct CSV output when listing in raw format (#1127) --- cmd/crowdsec-cli/alerts.go | 55 +++++++++++++++++-------------- cmd/crowdsec-cli/bouncers.go | 12 ++++++- cmd/crowdsec-cli/collections.go | 2 +- cmd/crowdsec-cli/decisions.go | 26 ++++++++++----- cmd/crowdsec-cli/hub.go | 8 ++--- cmd/crowdsec-cli/machines.go | 13 ++++++-- cmd/crowdsec-cli/parsers.go | 2 +- cmd/crowdsec-cli/postoverflows.go | 2 +- cmd/crowdsec-cli/scenarios.go | 2 +- cmd/crowdsec-cli/utils.go | 34 +++++++++++++++++-- 10 files changed, 109 insertions(+), 47 deletions(-) diff --git a/cmd/crowdsec-cli/alerts.go b/cmd/crowdsec-cli/alerts.go index 54b9972bb..5d96c71ca 100644 --- a/cmd/crowdsec-cli/alerts.go +++ b/cmd/crowdsec-cli/alerts.go @@ -2,6 +2,7 @@ package main import ( "context" + "encoding/csv" "encoding/json" "fmt" "net/url" @@ -48,36 +49,38 @@ func DecisionsFromAlert(alert *models.Alert) string { func AlertsToTable(alerts *models.GetAlertsResponse, printMachine bool) error { if csConfig.Cscli.Output == "raw" { + csvwriter := csv.NewWriter(os.Stdout) if printMachine { - fmt.Printf("id,scope,value,reason,country,as,decisions,created_at,machine\n") + err := csvwriter.Write([]string{"id", "scope", "value", "reason", "country", "as", "decisions", "created_at", "machine"}) + if err != nil { + return err + } } else { - fmt.Printf("id,scope,value,reason,country,as,decisions,created_at\n") + err := csvwriter.Write([]string{"id", "scope", "value", "reason", "country", "as", "decisions", "created_at"}) + if err != nil { + return err + } } for _, alertItem := range *alerts { - if printMachine { - fmt.Printf("%v,%v,%v,%v,%v,%v,%v,%v,%v\n", - alertItem.ID, - *alertItem.Source.Scope, - *alertItem.Source.Value, - *alertItem.Scenario, - alertItem.Source.Cn, - alertItem.Source.AsNumber+" "+alertItem.Source.AsName, - DecisionsFromAlert(alertItem), - *alertItem.StartAt, - alertItem.MachineID) - } else { - fmt.Printf("%v,%v,%v,%v,%v,%v,%v,%v\n", - alertItem.ID, - *alertItem.Source.Scope, - *alertItem.Source.Value, - *alertItem.Scenario, - alertItem.Source.Cn, - alertItem.Source.AsNumber+" "+alertItem.Source.AsName, - DecisionsFromAlert(alertItem), - *alertItem.StartAt) + row := []string{ + fmt.Sprintf("%d", alertItem.ID), + *alertItem.Source.Scope, + *alertItem.Source.Value, + *alertItem.Scenario, + alertItem.Source.Cn, + alertItem.Source.AsNumber + " " + alertItem.Source.AsName, + DecisionsFromAlert(alertItem), + *alertItem.StartAt, + } + if printMachine { + row = append(row, alertItem.MachineID) + } + err := csvwriter.Write(row) + if err != nil { + return err } - } + csvwriter.Flush() } else if csConfig.Cscli.Output == "json" { x, _ := json.MarshalIndent(alerts, "", " ") fmt.Printf("%s", string(x)) @@ -143,7 +146,9 @@ func DisplayOneAlert(alert *models.Alert, withDetail bool) error { fmt.Printf(" - Events Count : %d\n", *alert.EventsCount) fmt.Printf(" - Scope:Value: %s\n", scopeAndValue) fmt.Printf(" - Country : %s\n", alert.Source.Cn) - fmt.Printf(" - AS : %s\n\n", alert.Source.AsName) + fmt.Printf(" - AS : %s\n", alert.Source.AsName) + fmt.Printf(" - Begin : %s\n", *alert.StartAt) + fmt.Printf(" - End : %s\n\n", *alert.StopAt) foundActive := false table := tablewriter.NewWriter(os.Stdout) table.SetHeader([]string{"ID", "scope:value", "action", "expiration", "created_at"}) diff --git a/cmd/crowdsec-cli/bouncers.go b/cmd/crowdsec-cli/bouncers.go index 41491b22f..1005f17ad 100644 --- a/cmd/crowdsec-cli/bouncers.go +++ b/cmd/crowdsec-cli/bouncers.go @@ -1,6 +1,7 @@ package main import ( + "encoding/csv" "encoding/json" "fmt" "os" @@ -82,6 +83,11 @@ Note: This command requires database direct access, so is intended to be run on } fmt.Printf("%s", string(x)) } else if csConfig.Cscli.Output == "raw" { + csvwriter := csv.NewWriter(os.Stdout) + err := csvwriter.Write([]string{"name", "ip", "revoked", "last_pull", "type", "version"}) + if err != nil { + log.Fatalf("failed to write raw header: %s", err) + } for _, b := range blockers { var revoked string if !b.Revoked { @@ -89,8 +95,12 @@ Note: This command requires database direct access, so is intended to be run on } else { revoked = "pending" } - fmt.Printf("%s,%s,%s,%s,%s\n", b.Name, b.IPAddress, revoked, b.LastPull.Format(time.RFC3339), b.Version) + err := csvwriter.Write([]string{b.Name, b.IPAddress, revoked, b.LastPull.Format(time.RFC3339), b.Type, b.Version}) + if err != nil { + log.Fatalf("failed to write raw: %s", err) + } } + csvwriter.Flush() } }, } diff --git a/cmd/crowdsec-cli/collections.go b/cmd/crowdsec-cli/collections.go index 2b4f27542..5eebc9b1d 100644 --- a/cmd/crowdsec-cli/collections.go +++ b/cmd/crowdsec-cli/collections.go @@ -139,7 +139,7 @@ func NewCollectionsCmd() *cobra.Command { Args: cobra.ExactArgs(0), DisableAutoGenTag: true, Run: func(cmd *cobra.Command, args []string) { - ListItem(cwhub.COLLECTIONS, args) + ListItem(cwhub.COLLECTIONS, args, false, true) }, } cmdCollectionsList.PersistentFlags().BoolVarP(&all, "all", "a", false, "List as well disabled items") diff --git a/cmd/crowdsec-cli/decisions.go b/cmd/crowdsec-cli/decisions.go index c487bbf7f..b06879f15 100644 --- a/cmd/crowdsec-cli/decisions.go +++ b/cmd/crowdsec-cli/decisions.go @@ -2,6 +2,7 @@ package main import ( "context" + "encoding/csv" "encoding/json" "fmt" "net/url" @@ -52,23 +53,32 @@ func DecisionsToTable(alerts *models.GetAlertsResponse) error { alertItem.Decisions = newDecisions } if csConfig.Cscli.Output == "raw" { - fmt.Printf("id,source,ip,reason,action,country,as,events_count,expiration,simulated,alert_id\n") + csvwriter := csv.NewWriter(os.Stdout) + err := csvwriter.Write([]string{"id", "source", "ip", "reason", "action", "country", "as", "events_count", "expiration", "simulated", "alert_id"}) + if err != nil { + return err + } for _, alertItem := range *alerts { for _, decisionItem := range alertItem.Decisions { - fmt.Printf("%v,%v,%v,%v,%v,%v,%v,%v,%v,%v,%v\n", - decisionItem.ID, + err := csvwriter.Write([]string{ + fmt.Sprintf("%d", decisionItem.ID), *decisionItem.Origin, - *decisionItem.Scope+":"+*decisionItem.Value, + *decisionItem.Scope + ":" + *decisionItem.Value, *decisionItem.Scenario, *decisionItem.Type, alertItem.Source.Cn, - alertItem.Source.AsNumber+" "+alertItem.Source.AsName, - *alertItem.EventsCount, + alertItem.Source.AsNumber + " " + alertItem.Source.AsName, + fmt.Sprintf("%d", *alertItem.EventsCount), *decisionItem.Duration, - *decisionItem.Simulated, - alertItem.ID) + fmt.Sprintf("%t", *decisionItem.Simulated), + fmt.Sprintf("%d", alertItem.ID), + }) + if err != nil { + return err + } } } + csvwriter.Flush() } else if csConfig.Cscli.Output == "json" { x, _ := json.MarshalIndent(alerts, "", " ") fmt.Printf("%s", string(x)) diff --git a/cmd/crowdsec-cli/hub.go b/cmd/crowdsec-cli/hub.go index 8b2c6d5f4..21bcfd7f6 100644 --- a/cmd/crowdsec-cli/hub.go +++ b/cmd/crowdsec-cli/hub.go @@ -57,13 +57,13 @@ cscli hub update # Download list of available configurations from the hub } cwhub.DisplaySummary() log.Printf("PARSERS:") - ListItem(cwhub.PARSERS, args) + ListItem(cwhub.PARSERS, args, true, true) log.Printf("SCENARIOS:") - ListItem(cwhub.SCENARIOS, args) + ListItem(cwhub.SCENARIOS, args, true, false) log.Printf("COLLECTIONS:") - ListItem(cwhub.COLLECTIONS, args) + ListItem(cwhub.COLLECTIONS, args, true, false) log.Printf("POSTOVERFLOWS:") - ListItem(cwhub.PARSERS_OVFLW, args) + ListItem(cwhub.PARSERS_OVFLW, args, true, false) }, } cmdHubList.PersistentFlags().BoolVarP(&all, "all", "a", false, "List as well disabled items") diff --git a/cmd/crowdsec-cli/machines.go b/cmd/crowdsec-cli/machines.go index 7e3a69351..2d7a9241b 100644 --- a/cmd/crowdsec-cli/machines.go +++ b/cmd/crowdsec-cli/machines.go @@ -2,6 +2,7 @@ package main import ( saferand "crypto/rand" + "encoding/csv" "encoding/json" "fmt" "io/ioutil" @@ -144,7 +145,11 @@ Note: This command requires database direct access, so is intended to be run on } fmt.Printf("%s", string(x)) } else if csConfig.Cscli.Output == "raw" { - fmt.Printf("machine_id,ip_address,updated_at,validated,version\n") + csvwriter := csv.NewWriter(os.Stdout) + err := csvwriter.Write([]string{"machine_id", "ip_address", "updated_at", "validated", "version"}) + if err != nil { + log.Fatalf("failed to write header: %s", err) + } for _, w := range machines { var validated string if w.IsValidated { @@ -152,8 +157,12 @@ Note: This command requires database direct access, so is intended to be run on } else { validated = "false" } - fmt.Printf("%s,%s,%s,%s,%s\n", w.MachineId, w.IpAddress, w.UpdatedAt.Format(time.RFC3339), validated, w.Version) + err := csvwriter.Write([]string{w.MachineId, w.IpAddress, w.UpdatedAt.Format(time.RFC3339), validated, w.Version}) + if err != nil { + log.Fatalf("failed to write raw output : %s", err) + } } + csvwriter.Flush() } else { log.Errorf("unknown output '%s'", csConfig.Cscli.Output) } diff --git a/cmd/crowdsec-cli/parsers.go b/cmd/crowdsec-cli/parsers.go index 138897814..2235bd8a1 100644 --- a/cmd/crowdsec-cli/parsers.go +++ b/cmd/crowdsec-cli/parsers.go @@ -132,7 +132,7 @@ cscli parsers remove crowdsecurity/sshd-logs cscli parser list crowdsecurity/xxx`, DisableAutoGenTag: true, Run: func(cmd *cobra.Command, args []string) { - ListItem(cwhub.PARSERS, args) + ListItem(cwhub.PARSERS, args, false, true) }, } cmdParsersList.PersistentFlags().BoolVarP(&all, "all", "a", false, "List as well disabled items") diff --git a/cmd/crowdsec-cli/postoverflows.go b/cmd/crowdsec-cli/postoverflows.go index df7d584c4..8c38fe880 100644 --- a/cmd/crowdsec-cli/postoverflows.go +++ b/cmd/crowdsec-cli/postoverflows.go @@ -130,7 +130,7 @@ func NewPostOverflowsCmd() *cobra.Command { cscli postoverflows list crowdsecurity/xxx`, DisableAutoGenTag: true, Run: func(cmd *cobra.Command, args []string) { - ListItem(cwhub.PARSERS_OVFLW, args) + ListItem(cwhub.PARSERS_OVFLW, args, false, true) }, } cmdPostOverflowsList.PersistentFlags().BoolVarP(&all, "all", "a", false, "List as well disabled items") diff --git a/cmd/crowdsec-cli/scenarios.go b/cmd/crowdsec-cli/scenarios.go index 024825b10..059c01f99 100644 --- a/cmd/crowdsec-cli/scenarios.go +++ b/cmd/crowdsec-cli/scenarios.go @@ -132,7 +132,7 @@ cscli scenarios remove crowdsecurity/ssh-bf cscli scenarios list crowdsecurity/xxx`, DisableAutoGenTag: true, Run: func(cmd *cobra.Command, args []string) { - ListItem(cwhub.SCENARIOS, args) + ListItem(cwhub.SCENARIOS, args, false, true) }, } cmdScenariosList.PersistentFlags().BoolVarP(&all, "all", "a", false, "List as well disabled items") diff --git a/cmd/crowdsec-cli/utils.go b/cmd/crowdsec-cli/utils.go index 256a5ba13..071831b6b 100644 --- a/cmd/crowdsec-cli/utils.go +++ b/cmd/crowdsec-cli/utils.go @@ -1,6 +1,7 @@ package main import ( + "encoding/csv" "encoding/json" "fmt" "io/ioutil" @@ -101,7 +102,7 @@ func setHubBranch() error { return nil } -func ListItem(itemType string, args []string) { +func ListItem(itemType string, args []string, showType bool, showHeader bool) { var hubStatus []map[string]string @@ -131,13 +132,40 @@ func ListItem(itemType string, args []string) { } fmt.Printf("%s", string(x)) } else if csConfig.Cscli.Output == "raw" { - fmt.Printf("name,status,version,description\n") + csvwriter := csv.NewWriter(os.Stdout) + if showHeader { + if showType { + err := csvwriter.Write([]string{"name", "status", "version", "description", "type"}) + if err != nil { + log.Fatalf("failed to write header: %s", err) + } + } else { + err := csvwriter.Write([]string{"name", "status", "version", "description"}) + if err != nil { + log.Fatalf("failed to write header: %s", err) + } + } + + } for _, v := range hubStatus { if v["local_version"] == "" { v["local_version"] = "n/a" } - fmt.Printf("%s,%s,%s,%s\n", v["name"], v["status"], v["local_version"], v["description"]) + row := []string{ + v["name"], + v["status"], + v["local_version"], + v["description"], + } + if showType { + row = append(row, itemType) + } + err := csvwriter.Write(row) + if err != nil { + log.Fatalf("failed to write raw output : %s", err) + } } + csvwriter.Flush() } } From ed38ca3a73e430fa201f1bf8b86a4e74152c007f Mon Sep 17 00:00:00 2001 From: he2ss Date: Thu, 30 Dec 2021 11:32:05 +0100 Subject: [PATCH 02/14] cscli: raise error on unknown collection remove (#1133) --- cmd/crowdsec-cli/collections.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cmd/crowdsec-cli/collections.go b/cmd/crowdsec-cli/collections.go index 5eebc9b1d..22963120c 100644 --- a/cmd/crowdsec-cli/collections.go +++ b/cmd/crowdsec-cli/collections.go @@ -76,6 +76,9 @@ func NewCollectionsCmd() *cobra.Command { for _, name := range args { if !forceAction { item := cwhub.GetItem(cwhub.COLLECTIONS, name) + if item == nil { + log.Fatalf("unable to retrieve: %s\n", name) + } if len(item.BelongsToCollections) > 0 { log.Warningf("%s belongs to other collections :\n%s\n", name, item.BelongsToCollections) log.Printf("Run 'sudo cscli collections remove %s --force' if you want to force remove this sub collection\n", name) From f86ec1c389f3346446c6ac265c4b436a93972932 Mon Sep 17 00:00:00 2001 From: blotus Date: Thu, 30 Dec 2021 12:21:49 +0100 Subject: [PATCH 03/14] Docker api version negotiation (#1135) --- pkg/acquisition/modules/docker/docker.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/pkg/acquisition/modules/docker/docker.go b/pkg/acquisition/modules/docker/docker.go index bf883c653..2a2f0c6f2 100644 --- a/pkg/acquisition/modules/docker/docker.go +++ b/pkg/acquisition/modules/docker/docker.go @@ -109,7 +109,7 @@ func (d *DockerSource) Configure(Config []byte, logger *log.Entry) error { d.compiledContainerID = append(d.compiledContainerID, regexp.MustCompile(cont)) } - dockerClient, err := client.NewClientWithOpts(client.FromEnv) + dockerClient, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) if err != nil { return err } @@ -136,6 +136,12 @@ func (d *DockerSource) Configure(Config []byte, logger *log.Entry) error { } d.Client = dockerClient + _, err = d.Client.Info(context.Background()) + + if err != nil { + return errors.Wrapf(err, "failed to configure docker datasource %s", d.Config.DockerHost) + } + return nil } @@ -158,7 +164,7 @@ func (d *DockerSource) ConfigureByDSN(dsn string, labels map[string]string, logg d.logger = logger d.Config.Labels = labels - dockerClient, err := client.NewClientWithOpts(client.FromEnv) + dockerClient, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) if err != nil { return err } @@ -389,7 +395,7 @@ func (d *DockerSource) WatchContainer(monitChan chan *ContainerConfig, deleteCha delete(d.runningContainerState, idx) } } else { - log.Debugf("container list err: %s", err.Error()) + log.Errorf("container list err: %s", err.Error()) } continue } From f46f0ae553fb0a5899e33ec885d6c00942f8b00c Mon Sep 17 00:00:00 2001 From: Manuel Sabban Date: Thu, 30 Dec 2021 14:47:51 +0100 Subject: [PATCH 04/14] add the ability for the wizard to work on raspbian (#1136) Co-authored-by: sabban <15465465+sabban@users.noreply.github.com> --- wizard.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wizard.sh b/wizard.sh index febefba13..a858b470c 100755 --- a/wizard.sh +++ b/wizard.sh @@ -132,7 +132,7 @@ detect_services () { fi; done; done; - if [[ ${OSTYPE} == "linux-gnu" ]]; then + if [[ ${OSTYPE} == "linux-gnu" ]] || [[ ${OSTYPE} == "linux-gnueabihf" ]]; then DETECTED_SERVICES+=("linux") HMENU+=("linux" "on") else From cf175ab07eb770ec33226d9e7495bbc151727cc5 Mon Sep 17 00:00:00 2001 From: mmetc <92726601+mmetc@users.noreply.github.com> Date: Mon, 3 Jan 2022 17:09:07 +0100 Subject: [PATCH 05/14] fixed "help collections list" message (#1142) * fixed "help collections list" message * corrected usage of "as well" --- cmd/crowdsec-cli/collections.go | 6 +++--- cmd/crowdsec-cli/hub.go | 2 +- cmd/crowdsec-cli/parsers.go | 2 +- cmd/crowdsec-cli/postoverflows.go | 2 +- cmd/crowdsec-cli/scenarios.go | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cmd/crowdsec-cli/collections.go b/cmd/crowdsec-cli/collections.go index 22963120c..002c84c47 100644 --- a/cmd/crowdsec-cli/collections.go +++ b/cmd/crowdsec-cli/collections.go @@ -136,8 +136,8 @@ func NewCollectionsCmd() *cobra.Command { var cmdCollectionsList = &cobra.Command{ Use: "list collection [-a]", - Short: "List all collections or given one", - Long: `List all collections or given one`, + Short: "List all collections", + Long: `List all collections`, Example: `cscli collections list`, Args: cobra.ExactArgs(0), DisableAutoGenTag: true, @@ -145,7 +145,7 @@ func NewCollectionsCmd() *cobra.Command { ListItem(cwhub.COLLECTIONS, args, false, true) }, } - cmdCollectionsList.PersistentFlags().BoolVarP(&all, "all", "a", false, "List as well disabled items") + cmdCollectionsList.PersistentFlags().BoolVarP(&all, "all", "a", false, "List disabled items as well") cmdCollections.AddCommand(cmdCollectionsList) return cmdCollections diff --git a/cmd/crowdsec-cli/hub.go b/cmd/crowdsec-cli/hub.go index 21bcfd7f6..7f08a5690 100644 --- a/cmd/crowdsec-cli/hub.go +++ b/cmd/crowdsec-cli/hub.go @@ -66,7 +66,7 @@ cscli hub update # Download list of available configurations from the hub ListItem(cwhub.PARSERS_OVFLW, args, true, false) }, } - cmdHubList.PersistentFlags().BoolVarP(&all, "all", "a", false, "List as well disabled items") + cmdHubList.PersistentFlags().BoolVarP(&all, "all", "a", false, "List disabled items as well") cmdHub.AddCommand(cmdHubList) var cmdHubUpdate = &cobra.Command{ diff --git a/cmd/crowdsec-cli/parsers.go b/cmd/crowdsec-cli/parsers.go index 2235bd8a1..28740d79f 100644 --- a/cmd/crowdsec-cli/parsers.go +++ b/cmd/crowdsec-cli/parsers.go @@ -135,7 +135,7 @@ cscli parser list crowdsecurity/xxx`, ListItem(cwhub.PARSERS, args, false, true) }, } - cmdParsersList.PersistentFlags().BoolVarP(&all, "all", "a", false, "List as well disabled items") + cmdParsersList.PersistentFlags().BoolVarP(&all, "all", "a", false, "List disabled items as well") cmdParsers.AddCommand(cmdParsersList) return cmdParsers diff --git a/cmd/crowdsec-cli/postoverflows.go b/cmd/crowdsec-cli/postoverflows.go index 8c38fe880..8b7a89f8e 100644 --- a/cmd/crowdsec-cli/postoverflows.go +++ b/cmd/crowdsec-cli/postoverflows.go @@ -133,7 +133,7 @@ cscli postoverflows list crowdsecurity/xxx`, ListItem(cwhub.PARSERS_OVFLW, args, false, true) }, } - cmdPostOverflowsList.PersistentFlags().BoolVarP(&all, "all", "a", false, "List as well disabled items") + cmdPostOverflowsList.PersistentFlags().BoolVarP(&all, "all", "a", false, "List disabled items as well") cmdPostOverflows.AddCommand(cmdPostOverflowsList) return cmdPostOverflows diff --git a/cmd/crowdsec-cli/scenarios.go b/cmd/crowdsec-cli/scenarios.go index 059c01f99..51b7ab796 100644 --- a/cmd/crowdsec-cli/scenarios.go +++ b/cmd/crowdsec-cli/scenarios.go @@ -135,7 +135,7 @@ cscli scenarios list crowdsecurity/xxx`, ListItem(cwhub.SCENARIOS, args, false, true) }, } - cmdScenariosList.PersistentFlags().BoolVarP(&all, "all", "a", false, "List as well disabled items") + cmdScenariosList.PersistentFlags().BoolVarP(&all, "all", "a", false, "List disabled items as well") cmdScenarios.AddCommand(cmdScenariosList) return cmdScenarios From 6c4ec64ca9249efdde6a379f4d22fd7b5fb211c6 Mon Sep 17 00:00:00 2001 From: Shivam Sandbhor Date: Tue, 4 Jan 2022 16:19:23 +0530 Subject: [PATCH 06/14] Fix json output of cscli hub list (#1143) * Fix json output of cscli hub list * Fix functional tests. Signed-off-by: Shivam Sandbhor --- cmd/crowdsec-cli/collections.go | 2 +- cmd/crowdsec-cli/hub.go | 9 +-- cmd/crowdsec-cli/parsers.go | 2 +- cmd/crowdsec-cli/postoverflows.go | 2 +- cmd/crowdsec-cli/scenarios.go | 2 +- cmd/crowdsec-cli/utils.go | 74 ++++++++++--------- pkg/cwhub/cwhub.go | 54 +++++++++----- pkg/cwhub/cwhub_test.go | 6 +- .../tests_post-install_2collections.sh | 6 +- 9 files changed, 85 insertions(+), 72 deletions(-) diff --git a/cmd/crowdsec-cli/collections.go b/cmd/crowdsec-cli/collections.go index 002c84c47..0a1298d5d 100644 --- a/cmd/crowdsec-cli/collections.go +++ b/cmd/crowdsec-cli/collections.go @@ -142,7 +142,7 @@ func NewCollectionsCmd() *cobra.Command { Args: cobra.ExactArgs(0), DisableAutoGenTag: true, Run: func(cmd *cobra.Command, args []string) { - ListItem(cwhub.COLLECTIONS, args, false, true) + ListItems([]string{cwhub.COLLECTIONS}, args, false, true) }, } cmdCollectionsList.PersistentFlags().BoolVarP(&all, "all", "a", false, "List disabled items as well") diff --git a/cmd/crowdsec-cli/hub.go b/cmd/crowdsec-cli/hub.go index 7f08a5690..721920160 100644 --- a/cmd/crowdsec-cli/hub.go +++ b/cmd/crowdsec-cli/hub.go @@ -56,14 +56,7 @@ cscli hub update # Download list of available configurations from the hub log.Info(v) } cwhub.DisplaySummary() - log.Printf("PARSERS:") - ListItem(cwhub.PARSERS, args, true, true) - log.Printf("SCENARIOS:") - ListItem(cwhub.SCENARIOS, args, true, false) - log.Printf("COLLECTIONS:") - ListItem(cwhub.COLLECTIONS, args, true, false) - log.Printf("POSTOVERFLOWS:") - ListItem(cwhub.PARSERS_OVFLW, args, true, false) + ListItems([]string{cwhub.PARSERS, cwhub.COLLECTIONS, cwhub.SCENARIOS, cwhub.PARSERS_OVFLW}, args, true, false) }, } cmdHubList.PersistentFlags().BoolVarP(&all, "all", "a", false, "List disabled items as well") diff --git a/cmd/crowdsec-cli/parsers.go b/cmd/crowdsec-cli/parsers.go index 28740d79f..cbf06660d 100644 --- a/cmd/crowdsec-cli/parsers.go +++ b/cmd/crowdsec-cli/parsers.go @@ -132,7 +132,7 @@ cscli parsers remove crowdsecurity/sshd-logs cscli parser list crowdsecurity/xxx`, DisableAutoGenTag: true, Run: func(cmd *cobra.Command, args []string) { - ListItem(cwhub.PARSERS, args, false, true) + ListItems([]string{cwhub.PARSERS}, args, false, true) }, } cmdParsersList.PersistentFlags().BoolVarP(&all, "all", "a", false, "List disabled items as well") diff --git a/cmd/crowdsec-cli/postoverflows.go b/cmd/crowdsec-cli/postoverflows.go index 8b7a89f8e..ceda1ad00 100644 --- a/cmd/crowdsec-cli/postoverflows.go +++ b/cmd/crowdsec-cli/postoverflows.go @@ -130,7 +130,7 @@ func NewPostOverflowsCmd() *cobra.Command { cscli postoverflows list crowdsecurity/xxx`, DisableAutoGenTag: true, Run: func(cmd *cobra.Command, args []string) { - ListItem(cwhub.PARSERS_OVFLW, args, false, true) + ListItems([]string{cwhub.PARSERS_OVFLW}, args, false, true) }, } cmdPostOverflowsList.PersistentFlags().BoolVarP(&all, "all", "a", false, "List disabled items as well") diff --git a/cmd/crowdsec-cli/scenarios.go b/cmd/crowdsec-cli/scenarios.go index 51b7ab796..807376c0f 100644 --- a/cmd/crowdsec-cli/scenarios.go +++ b/cmd/crowdsec-cli/scenarios.go @@ -132,7 +132,7 @@ cscli scenarios remove crowdsecurity/ssh-bf cscli scenarios list crowdsecurity/xxx`, DisableAutoGenTag: true, Run: func(cmd *cobra.Command, args []string) { - ListItem(cwhub.SCENARIOS, args, false, true) + ListItems([]string{cwhub.SCENARIOS}, args, false, true) }, } cmdScenariosList.PersistentFlags().BoolVarP(&all, "all", "a", false, "List disabled items as well") diff --git a/cmd/crowdsec-cli/utils.go b/cmd/crowdsec-cli/utils.go index 071831b6b..e198cb909 100644 --- a/cmd/crowdsec-cli/utils.go +++ b/cmd/crowdsec-cli/utils.go @@ -102,31 +102,35 @@ func setHubBranch() error { return nil } -func ListItem(itemType string, args []string, showType bool, showHeader bool) { +func ListItems(itemTypes []string, args []string, showType bool, showHeader bool) { - var hubStatus []map[string]string + var hubStatusByItemType = make(map[string][]cwhub.ItemHubStatus) - if len(args) == 1 { - hubStatus = cwhub.HubStatus(itemType, args[0], all) - } else { - hubStatus = cwhub.HubStatus(itemType, "", all) + for _, itemType := range itemTypes { + if len(args) == 1 { + // This means that user requested a specific item by name + hubStatusByItemType[itemType] = cwhub.GetHubStatusForItemType(itemType, args[0], all) + } else { + hubStatusByItemType[itemType] = cwhub.GetHubStatusForItemType(itemType, "", all) + } } if csConfig.Cscli.Output == "human" { - - table := tablewriter.NewWriter(os.Stdout) - table.SetCenterSeparator("") - table.SetColumnSeparator("") - - table.SetHeaderAlignment(tablewriter.ALIGN_LEFT) - table.SetAlignment(tablewriter.ALIGN_LEFT) - table.SetHeader([]string{"Name", fmt.Sprintf("%v Status", emoji.Package), "Version", "Local Path"}) - for _, v := range hubStatus { - table.Append([]string{v["name"], v["utf8_status"], v["local_version"], v["local_path"]}) + for itemType, statuses := range hubStatusByItemType { + fmt.Println(strings.ToUpper(itemType)) + table := tablewriter.NewWriter(os.Stdout) + table.SetCenterSeparator("") + table.SetColumnSeparator("") + table.SetHeaderAlignment(tablewriter.ALIGN_LEFT) + table.SetAlignment(tablewriter.ALIGN_LEFT) + table.SetHeader([]string{"Name", fmt.Sprintf("%v Status", emoji.Package), "Version", "Local Path"}) + for _, status := range statuses { + table.Append([]string{status.Name, status.UTF8_Status, status.LocalVersion, status.LocalPath}) + } + table.Render() } - table.Render() } else if csConfig.Cscli.Output == "json" { - x, err := json.MarshalIndent(hubStatus, "", " ") + x, err := json.MarshalIndent(hubStatusByItemType, "", " ") if err != nil { log.Fatalf("failed to unmarshal") } @@ -147,22 +151,24 @@ func ListItem(itemType string, args []string, showType bool, showHeader bool) { } } - for _, v := range hubStatus { - if v["local_version"] == "" { - v["local_version"] = "n/a" - } - row := []string{ - v["name"], - v["status"], - v["local_version"], - v["description"], - } - if showType { - row = append(row, itemType) - } - err := csvwriter.Write(row) - if err != nil { - log.Fatalf("failed to write raw output : %s", err) + for itemType, statuses := range hubStatusByItemType { + for _, status := range statuses { + if status.LocalVersion == "" { + status.LocalVersion = "n/a" + } + row := []string{ + status.Name, + status.Status, + status.LocalVersion, + status.Description, + } + if showType { + row = append(row, itemType) + } + err := csvwriter.Write(row) + if err != nil { + log.Fatalf("failed to write raw output : %s", err) + } } } csvwriter.Flush() diff --git a/pkg/cwhub/cwhub.go b/pkg/cwhub/cwhub.go index b8dac1935..79b28207e 100644 --- a/pkg/cwhub/cwhub.go +++ b/pkg/cwhub/cwhub.go @@ -35,6 +35,15 @@ type ItemVersion struct { Deprecated bool } +type ItemHubStatus struct { + Name string `json:"name"` + LocalVersion string `json:"local_version"` + LocalPath string `json:"local_path"` + Description string `json:"description"` + UTF8_Status string `json:"utf8_status"` + Status string `json:"status"` +} + //Item can be : parsed, scenario, collection type Item struct { /*descriptive info*/ @@ -72,6 +81,27 @@ type Item struct { Collections []string `yaml:"collections,omitempty"` } +func (i *Item) toHubStatus() ItemHubStatus { + hubStatus := ItemHubStatus{} + hubStatus.Name = i.Name + hubStatus.LocalVersion = i.LocalVersion + hubStatus.LocalPath = i.LocalPath + hubStatus.Description = i.Description + + status, ok, warning, managed := ItemStatus(*i) + hubStatus.Status = status + if !managed { + hubStatus.UTF8_Status = fmt.Sprintf("%v %s", emoji.House, status) + } else if !i.Installed { + hubStatus.UTF8_Status = fmt.Sprintf("%v %s", emoji.Prohibited, status) + } else if warning { + hubStatus.UTF8_Status = fmt.Sprintf("%v %s", emoji.Warning, status) + } else if ok { + hubStatus.UTF8_Status = fmt.Sprintf("%v %s", emoji.CheckMark, status) + } + return hubStatus +} + var skippedLocal = 0 var skippedTainted = 0 @@ -240,18 +270,18 @@ func GetUpstreamInstalledScenarios() ([]Item, error) { } //Returns a list of entries for packages : name, status, local_path, local_version, utf8_status (fancy) -func HubStatus(itemType string, name string, all bool) []map[string]string { +func GetHubStatusForItemType(itemType string, name string, all bool) []ItemHubStatus { if _, ok := hubIdx[itemType]; !ok { log.Errorf("type %s doesn't exist", itemType) return nil } - var ret []map[string]string + var ret = make([]ItemHubStatus, 0) /*remember, you do it for the user :)*/ for _, item := range hubIdx[itemType] { if name != "" && name != item.Name { - //user has required a specific name + //user has requested a specific name continue } //Only enabled items ? @@ -259,23 +289,7 @@ func HubStatus(itemType string, name string, all bool) []map[string]string { continue } //Check the item status - status, ok, warning, managed := ItemStatus(item) - tmp := make(map[string]string) - tmp["name"] = item.Name - tmp["status"] = status - tmp["local_version"] = item.LocalVersion - tmp["local_path"] = item.LocalPath - tmp["description"] = item.Description - if !managed { - tmp["utf8_status"] = fmt.Sprintf("%v %s", emoji.House, status) - } else if !item.Installed { - tmp["utf8_status"] = fmt.Sprintf("%v %s", emoji.Prohibited, status) - } else if warning { - tmp["utf8_status"] = fmt.Sprintf("%v %s", emoji.Warning, status) - } else if ok { - tmp["utf8_status"] = fmt.Sprintf("%v %s", emoji.CheckMark, status) - } - ret = append(ret, tmp) + ret = append(ret, item.toHubStatus()) } return ret } diff --git a/pkg/cwhub/cwhub_test.go b/pkg/cwhub/cwhub_test.go index cba548178..e59df4089 100644 --- a/pkg/cwhub/cwhub_test.go +++ b/pkg/cwhub/cwhub_test.go @@ -321,10 +321,10 @@ func TestInstallParser(t *testing.T) { for _, it := range hubIdx[PARSERS] { testInstallItem(cfg.Hub, t, it) it = hubIdx[PARSERS][it.Name] - _ = HubStatus(PARSERS, it.Name, false) + _ = GetHubStatusForItemType(PARSERS, it.Name, false) testTaintItem(cfg.Hub, t, it) it = hubIdx[PARSERS][it.Name] - _ = HubStatus(PARSERS, it.Name, false) + _ = GetHubStatusForItemType(PARSERS, it.Name, false) testUpdateItem(cfg.Hub, t, it) it = hubIdx[PARSERS][it.Name] testDisableItem(cfg.Hub, t, it) @@ -361,7 +361,7 @@ func TestInstallCollection(t *testing.T) { testDisableItem(cfg.Hub, t, it) it = hubIdx[COLLECTIONS][it.Name] - x := HubStatus(COLLECTIONS, it.Name, false) + x := GetHubStatusForItemType(COLLECTIONS, it.Name, false) log.Printf("%+v", x) break } diff --git a/scripts/func_tests/tests_post-install_2collections.sh b/scripts/func_tests/tests_post-install_2collections.sh index eff573aef..2fbad686a 100755 --- a/scripts/func_tests/tests_post-install_2collections.sh +++ b/scripts/func_tests/tests_post-install_2collections.sh @@ -10,7 +10,7 @@ ${CSCLI_BIN} collections list || fail "failed to list collections" BASE_COLLECTION_COUNT=2 # we expect 1 collections : linux -${CSCLI_BIN} collections list -ojson | ${JQ} ". | length == ${BASE_COLLECTION_COUNT}" || fail "(first) expected exactly ${BASE_COLLECTION_COUNT} collection" +${CSCLI_BIN} collections list -ojson | ${JQ} ".collections | length == ${BASE_COLLECTION_COUNT}" || fail "(first) expected exactly ${BASE_COLLECTION_COUNT} collection" # install an extra collection ${CSCLI} collections install crowdsecurity/mysql || fail "failed to install collection" @@ -18,7 +18,7 @@ ${CSCLI} collections install crowdsecurity/mysql || fail "failed to install coll BASE_COLLECTION_COUNT=$(($BASE_COLLECTION_COUNT+1)) # we should now have 2 collections :) -${CSCLI_BIN} collections list -ojson | ${JQ} ". | length == ${BASE_COLLECTION_COUNT}" || fail "(post install) expected exactly ${BASE_COLLECTION_COUNT} collection" +${CSCLI_BIN} collections list -ojson | ${JQ} ".collections | length == ${BASE_COLLECTION_COUNT}" || fail "(post install) expected exactly ${BASE_COLLECTION_COUNT} collection" # remove the collection ${CSCLI} collections remove crowdsecurity/mysql || fail "failed to remove collection" @@ -26,5 +26,5 @@ ${CSCLI} collections remove crowdsecurity/mysql || fail "failed to remove collec BASE_COLLECTION_COUNT=$(($BASE_COLLECTION_COUNT-1)) # we expect 1 collections : linux -${CSCLI_BIN} collections list -ojson | ${JQ} ". | length == ${BASE_COLLECTION_COUNT}" || fail "(post remove) expected exactly ${BASE_COLLECTION_COUNT} collection" +${CSCLI_BIN} collections list -ojson | ${JQ} ".collections | length == ${BASE_COLLECTION_COUNT}" || fail "(post remove) expected exactly ${BASE_COLLECTION_COUNT} collection" From 8e3004ebb3fbcb979c4c70b804c1d7b37a7772e4 Mon Sep 17 00:00:00 2001 From: "Thibault \"bui\" Koechlin" Date: Tue, 4 Jan 2022 14:02:07 +0100 Subject: [PATCH 07/14] fix race condition on repetitive trigger buckets creation (#1144) --- pkg/leakybucket/manager_run.go | 264 ++++++++++++++++++--------------- 1 file changed, 145 insertions(+), 119 deletions(-) diff --git a/pkg/leakybucket/manager_run.go b/pkg/leakybucket/manager_run.go index 5cd94efe5..6fb24829b 100644 --- a/pkg/leakybucket/manager_run.go +++ b/pkg/leakybucket/manager_run.go @@ -2,13 +2,14 @@ package leakybucket import ( "encoding/json" - "errors" "fmt" "io/ioutil" "math" "os" "time" + "github.com/pkg/errors" + "github.com/mohae/deepcopy" log "github.com/sirupsen/logrus" @@ -154,14 +155,137 @@ func ShutdownAllBuckets(buckets *Buckets) error { return nil } +func PourItemToBucket(bucket *Leaky, holder BucketFactory, buckets *Buckets, parsed types.Event) (bool, error) { + var sent bool + var buckey = bucket.Mapkey + var err error + + sigclosed := 0 + failed_sent := 0 + attempts := 0 + start := time.Now() + + for !sent { + attempts += 1 + /* Warn the user if we used more than a 100 ms to pour an event, it's at least an half lock*/ + if attempts%100000 == 0 && start.Add(100*time.Millisecond).Before(time.Now()) { + holder.logger.Warningf("stuck for %s sending event to %s (sigclosed:%d failed_sent:%d attempts:%d)", time.Since(start), + buckey, sigclosed, failed_sent, attempts) + } + + /* check if leak routine is up */ + select { + case _, ok := <-bucket.Signal: + if !ok { + //the bucket was found and dead, get a new one and continue + bucket.logger.Tracef("Bucket %s found dead, cleanup the body", buckey) + buckets.Bucket_map.Delete(buckey) + sigclosed += 1 + bucket, err = LoadOrStoreBucketFromHolder(buckey, buckets, holder, parsed.ExpectMode) + if err != nil { + return false, err + } + continue + } + holder.logger.Tracef("Signal exists, try to pour :)") + default: + /*nothing to read, but not closed, try to pour */ + holder.logger.Tracef("Signal exists but empty, try to pour :)") + } + + /*let's see if this time-bucket should have expired */ + if bucket.Mode == TIMEMACHINE { + bucket.mutex.Lock() + firstTs := bucket.First_ts + lastTs := bucket.Last_ts + bucket.mutex.Unlock() + + if !firstTs.IsZero() { + var d time.Time + err = d.UnmarshalText([]byte(parsed.MarshaledTime)) + if err != nil { + holder.logger.Warningf("Failed unmarshaling event time (%s) : %v", parsed.MarshaledTime, err) + } + if d.After(lastTs.Add(bucket.Duration)) { + bucket.logger.Tracef("bucket is expired (curr event: %s, bucket deadline: %s), kill", d, lastTs.Add(bucket.Duration)) + buckets.Bucket_map.Delete(buckey) + //not sure about this, should we create a new one ? + sigclosed += 1 + bucket, err = LoadOrStoreBucketFromHolder(buckey, buckets, holder, parsed.ExpectMode) + if err != nil { + return false, err + } + continue + } + } + } + /*the bucket seems to be up & running*/ + select { + case bucket.In <- parsed: + holder.logger.Tracef("Successfully sent !") + if BucketPourTrack { + if _, ok := BucketPourCache[bucket.Name]; !ok { + BucketPourCache[bucket.Name] = make([]types.Event, 0) + } + evt := deepcopy.Copy(parsed) + BucketPourCache[bucket.Name] = append(BucketPourCache[bucket.Name], evt.(types.Event)) + } + sent = true + continue + default: + failed_sent += 1 + holder.logger.Tracef("Failed to send, try again") + continue + + } + } + holder.logger.Debugf("bucket '%s' is poured", holder.Name) + return sent, nil +} + +func LoadOrStoreBucketFromHolder(partitionKey string, buckets *Buckets, holder BucketFactory, expectMode int) (*Leaky, error) { + + biface, ok := buckets.Bucket_map.Load(partitionKey) + + /* the bucket doesn't exist, create it !*/ + if !ok { + var fresh_bucket *Leaky + + switch expectMode { + case TIMEMACHINE: + fresh_bucket = NewTimeMachine(holder) + holder.logger.Debugf("Creating TimeMachine bucket") + case LIVE: + fresh_bucket = NewLeaky(holder) + holder.logger.Debugf("Creating Live bucket") + default: + return nil, fmt.Errorf("input event has no expected mode : %+v", expectMode) + } + fresh_bucket.In = make(chan types.Event) + fresh_bucket.Mapkey = partitionKey + fresh_bucket.Signal = make(chan bool, 1) + actual, stored := buckets.Bucket_map.LoadOrStore(partitionKey, fresh_bucket) + if !stored { + holder.tomb.Go(func() error { + return LeakRoutine(fresh_bucket) + }) + biface = fresh_bucket + //once the created goroutine is ready to process event, we can return it + <-fresh_bucket.Signal + } else { + holder.logger.Debugf("Unexpectedly found exisint bucket for %s", partitionKey) + biface = actual + } + holder.logger.Debugf("Created new bucket %s", partitionKey) + } + return biface.(*Leaky), nil +} + func PourItemToHolders(parsed types.Event, holders []BucketFactory, buckets *Buckets) (bool, error) { var ( - ok, condition, sent bool - err error + ok, condition, poured bool ) - //synchronize with DumpBucketsStateAt - //to track bucket pour : track items that enter the pour routine if BucketPourTrack { if BucketPourCache == nil { BucketPourCache = make(map[string][]types.Event) @@ -173,8 +297,10 @@ func PourItemToHolders(parsed types.Event, holders []BucketFactory, buckets *Buc BucketPourCache["OK"] = append(BucketPourCache["OK"], evt.(types.Event)) } + //find the relevant holders (scenarios) for idx, holder := range holders { + //evaluate bucket's condition if holder.RunTimeFilter != nil { holder.logger.Tracef("event against holder %d/%d", idx, len(holders)) output, err := expr.Run(holder.RunTimeFilter, exprhelpers.GetExprEnv(map[string]interface{}{"evt": &parsed})) @@ -197,7 +323,7 @@ func PourItemToHolders(parsed types.Event, holders []BucketFactory, buckets *Buc } } - sent = false + //groupby determines the partition key for the specific bucket var groupby string if holder.RunTimeGroupBy != nil { tmpGroupBy, err := expr.Run(holder.RunTimeGroupBy, exprhelpers.GetExprEnv(map[string]interface{}{"evt": &parsed})) @@ -213,119 +339,19 @@ func PourItemToHolders(parsed types.Event, holders []BucketFactory, buckets *Buc } buckey := GetKey(holder, groupby) - sigclosed := 0 - keymiss := 0 - failed_sent := 0 - attempts := 0 - start := time.Now() - for !sent { - attempts += 1 - /* Warn the user if we used more than a 100 ms to pour an event, it's at least an half lock*/ - if attempts%100000 == 0 && start.Add(100*time.Millisecond).Before(time.Now()) { - holder.logger.Warningf("stuck for %s sending event to %s (sigclosed:%d keymiss:%d failed_sent:%d attempts:%d)", time.Since(start), - buckey, sigclosed, keymiss, failed_sent, attempts) - } - biface, ok := buckets.Bucket_map.Load(buckey) - //biface, bigout - /* the bucket doesn't exist, create it !*/ - if !ok { - /* - not found in map - */ - - holder.logger.Debugf("Creating bucket %s", buckey) - keymiss += 1 - var fresh_bucket *Leaky - - switch parsed.ExpectMode { - case TIMEMACHINE: - fresh_bucket = NewTimeMachine(holder) - holder.logger.Debugf("Creating TimeMachine bucket") - case LIVE: - fresh_bucket = NewLeaky(holder) - holder.logger.Debugf("Creating Live bucket") - default: - holder.logger.Fatalf("input event has no expected mode, malformed : %+v", parsed) - } - fresh_bucket.In = make(chan types.Event) - fresh_bucket.Mapkey = buckey - fresh_bucket.Signal = make(chan bool, 1) - buckets.Bucket_map.Store(buckey, fresh_bucket) - holder.tomb.Go(func() error { - return LeakRoutine(fresh_bucket) - }) - - holder.logger.Debugf("Created new bucket %s", buckey) - - //wait for signal to be opened - <-fresh_bucket.Signal - continue - } - - bucket := biface.(*Leaky) - /* check if leak routine is up */ - select { - case _, ok := <-bucket.Signal: - if !ok { - //it's closed, delete it - bucket.logger.Debugf("Bucket %s found dead, cleanup the body", buckey) - buckets.Bucket_map.Delete(buckey) - sigclosed += 1 - continue - } - holder.logger.Tracef("Signal exists, try to pour :)") - - default: - /*nothing to read, but not closed, try to pour */ - holder.logger.Tracef("Signal exists but empty, try to pour :)") - - } - /*let's see if this time-bucket should have expired */ - if bucket.Mode == TIMEMACHINE { - bucket.mutex.Lock() - firstTs := bucket.First_ts - lastTs := bucket.Last_ts - bucket.mutex.Unlock() - - if !firstTs.IsZero() { - var d time.Time - err = d.UnmarshalText([]byte(parsed.MarshaledTime)) - if err != nil { - holder.logger.Warningf("Failed unmarshaling event time (%s) : %v", parsed.MarshaledTime, err) - } - if d.After(lastTs.Add(bucket.Duration)) { - bucket.logger.Tracef("bucket is expired (curr event: %s, bucket deadline: %s), kill", d, lastTs.Add(bucket.Duration)) - buckets.Bucket_map.Delete(buckey) - continue - } - } - } - /*if we're here, let's try to pour */ - - select { - case bucket.In <- parsed: - holder.logger.Tracef("Successfully sent !") - //and track item poured to each bucket - if BucketPourTrack { - if _, ok := BucketPourCache[bucket.Name]; !ok { - BucketPourCache[bucket.Name] = make([]types.Event, 0) - } - evt := deepcopy.Copy(parsed) - BucketPourCache[bucket.Name] = append(BucketPourCache[bucket.Name], evt.(types.Event)) - } - - //sent was successful ! - sent = true - continue - default: - failed_sent += 1 - holder.logger.Tracef("Failed to send, try again") - continue - - } + //we need to either find the existing bucket, or create a new one (if it's the first event to hit it for this partition key) + bucket, err := LoadOrStoreBucketFromHolder(buckey, buckets, holder, parsed.ExpectMode) + if err != nil { + return false, errors.Wrap(err, "failed to load or store bucket") + } + //finally, pour the even into the bucket + ok, err := PourItemToBucket(bucket, holder, buckets, parsed) + if err != nil { + return false, errors.Wrap(err, "failed to pour bucket") + } + if ok { + poured = true } - - holder.logger.Debugf("bucket '%s' is poured", holder.Name) } - return sent, nil + return poured, nil } From ba71c554926cc13b9b2637b9b292eb759c578020 Mon Sep 17 00:00:00 2001 From: Shivam Sandbhor Date: Wed, 5 Jan 2022 15:12:27 +0530 Subject: [PATCH 08/14] Fix cscli inpsect json output (#1145) * Fix cscli inpsect json output Signed-off-by: Shivam Sandbhor --- cmd/crowdsec-cli/utils.go | 22 +++++++++++++--- pkg/cwhub/cwhub.go | 54 +++++++++++++++++++-------------------- 2 files changed, 45 insertions(+), 31 deletions(-) diff --git a/cmd/crowdsec-cli/utils.go b/cmd/crowdsec-cli/utils.go index e198cb909..8fa387bf8 100644 --- a/cmd/crowdsec-cli/utils.go +++ b/cmd/crowdsec-cli/utils.go @@ -306,11 +306,25 @@ func InspectItem(name string, objecitemType string) { if hubItem == nil { log.Fatalf("unable to retrieve item.") } - buff, err := yaml.Marshal(*hubItem) - if err != nil { - log.Fatalf("unable to marshal item : %s", err) + var b []byte + var err error + switch csConfig.Cscli.Output { + case "human", "raw": + b, err = yaml.Marshal(*hubItem) + if err != nil { + log.Fatalf("unable to marshal item : %s", err) + } + case "json": + b, err = json.MarshalIndent(*hubItem, "", " ") + if err != nil { + log.Fatalf("unable to marshal item : %s", err) + } } - fmt.Printf("%s", string(buff)) + fmt.Printf("%s", string(b)) + if csConfig.Cscli.Output == "json" || csConfig.Cscli.Output == "raw" { + return + } + if csConfig.Prometheus.Enabled { if csConfig.Prometheus.ListenAddr == "" || csConfig.Prometheus.ListenPort == 0 { log.Warningf("No prometheus address or port specified in '%s', can't show metrics", *csConfig.FilePath) diff --git a/pkg/cwhub/cwhub.go b/pkg/cwhub/cwhub.go index 79b28207e..1e03b8412 100644 --- a/pkg/cwhub/cwhub.go +++ b/pkg/cwhub/cwhub.go @@ -31,8 +31,8 @@ var HubBranch = "master" var HubIndexFile = ".index.json" type ItemVersion struct { - Digest string - Deprecated bool + Digest string `json:"digest,omitempty"` + Deprecated bool `json:"deprecated,omitempty"` } type ItemHubStatus struct { @@ -47,38 +47,38 @@ type ItemHubStatus struct { //Item can be : parsed, scenario, collection type Item struct { /*descriptive info*/ - Type string `yaml:"type,omitempty"` //parser|postoverflows|scenario|collection(|enrich) - Stage string `json:"stage" yaml:"stage,omitempty,omitempty"` //Stage for parser|postoverflow : s00-raw/s01-... - Name string //as seen in .config.json, usually "author/name" - FileName string //the filename, ie. apache2-logs.yaml - Description string `yaml:"description,omitempty"` //as seen in .config.json - Author string `json:"author"` //as seen in .config.json - References []string `yaml:"references,omitempty"` //as seen in .config.json - BelongsToCollections []string `yaml:"belongs_to_collections,omitempty"` /*if it's part of collections, track name here*/ + Type string `yaml:"type,omitempty" json:"type,omitempty"` //parser|postoverflows|scenario|collection(|enrich) + Stage string `json:"stage,omitempty" yaml:"stage,omitempty,omitempty"` //Stage for parser|postoverflow : s00-raw/s01-... + Name string `json:"name,omitempty"` //as seen in .config.json, usually "author/name" + FileName string `json:"file_name,omitempty"` //the filename, ie. apache2-logs.yaml + Description string `yaml:"description,omitempty" json:"description,omitempty"` //as seen in .config.json + Author string `json:"author,omitempty"` //as seen in .config.json + References []string `yaml:"references,omitempty" json:"references,omitempty"` //as seen in .config.json + BelongsToCollections []string `yaml:"belongs_to_collections,omitempty" json:"belongs_to_collections,omitempty"` /*if it's part of collections, track name here*/ /*remote (hub) infos*/ - RemoteURL string `yaml:"remoteURL,omitempty"` //the full remote uri of file in http - RemotePath string `json:"path" yaml:"remote_path,omitempty"` //the path relative to git ie. /parsers/stage/author/file.yaml - RemoteHash string `yaml:"hash,omitempty"` //the meow - Version string `json:"version"` //the last version - Versions map[string]ItemVersion `json:"versions" yaml:"-"` //the list of existing versions + RemoteURL string `yaml:"remoteURL,omitempty" json:"remoteURL,omitempty"` //the full remote uri of file in http + RemotePath string `json:"path,omitempty" yaml:"remote_path,omitempty"` //the path relative to git ie. /parsers/stage/author/file.yaml + RemoteHash string `yaml:"hash,omitempty" json:"hash,omitempty"` //the meow + Version string `json:"version,omitempty"` //the last version + Versions map[string]ItemVersion `json:"versions,omitempty" yaml:"-"` //the list of existing versions /*local (deployed) infos*/ - LocalPath string `yaml:"local_path,omitempty"` //the local path relative to ${CFG_DIR} + LocalPath string `yaml:"local_path,omitempty" json:"local_path,omitempty"` //the local path relative to ${CFG_DIR} //LocalHubPath string - LocalVersion string - LocalHash string //the local meow - Installed bool - Downloaded bool - UpToDate bool - Tainted bool //has it been locally modified - Local bool //if it's a non versioned control one + LocalVersion string `json:"local_version,omitempty"` + LocalHash string `json:"local_hash,omitempty"` //the local meow + Installed bool `json:"installed,omitempty"` + Downloaded bool `json:"downloaded,omitempty"` + UpToDate bool `json:"up_to_date,omitempty"` + Tainted bool `json:"tainted,omitempty"` //has it been locally modified + Local bool `json:"local,omitempty"` //if it's a non versioned control one /*if it's a collection, it not a single file*/ - Parsers []string `yaml:"parsers,omitempty"` - PostOverflows []string `yaml:"postoverflows,omitempty"` - Scenarios []string `yaml:"scenarios,omitempty"` - Collections []string `yaml:"collections,omitempty"` + Parsers []string `yaml:"parsers,omitempty" json:"parsers,omitempty"` + PostOverflows []string `yaml:"postoverflows,omitempty" json:"postoverflows,omitempty"` + Scenarios []string `yaml:"scenarios,omitempty" json:"scenarios,omitempty"` + Collections []string `yaml:"collections,omitempty" json:"collections,omitempty"` } func (i *Item) toHubStatus() ItemHubStatus { From 6c676c4869fb082b7894948d46f2eccc264f897d Mon Sep 17 00:00:00 2001 From: "Thibault \"bui\" Koechlin" Date: Wed, 5 Jan 2022 13:50:04 +0100 Subject: [PATCH 09/14] fix #1131 : complain when validating unknown machine (#1146) --- pkg/database/machines.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pkg/database/machines.go b/pkg/database/machines.go index 893098184..3e424a7dc 100644 --- a/pkg/database/machines.go +++ b/pkg/database/machines.go @@ -77,10 +77,13 @@ func (c *Client) ListMachines() ([]*ent.Machine, error) { } func (c *Client) ValidateMachine(machineID string) error { - _, err := c.Ent.Machine.Update().Where(machine.MachineIdEQ(machineID)).SetIsValidated(true).Save(c.CTX) + rets, err := c.Ent.Machine.Update().Where(machine.MachineIdEQ(machineID)).SetIsValidated(true).Save(c.CTX) if err != nil { return errors.Wrapf(UpdateFail, "validating machine: %s", err) } + if rets == 0 { + return fmt.Errorf("machine not found") + } return nil } From a6e405422c732e9b6c46ae1004a2b80297df8336 Mon Sep 17 00:00:00 2001 From: Shivam Sandbhor Date: Thu, 6 Jan 2022 15:50:59 +0530 Subject: [PATCH 10/14] Add email notification plugin. (#1013) * Add email notification plugin. * Add plugin binary to gitignore Signed-off-by: Shivam Sandbhor --- .gitignore | 2 +- Makefile | 28 +- config/profiles.yaml | 1 + plugins/notifications/email/LICENSE | 21 + plugins/notifications/email/Makefile | 16 + plugins/notifications/email/email.yaml | 35 + plugins/notifications/email/go.mod | 11 + plugins/notifications/email/go.sum | 893 +++++++++++++++++++++++++ plugins/notifications/email/main.go | 122 ++++ wizard.sh | 4 + 10 files changed, 1130 insertions(+), 3 deletions(-) create mode 100644 plugins/notifications/email/LICENSE create mode 100644 plugins/notifications/email/Makefile create mode 100644 plugins/notifications/email/email.yaml create mode 100644 plugins/notifications/email/go.mod create mode 100644 plugins/notifications/email/go.sum create mode 100644 plugins/notifications/email/main.go diff --git a/.gitignore b/.gitignore index ef8480590..7f4d2f119 100644 --- a/.gitignore +++ b/.gitignore @@ -23,4 +23,4 @@ cmd/crowdsec/crowdsec plugins/notifications/http/notification-http plugins/notifications/slack/notification-slack plugins/notifications/splunk/notification-splunk - +plugins/notifications/email/notification-email diff --git a/Makefile b/Makefile index 0a2e8b614..51b1513a3 100644 --- a/Makefile +++ b/Makefile @@ -15,15 +15,22 @@ DATA_PREFIX = $(PREFIX)"/var/run/crowdsec/" PID_DIR = $(PREFIX)"/var/run/" CROWDSEC_FOLDER = "./cmd/crowdsec" CSCLI_FOLDER = "./cmd/crowdsec-cli/" + HTTP_PLUGIN_FOLDER = "./plugins/notifications/http" SLACK_PLUGIN_FOLDER = "./plugins/notifications/slack" SPLUNK_PLUGIN_FOLDER = "./plugins/notifications/splunk" +EMAIL_PLUGIN_FOLDER = "./plugins/notifications/email" + HTTP_PLUGIN_BIN = "notification-http" SLACK_PLUGIN_BIN = "notification-slack" SPLUNK_PLUGIN_BIN = "notification-splunk" +EMAIL_PLUGIN_BIN = "notification-email" + HTTP_PLUGIN_CONFIG = "http.yaml" SLACK_PLUGIN_CONFIG = "slack.yaml" SPLUNK_PLUGIN_CONFIG = "splunk.yaml" +EMAIL_PLUGIN_CONFIG = "email.yaml" + CROWDSEC_BIN = "crowdsec" CSCLI_BIN = "cscli" BUILD_CMD = "build" @@ -66,9 +73,9 @@ build: goversion crowdsec cscli plugins static: crowdsec_static cscli_static plugins_static -plugins: http-plugin slack-plugin splunk-plugin +plugins: http-plugin slack-plugin splunk-plugin email-plugin -plugins_static: http-plugin_static slack-plugin_static splunk-plugin_static +plugins_static: http-plugin_static slack-plugin_static splunk-plugin_static email-plugin_static goversion: @if [ $(GO_MAJOR_VERSION) -gt $(MINIMUM_SUPPORTED_GO_MAJOR_VERSION) ]; then \ @@ -107,6 +114,8 @@ slack-plugin: goversion splunk-plugin: goversion @GOARCH=$(GOARCH) GOOS=$(GOOS) $(MAKE) -C $(SPLUNK_PLUGIN_FOLDER) build --no-print-directory +email-plugin: goversion + @GOARCH=$(GOARCH) GOOS=$(GOOS) $(MAKE) -C $(EMAIL_PLUGIN_FOLDER) build --no-print-directory cscli_static: goversion @GOARCH=$(GOARCH) GOOS=$(GOOS) $(MAKE) -C $(CSCLI_FOLDER) static --no-print-directory @@ -123,6 +132,9 @@ slack-plugin_static: goversion splunk-plugin_static:goversion @GOARCH=$(GOARCH) GOOS=$(GOOS) $(MAKE) -C $(SPLUNK_PLUGIN_FOLDER) static --no-print-directory +email-plugin_static:goversion + @GOARCH=$(GOARCH) GOOS=$(GOOS) $(MAKE) -C $(EMAIL_PLUGIN_FOLDER) static --no-print-directory + test: goversion @$(MAKE) -C $(CROWDSEC_FOLDER) test --no-print-directory @@ -133,15 +145,21 @@ package: @mkdir -p $(RELDIR)/$(subst ./,,$(HTTP_PLUGIN_FOLDER)) @mkdir -p $(RELDIR)/$(subst ./,,$(SLACK_PLUGIN_FOLDER)) @mkdir -p $(RELDIR)/$(subst ./,,$(SPLUNK_PLUGIN_FOLDER)) + @mkdir -p $(RELDIR)/$(subst ./,,$(EMAIL_PLUGIN_FOLDER)) @cp $(CROWDSEC_FOLDER)/$(CROWDSEC_BIN) $(RELDIR)/cmd/crowdsec @cp $(CSCLI_FOLDER)/$(CSCLI_BIN) $(RELDIR)/cmd/crowdsec-cli + @cp $(HTTP_PLUGIN_FOLDER)/$(HTTP_PLUGIN_BIN) $(RELDIR)/$(subst ./,,$(HTTP_PLUGIN_FOLDER)) @cp $(SLACK_PLUGIN_FOLDER)/$(SLACK_PLUGIN_BIN) $(RELDIR)/$(subst ./,,$(SLACK_PLUGIN_FOLDER)) @cp $(SPLUNK_PLUGIN_FOLDER)/$(SPLUNK_PLUGIN_BIN) $(RELDIR)/$(subst ./,,$(SPLUNK_PLUGIN_FOLDER)) + @cp $(EMAIL_PLUGIN_FOLDER)/$(EMAIL_PLUGIN_BIN) $(RELDIR)/$(subst ./,,$(EMAIL_PLUGIN_FOLDER)) + @cp $(HTTP_PLUGIN_FOLDER)/$(HTTP_PLUGIN_CONFIG) $(RELDIR)/$(subst ./,,$(HTTP_PLUGIN_FOLDER)) @cp $(SLACK_PLUGIN_FOLDER)/$(SLACK_PLUGIN_CONFIG) $(RELDIR)/$(subst ./,,$(SLACK_PLUGIN_FOLDER)) @cp $(SPLUNK_PLUGIN_FOLDER)/$(SPLUNK_PLUGIN_CONFIG) $(RELDIR)/$(subst ./,,$(SPLUNK_PLUGIN_FOLDER)) + @cp $(EMAIL_PLUGIN_FOLDER)/$(EMAIL_PLUGIN_CONFIG) $(RELDIR)/$(subst ./,,$(EMAIL_PLUGIN_FOLDER)) + @cp -R ./config/ $(RELDIR) @cp wizard.sh $(RELDIR) @cp scripts/test_env.sh $(RELDIR) @@ -154,15 +172,21 @@ package_static: @mkdir -p $(RELDIR)/$(subst ./,,$(HTTP_PLUGIN_FOLDER)) @mkdir -p $(RELDIR)/$(subst ./,,$(SLACK_PLUGIN_FOLDER)) @mkdir -p $(RELDIR)/$(subst ./,,$(SPLUNK_PLUGIN_FOLDER)) + @mkdir -p $(RELDIR)/$(subst ./,,$(EMAIL_PLUGIN_FOLDER)) @cp $(CROWDSEC_FOLDER)/$(CROWDSEC_BIN) $(RELDIR)/cmd/crowdsec @cp $(CSCLI_FOLDER)/$(CSCLI_BIN) $(RELDIR)/cmd/crowdsec-cli + @cp $(HTTP_PLUGIN_FOLDER)/$(HTTP_PLUGIN_BIN) $(RELDIR)/$(subst ./,,$(HTTP_PLUGIN_FOLDER)) @cp $(SLACK_PLUGIN_FOLDER)/$(SLACK_PLUGIN_BIN) $(RELDIR)/$(subst ./,,$(SLACK_PLUGIN_FOLDER)) @cp $(SPLUNK_PLUGIN_FOLDER)/$(SPLUNK_PLUGIN_BIN) $(RELDIR)/$(subst ./,,$(SPLUNK_PLUGIN_FOLDER)) + @cp $(EMAIL_PLUGIN_FOLDER)/$(EMAIL_PLUGIN_BIN) $(RELDIR)/$(subst ./,,$(EMAIL_PLUGIN_FOLDER)) + @cp $(HTTP_PLUGIN_FOLDER)/$(HTTP_PLUGIN_CONFIG) $(RELDIR)/$(subst ./,,$(HTTP_PLUGIN_FOLDER)) @cp $(SLACK_PLUGIN_FOLDER)/$(SLACK_PLUGIN_CONFIG) $(RELDIR)/$(subst ./,,$(SLACK_PLUGIN_FOLDER)) @cp $(SPLUNK_PLUGIN_FOLDER)/$(SPLUNK_PLUGIN_CONFIG) $(RELDIR)/$(subst ./,,$(SPLUNK_PLUGIN_FOLDER)) + @cp $(EMAIL_PLUGIN_FOLDER)/$(EMAIL_PLUGIN_CONFIG) $(RELDIR)/$(subst ./,,$(EMAIL_PLUGIN_FOLDER)) + @cp -R ./config/ $(RELDIR) @cp wizard.sh $(RELDIR) @cp scripts/test_env.sh $(RELDIR) diff --git a/config/profiles.yaml b/config/profiles.yaml index b24eabb4f..ebbf44db7 100644 --- a/config/profiles.yaml +++ b/config/profiles.yaml @@ -9,4 +9,5 @@ decisions: # - slack_default # Set the webhook in /etc/crowdsec/notifications/slack.yaml before enabling this. # - splunk_default # Set the splunk url and token in /etc/crowdsec/notifications/splunk.yaml before enabling this. # - http_default # Set the required http parameters in /etc/crowdsec/notifications/http.yaml before enabling this. +# - email_default # Set the required http parameters in /etc/crowdsec/notifications/email.yaml before enabling this. on_success: break diff --git a/plugins/notifications/email/LICENSE b/plugins/notifications/email/LICENSE new file mode 100644 index 000000000..912563863 --- /dev/null +++ b/plugins/notifications/email/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Crowdsec + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/plugins/notifications/email/Makefile b/plugins/notifications/email/Makefile new file mode 100644 index 000000000..b304a88ad --- /dev/null +++ b/plugins/notifications/email/Makefile @@ -0,0 +1,16 @@ +# Go parameters +GOCMD=go +GOBUILD=$(GOCMD) build +GOCLEAN=$(GOCMD) clean +GOTEST=$(GOCMD) test +GOGET=$(GOCMD) get +BINARY_NAME=notification-email + +clean: + @rm -f $(BINARY_NAME) + +build: clean + @$(GOBUILD) $(LD_OPTS) -o $(BINARY_NAME) -v + +static: clean + $(GOBUILD) $(LD_OPTS_STATIC) -o $(BINARY_NAME) -v -a -tags netgo \ No newline at end of file diff --git a/plugins/notifications/email/email.yaml b/plugins/notifications/email/email.yaml new file mode 100644 index 000000000..875a85b89 --- /dev/null +++ b/plugins/notifications/email/email.yaml @@ -0,0 +1,35 @@ +# Don't change this +type: email + +name: email_default # this must match with the registered plugin in the profile +log_level: info # Options include: trace, debug, info, warn, error, off + +format: | # This template receives list of models.Alert objects + {{range . -}} + {{$alert := . -}} + {{range .Decisions -}} + {{.Value}} will get {{.Type}} for next {{.Duration}} for triggering {{.Scenario}}. Shodan + {{end -}} + {{end -}} + +smtp_host: # eg value smtp.gmail.com +smtp_username: #Replace this with your actual username +smtp_password: #Replace this with your actual password +smtp_port: # Common values are any of [25, 465, 587, 2525] +auth_type: # Valid choices are either of "none", "crammd5", "login", "plain" +sender_email: # eg: foo@gmail.com +email_subject: CrowdSec Notification +receiver_emails: + # - email1@gmail.com + # - email2@gmail.com +encryption_type: ssltls # eg valid choices are either "ssltls" or "none" + + + +# group_wait: # duration to wait collecting alerts before sending to this plugin, eg "30s" + +# group_threshold: # if alerts exceed this, then the plugin will be sent the message. eg "10" + +# max_retry: # number of tries to attempt to send message to plugins in case of error. + +timeout: 20s # duration to wait for response from plugin before considering this attempt a failure. eg "10s" diff --git a/plugins/notifications/email/go.mod b/plugins/notifications/email/go.mod new file mode 100644 index 000000000..4732ac50d --- /dev/null +++ b/plugins/notifications/email/go.mod @@ -0,0 +1,11 @@ +module github.com/crowdsecurity/email-plugin + +go 1.16 + +require ( + github.com/crowdsecurity/crowdsec v1.2.0 + github.com/hashicorp/go-hclog v1.0.0 + github.com/hashicorp/go-plugin v1.4.3 + github.com/xhit/go-simple-mail/v2 v2.10.0 + gopkg.in/yaml.v2 v2.4.0 +) diff --git a/plugins/notifications/email/go.sum b/plugins/notifications/email/go.sum new file mode 100644 index 000000000..c8b0c153b --- /dev/null +++ b/plugins/notifications/email/go.sum @@ -0,0 +1,893 @@ +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/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/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +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= +entgo.io/ent v0.7.0/go.mod h1:HZZJxglL8ro4OVDmM06lijj4bOTGcaDdrZttDZ8fVJs= +github.com/AlecAivazis/survey/v2 v2.2.7/go.mod h1:9DYvHgXtiXm6nCn+jXnOXLKbH+Yo9u8fAS/SduGdoPk= +github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +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/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= +github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= +github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= +github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= +github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= +github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= +github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= +github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= +github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= +github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= +github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= +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/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/alexliesenfeld/health v0.5.1/go.mod h1:N4NDIeQtlWumG+6z1ne1v62eQxktz5ylEgGgH9emdMw= +github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= +github.com/antonmedv/expr v1.8.9/go.mod h1:5qsM3oLGDND7sDmQGDXHkYfkjYMUX14qsgqmHhwGEk8= +github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/appleboy/gin-jwt/v2 v2.6.4/go.mod h1:CZpq1cRw+kqi0+yD2CwVw7VGXrrx4AqBdeZnwxVmoAs= +github.com/appleboy/gofight/v2 v2.1.2/go.mod h1:frW+U1QZEdDgixycTj4CygQ48yLTUhplt43+Wczp3rw= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= +github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= +github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= +github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= +github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= +github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go v1.34.28/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/+8rV9s48= +github.com/aws/aws-sdk-go v1.38.34/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= +github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= +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/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= +github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= +github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= +github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= +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/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= +github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= +github.com/containerd/containerd v1.4.3/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/crowdsecurity/crowdsec v1.2.0 h1:tVyE2YczFVqHI+2k8qgnRleVVwIIlwdIo1HsCM5eIi4= +github.com/crowdsecurity/crowdsec v1.2.0/go.mod h1:qPjm7NpE8oE0o1o+trVprKye4U5TapNchlzfGoqw/Ec= +github.com/crowdsecurity/grokky v0.0.0-20210908095311-0b3373925934/go.mod h1:fx5UYUYAFIrOUNAkFCUOM2wJcsp9EWSQE9R0/9kaFJg= +github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/denisbrodbeck/machineid v1.0.1/go.mod h1:dJUwb7PTidGDeYyUBmXZ2GphQBbjJCrnectwCyxcUSI= +github.com/dghubble/sling v1.3.0/go.mod h1:XXShWaBWKzNLhu2OxikSNFrlsvowtz4kyRuXUG7oQKY= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v20.10.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= +github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= +github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= +github.com/enescakir/emoji v1.0.0/go.mod h1:Bt1EKuLnKDTYpLALApstIkAjdDrS/8IAgTkKp+WKFD0= +github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= +github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= +github.com/gdamore/tcell v1.3.0/go.mod h1:Hjvr+Ofd+gLglo7RYKxxnzCBmev3BzsS67MebKS4zMM= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= +github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= +github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= +github.com/go-bindata/go-bindata v1.0.1-0.20190711162640-ee3c2418e368/go.mod h1:7xCgX1lzlrXPHkfvn3EhumqHkmSlzt8at9q7v0ax19c= +github.com/go-co-op/gocron v0.5.1/go.mod h1:6Btk4lVj3bnFAgbVfr76W8impTyhYrEi1pV5Pt4Tp/M= +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-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= +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-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= +github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= +github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= +github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk= +github.com/go-openapi/analysis v0.19.4/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk= +github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU= +github.com/go-openapi/analysis v0.19.10/go.mod h1:qmhS3VNFxBlquFJ0RGoDtylO9y4pgTAUNE9AEEMdlJQ= +github.com/go-openapi/analysis v0.19.16/go.mod h1:GLInF007N83Ad3m8a/CbQ5TPzdnGT7workfHwuVjNVk= +github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= +github.com/go-openapi/errors v0.19.3/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= +github.com/go-openapi/errors v0.19.6/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= +github.com/go-openapi/errors v0.19.7/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= +github.com/go-openapi/errors v0.19.8/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= +github.com/go-openapi/errors v0.19.9/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= +github.com/go-openapi/inflect v0.19.0/go.mod h1:lHpZVlpIQqLyKwJ4N+YSc9hchQy/i12fJykb83CRBH4= +github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= +github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= +github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= +github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs= +github.com/go-openapi/loads v0.19.3/go.mod h1:YVfqhUCdahYwR3f3iiwQLhicVRvLlU/WO5WPaZvcvSI= +github.com/go-openapi/loads v0.19.5/go.mod h1:dswLCAdonkRufe/gSUC3gN8nTSaB9uaS2es0x5/IbjY= +github.com/go-openapi/loads v0.19.6/go.mod h1:brCsvE6j8mnbmGBh103PT/QLHfbyDxA4hsKvYBNEGVc= +github.com/go-openapi/loads v0.19.7/go.mod h1:brCsvE6j8mnbmGBh103PT/QLHfbyDxA4hsKvYBNEGVc= +github.com/go-openapi/loads v0.20.0/go.mod h1:2LhKquiE513rN5xC6Aan6lYOSddlL8Mp20AW9kpviM4= +github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA= +github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64= +github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4= +github.com/go-openapi/runtime v0.19.15/go.mod h1:dhGWCTKRXlAfGnQG0ONViOZpjfg0m2gUt9nTQPQZuoo= +github.com/go-openapi/runtime v0.19.16/go.mod h1:5P9104EJgYcizotuXhEuUrzVc+j1RiSjahULvYmlv98= +github.com/go-openapi/runtime v0.19.24/go.mod h1:Lm9YGCeecBnUUkFTxPC4s1+lwrkJ0pthx8YvyjCfkgk= +github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY= +github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= +github.com/go-openapi/spec v0.19.6/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk= +github.com/go-openapi/spec v0.19.8/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk= +github.com/go-openapi/spec v0.19.15/go.mod h1:+81FIL1JwC5P3/Iuuozq3pPE9dXdIEGxFutcFKaVbmU= +github.com/go-openapi/spec v0.20.0/go.mod h1:+81FIL1JwC5P3/Iuuozq3pPE9dXdIEGxFutcFKaVbmU= +github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= +github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= +github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY= +github.com/go-openapi/strfmt v0.19.2/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU= +github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU= +github.com/go-openapi/strfmt v0.19.4/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk0dgdHXr2Qk= +github.com/go-openapi/strfmt v0.19.5/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk0dgdHXr2Qk= +github.com/go-openapi/strfmt v0.19.11/go.mod h1:UukAYgTaQfqJuAFlNxxMWNvMYiwiXtLsF2VwmoFtbtc= +github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.7/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY= +github.com/go-openapi/swag v0.19.9/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY= +github.com/go-openapi/swag v0.19.12/go.mod h1:eFdyEBkTdoAf/9RXBvj4cr1nH7GD8Kzo5HTt47gr72M= +github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= +github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA= +github.com/go-openapi/validate v0.19.3/go.mod h1:90Vh6jjkTn+OT1Eefm0ZixWNFjhtOH7vS9k0lo6zwJo= +github.com/go-openapi/validate v0.19.10/go.mod h1:RKEZTUWDkxKQxN2jDT7ZnZi2bhZlbNMAuKvKB+IaGx8= +github.com/go-openapi/validate v0.19.12/go.mod h1:Rzou8hA/CBw8donlS6WNEUQupNvUZ0waH08tGe6kAQ4= +github.com/go-openapi/validate v0.19.15/go.mod h1:tbn/fdOwYHgrhPBzidZfJC2MIVvs9GA7monOmWBbeCI= +github.com/go-openapi/validate v0.20.0/go.mod h1:b60iJT+xNNLfaQJUqLI7946tYiFEOuE9E4k54HpKcJ0= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= +github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= +github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-sql-driver/mysql v1.5.1-0.20200311113236-681ffa848bae/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= +github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= +github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg= +github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs= +github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= +github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= +github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk= +github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28= +github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo= +github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk= +github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw= +github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360= +github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg= +github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE= +github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8= +github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= +github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= +github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= +github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= +github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ= +github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0= +github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= +github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/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/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +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/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +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/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +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/goombaio/namegenerator v0.0.0-20181006234301-989e774b106e/go.mod h1:AFIo+02s+12CEg8Gzz9kzhCbmbq6JcKNrhHffCGA9z4= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v1.0.0 h1:bkKf0BeBXcSYa7f5Fyi9gMuQ8gNsxeiNpZjR6VxNZeo= +github.com/hashicorp/go-hclog v1.0.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-plugin v1.4.2/go.mod h1:5fGEH17QVwTTcR0zV7yhDPLLmFX9YSZ38b18Udy6vYQ= +github.com/hashicorp/go-plugin v1.4.3 h1:DXmvivbWD5qdiBts9TpBC7BYL1Aia5sxbRgQB+v6UZM= +github.com/hashicorp/go-plugin v1.4.3/go.mod h1:5fGEH17QVwTTcR0zV7yhDPLLmFX9YSZ38b18Udy6vYQ= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= +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/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb h1:b5rjCoWHc7eqmAS4/qyk21ZsHyb6Mxv/jykxvNTkU4M= +github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= +github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= +github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/influxdata/go-syslog/v3 v3.0.0/go.mod h1:tulsOp+CecTAYC27u9miMgq21GqXRW6VdKbOG+QSP4Q= +github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= +github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +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/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/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/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.4/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= +github.com/leodido/ragel-machinery v0.0.0-20181214104525-299bdde78165/go.mod h1:WZxr2/6a/Ar9bMDc2rN/LJrE/hF6bXE4LPyDSIxwAfg= +github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= +github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= +github.com/lucasb-eyer/go-colorful v1.0.2/go.mod h1:0MS4r+7BZKSJ5mw4/S5MPN+qHFF1fYclkSPilDOKW0s= +github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= +github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= +github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= +github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= +github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= +github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= +github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.8/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= +github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= +github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.4.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= +github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= +github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= +github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= +github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= +github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nxadm/tail v1.4.6/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= +github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= +github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= +github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= +github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= +github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= +github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/oschwald/geoip2-golang v1.4.0/go.mod h1:8QwxJvRImBH+Zl6Aa6MaIcs5YdlZSTKtzmPGzQqi9ng= +github.com/oschwald/maxminddb-golang v1.6.0/go.mod h1:DUJFucBg2cvqx42YmDa/+xHvb0elJtOm3o4aFQ/nb/w= +github.com/oschwald/maxminddb-golang v1.8.0/go.mod h1:RXZtst0N6+FY/3qCNmZMBApR19cdQj43/NM9VkrNAis= +github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo= +github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= +github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= +github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= +github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= +github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= +github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.10.0/go.mod h1:WJM3cc3yu7XKBKa/I8WeZm+V3eltZnBwfENSU7mdogU= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/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/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.18.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/prom2json v1.3.0/go.mod h1:rMN7m0ApCowcoDlypBHlkNbp5eJQf/+1isKykIP5ZnM= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rivo/tview v0.0.0-20200219210816-cd38d7432498/go.mod h1:6lkG1x+13OShEf0EaOCaTQYyB7d5nSbb181KtjlS+84= +github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= +github.com/sanity-io/litter v1.2.0/go.mod h1:JF6pZUFgu2Q0sBZ+HSV35P8TVPI1TTzEwyu9FXAw2W4= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= +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/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= +github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= +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/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/testify v0.0.0-20161117074351-18a02ba4a312/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +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/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/tidwall/gjson v1.6.0/go.mod h1:P256ACg0Mn+j1RXIDXoss50DeIABTYK1PULOJHhxOls= +github.com/tidwall/match v1.0.1/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0+pL9E= +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= +github.com/ugorji/go v1.2.3/go.mod h1:5l8GZ8hZvmL4uMdy+mhCO1LjswGRYco9Q3HfuisB21A= +github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/ugorji/go/codec v1.2.3/go.mod h1:5FxzDJIgeiWJZslYHPj+LS1dq1ZBQVelZFnjsFGI/Uc= +github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= +github.com/vjeantet/grok v1.0.1/go.mod h1:ax1aAchzC6/QMXMcyzHQGZWaW1l195+uMYIkCWPCNIo= +github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= +github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= +github.com/xhit/go-simple-mail/v2 v2.10.0 h1:nib6RaJ4qVh5HD9UE9QJqnUZyWp3upv+Z6CFxaMj0V8= +github.com/xhit/go-simple-mail/v2 v2.10.0/go.mod h1:kA1XbQfCI4JxQ9ccSN6VFyIEkkugOm7YiPkA5hKiQn4= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= +go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.mongodb.org/mongo-driver v1.3.0/go.mod h1:MSWZXKOynuguX+JSvwP8i+58jYCXxbia8HS3gZBapIE= +go.mongodb.org/mongo-driver v1.3.4/go.mod h1:MSWZXKOynuguX+JSvwP8i+58jYCXxbia8HS3gZBapIE= +go.mongodb.org/mongo-driver v1.4.3/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4SoGjYphSc= +go.mongodb.org/mongo-driver v1.4.4/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4SoGjYphSc= +go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +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.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/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-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +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-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/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/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +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-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/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-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/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-20190125091013-d26f9f9a57f3/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-20190320064053-1272bf9dcd53/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-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b h1:iFwSg7t5GZmB/Q5TjiEAsdoLDrdJRC1RiF2WhuV29Qw= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +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/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-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +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-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/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-20181122145206-62eef0e2fa9b/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-20190222072716-a9d3bda3a223/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-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/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-20190530182044-ad28b68e88f1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/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-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191224085550-c709ea063b76/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210503173754-0981d6026fa6 h1:cdsMqa2nXzqlgs183pHxtvoVwU7CyzaCTAUOg94af4c= +golang.org/x/sys v0.0.0-20210503173754-0981d6026fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +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/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +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-20191024005414-555d28b269f0/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-20180828015842-6cd1fcedba52/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-20181030221726-6c7e314b6563/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-20190125232054-d66bd3c5d5a6/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-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190420181800-aa740d480789/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-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190617190820-da514acc4774/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-20190624222133-a101b041ded4/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-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= +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.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +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/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +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-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= +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-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20210114201628-6edceaf6022f h1:izedQ6yVIc5mZsRuXzmSreCOlzI0lCU1HpG8yEdMiKw= +google.golang.org/genproto v0.0.0-20210114201628-6edceaf6022f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.35.0 h1:TwIQcH3es+MojMVojxxfQ3l3OF2KzlRxML2xZq0kRo8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +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/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= +gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637/go.mod h1:BHsqpu/nsuzkT5BpiH1EMZPLyqSMM8JbIavyFACoFNk= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +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.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= +gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= +honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +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= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= diff --git a/plugins/notifications/email/main.go b/plugins/notifications/email/main.go new file mode 100644 index 000000000..43cb818bc --- /dev/null +++ b/plugins/notifications/email/main.go @@ -0,0 +1,122 @@ +package main + +import ( + "context" + "fmt" + "os" + + "github.com/crowdsecurity/crowdsec/pkg/protobufs" + "github.com/hashicorp/go-hclog" + plugin "github.com/hashicorp/go-plugin" + mail "github.com/xhit/go-simple-mail/v2" + "gopkg.in/yaml.v2" +) + +var logger hclog.Logger = hclog.New(&hclog.LoggerOptions{ + Name: "email-plugin", + Level: hclog.LevelFromString("DEBUG"), + Output: os.Stderr, + JSONFormat: true, +}) + +var AuthStringToType map[string]mail.AuthType = map[string]mail.AuthType{ + "none": mail.AuthNone, + "crammd5": mail.AuthCRAMMD5, + "login": mail.AuthLogin, + "plain": mail.AuthPlain, +} + +var EncryptionStringToType map[string]mail.Encryption = map[string]mail.Encryption{ + "ssltls": mail.EncryptionSTARTTLS, + "none": mail.EncryptionNone, +} + +type PluginConfig struct { + Name string `yaml:"name"` + LogLevel *string `yaml:"log_level"` + + SMTPHost string `yaml:"smtp_host"` + SMTPPort int `yaml:"smtp_port"` + SMTPUsername string `yaml:"smtp_username"` + SMTPPassword string `yaml:"smtp_password"` + SenderEmail string `yaml:"sender_email"` + ReceiverEmails []string `yaml:"receiver_emails"` + EmailSubject string `yaml:"email_subject"` + EncryptionType string `yaml:"encryption_type"` + AuthType string `yaml:"auth_type"` +} + +type EmailPlugin struct { + ConfigByName map[string]PluginConfig +} + +func (n *EmailPlugin) Configure(ctx context.Context, config *protobufs.Config) (*protobufs.Empty, error) { + d := PluginConfig{} + if err := yaml.Unmarshal(config.Config, &d); err != nil { + return nil, err + } + n.ConfigByName[d.Name] = d + return &protobufs.Empty{}, nil +} + +func (n *EmailPlugin) Notify(ctx context.Context, notification *protobufs.Notification) (*protobufs.Empty, error) { + if _, ok := n.ConfigByName[notification.Name]; !ok { + return nil, fmt.Errorf("invalid plugin config name %s", notification.Name) + } + cfg := n.ConfigByName[notification.Name] + if cfg.LogLevel != nil && *cfg.LogLevel != "" { + logger.SetLevel(hclog.LevelFromString(*cfg.LogLevel)) + } else { + logger.SetLevel(hclog.Info) + } + logger = logger.Named(cfg.Name) + logger.Debug("got notification") + + server := mail.NewSMTPClient() + server.Host = cfg.SMTPHost + server.Port = cfg.SMTPPort + server.Username = cfg.SMTPUsername + server.Password = cfg.SMTPPassword + server.Encryption = EncryptionStringToType[cfg.EncryptionType] + server.Authentication = AuthStringToType[cfg.AuthType] + + logger.Debug("making smtp connection") + smtpClient, err := server.Connect() + if err != nil { + return &protobufs.Empty{}, err + } + logger.Debug("smtp connection done") + + email := mail.NewMSG() + email.SetFrom(fmt.Sprintf("From <%s>", cfg.SenderEmail)). + AddTo(cfg.ReceiverEmails...). + SetSubject(cfg.EmailSubject) + email.SetBody(mail.TextHTML, notification.Text) + + err = email.Send(smtpClient) + if err != nil { + return &protobufs.Empty{}, err + } else { + logger.Info(fmt.Sprintf("sent email to %v", cfg.ReceiverEmails)) + } + return &protobufs.Empty{}, nil +} + +func main() { + var handshake = plugin.HandshakeConfig{ + ProtocolVersion: 1, + MagicCookieKey: "CROWDSEC_PLUGIN_KEY", + MagicCookieValue: os.Getenv("CROWDSEC_PLUGIN_KEY"), + } + + plugin.Serve(&plugin.ServeConfig{ + HandshakeConfig: handshake, + Plugins: map[string]plugin.Plugin{ + "email": &protobufs.NotifierPlugin{ + Impl: &EmailPlugin{ConfigByName: make(map[string]PluginConfig)}, + }, + }, + GRPCServer: plugin.DefaultGRPCServer, + Logger: logger, + }) +} diff --git a/wizard.sh b/wizard.sh index a858b470c..23d586eae 100755 --- a/wizard.sh +++ b/wizard.sh @@ -68,10 +68,12 @@ smb HTTP_PLUGIN_BINARY="./plugins/notifications/http/notification-http" SLACK_PLUGIN_BINARY="./plugins/notifications/slack/notification-slack" SPLUNK_PLUGIN_BINARY="./plugins/notifications/splunk/notification-splunk" +EMAIL_PLUGIN_BINARY="./plugins/notifications/email/notification-email" HTTP_PLUGIN_CONFIG="./plugins/notifications/http/http.yaml" SLACK_PLUGIN_CONFIG="./plugins/notifications/slack/slack.yaml" SPLUNK_PLUGIN_CONFIG="./plugins/notifications/splunk/splunk.yaml" +EMAIL_PLUGIN_CONFIG="./plugins/notifications/email/email.yaml" BACKUP_DIR=$(mktemp -d) rm -rf $BACKUP_DIR @@ -493,11 +495,13 @@ install_plugins(){ cp ${SLACK_PLUGIN_BINARY} ${CROWDSEC_PLUGIN_DIR} cp ${SPLUNK_PLUGIN_BINARY} ${CROWDSEC_PLUGIN_DIR} cp ${HTTP_PLUGIN_BINARY} ${CROWDSEC_PLUGIN_DIR} + cp ${EMAIL_PLUGIN_BINARY} ${CROWDSEC_PLUGIN_DIR} if [[ ${DOCKER_MODE} == "false" ]]; then cp -n ${SLACK_PLUGIN_CONFIG} /etc/crowdsec/notifications/ cp -n ${SPLUNK_PLUGIN_CONFIG} /etc/crowdsec/notifications/ cp -n ${HTTP_PLUGIN_CONFIG} /etc/crowdsec/notifications/ + cp -n ${EMAIL_PLUGIN_CONFIG} /etc/crowdsec/notifications fi } From c109e0e7dd8dd7a0a1c6f34baacddc09976744e9 Mon Sep 17 00:00:00 2001 From: Shivam Sandbhor Date: Fri, 7 Jan 2022 21:23:56 +0530 Subject: [PATCH 11/14] Add option to print machine creds (#1149) * Add option to print machine creds Signed-off-by: Shivam Sandbhor --- cmd/crowdsec-cli/machines.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/crowdsec-cli/machines.go b/cmd/crowdsec-cli/machines.go index 2d7a9241b..d164af9d6 100644 --- a/cmd/crowdsec-cli/machines.go +++ b/cmd/crowdsec-cli/machines.go @@ -256,7 +256,7 @@ cscli machines add MyTestMachine --password MyPassword if err != nil { log.Fatalf("unable to marshal api credentials: %s", err) } - if dumpFile != "" { + if dumpFile != "" && dumpFile != "-" { err = ioutil.WriteFile(dumpFile, apiConfigDump, 0644) if err != nil { log.Fatalf("write api credentials in '%s' failed: %s", dumpFile, err) From 4a11060930775996ebe1fda90b73fe286e9787ed Mon Sep 17 00:00:00 2001 From: blotus Date: Tue, 11 Jan 2022 14:19:43 +0100 Subject: [PATCH 12/14] Kinesis datasource (#1147) --- .github/workflows/ci_go-test.yml | 8 +- go.mod | 6 +- go.sum | 7 + pkg/acquisition/acquisition.go | 5 + pkg/acquisition/modules/kinesis/kinesis.go | 510 ++++++++++++++++++ .../modules/kinesis/kinesis_test.go | 325 +++++++++++ 6 files changed, 855 insertions(+), 6 deletions(-) create mode 100644 pkg/acquisition/modules/kinesis/kinesis.go create mode 100644 pkg/acquisition/modules/kinesis/kinesis_test.go diff --git a/.github/workflows/ci_go-test.yml b/.github/workflows/ci_go-test.yml index 76bb9f495..0e9dc7922 100644 --- a/.github/workflows/ci_go-test.yml +++ b/.github/workflows/ci_go-test.yml @@ -3,13 +3,14 @@ name: tests #those env variables are for localstack, so we can emulate aws services env: AWS_HOST: localstack - SERVICES: cloudwatch,logs + SERVICES: cloudwatch,logs,kinesis #those are to mimic aws config AWS_ACCESS_KEY_ID: AKIAIOSFODNN7EXAMPLE AWS_SECRET_ACCESS_KEY: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY AWS_REGION: us-east-1 #and to override our endpoint in aws sdk - AWS_ENDPOINT_FORCE: http://localhost:4566 + AWS_ENDPOINT_FORCE: http://localhost:4566 + KINESIS_INITIALIZE_STREAMS: "stream-1-shard:1,stream-2-shards:2" on: push: @@ -32,7 +33,7 @@ jobs: runs-on: ubuntu-latest services: localstack: - image: localstack/localstack:0.12.11 + image: localstack/localstack:0.13.3 ports: - 4566:4566 # Localstack exposes all services on same port env: @@ -43,6 +44,7 @@ jobs: KINESIS_ERROR_PROBABILITY: "" DOCKER_HOST: unix:///var/run/docker.sock HOST_TMP_FOLDER: "/tmp" + KINESIS_INITIALIZE_STREAMS: ${{ env.KINESIS_INITIALIZE_STREAMS }} HOSTNAME_EXTERNAL: ${{ env.AWS_HOST }} # Required so that resource urls are provided properly # e.g sqs url will get localhost if we don't set this env to map our service options: >- diff --git a/go.mod b/go.mod index 6d760a107..f0334a5ea 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/alexliesenfeld/health v0.5.1 github.com/antonmedv/expr v1.8.9 github.com/appleboy/gin-jwt/v2 v2.6.4 - github.com/aws/aws-sdk-go v1.38.34 + github.com/aws/aws-sdk-go v1.42.25 github.com/buger/jsonparser v1.1.1 github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf github.com/crowdsecurity/grokky v0.0.0-20210908095311-0b3373925934 @@ -129,10 +129,10 @@ require ( github.com/vjeantet/grok v1.0.1 // indirect github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect go.mongodb.org/mongo-driver v1.4.4 // indirect - golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 // indirect + golang.org/x/net v0.0.0-20211209124913-491a49abca63 // indirect golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf // indirect - golang.org/x/text v0.3.5 // indirect + golang.org/x/text v0.3.6 // indirect google.golang.org/appengine v1.6.6 // indirect google.golang.org/genproto v0.0.0-20210114201628-6edceaf6022f // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect diff --git a/go.sum b/go.sum index 56b4ed277..b94c5ff3b 100644 --- a/go.sum +++ b/go.sum @@ -80,6 +80,8 @@ github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN github.com/aws/aws-sdk-go v1.34.28/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/+8rV9s48= github.com/aws/aws-sdk-go v1.38.34 h1:JSAyS6hSDLbRmCAz9VAkwDf5oh/olt9mBTrVBWGJcU8= github.com/aws/aws-sdk-go v1.38.34/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= +github.com/aws/aws-sdk-go v1.42.25 h1:BbdvHAi+t9LRiaYUyd53noq9jcaAcfzOhSVbKfr6Avs= +github.com/aws/aws-sdk-go v1.42.25/go.mod h1:gyRszuZ/icHmHAVE4gc/r+cfCmhA1AD+vqfWbgI+eHs= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= 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= @@ -816,6 +818,8 @@ golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20211209124913-491a49abca63 h1:iocB37TsdFuN6IBRZ+ry36wrkoV51/tl5vOWqkcPGvY= +golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 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= @@ -876,6 +880,7 @@ golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210921065528-437939a70204 h1:JJhkWtBuTQKyz2bd5WG9H8iUsJRU3En/KRfN8B2RnDs= @@ -891,6 +896,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 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= diff --git a/pkg/acquisition/acquisition.go b/pkg/acquisition/acquisition.go index 8bd04b86f..648417c5c 100644 --- a/pkg/acquisition/acquisition.go +++ b/pkg/acquisition/acquisition.go @@ -11,6 +11,7 @@ import ( dockeracquisition "github.com/crowdsecurity/crowdsec/pkg/acquisition/modules/docker" fileacquisition "github.com/crowdsecurity/crowdsec/pkg/acquisition/modules/file" journalctlacquisition "github.com/crowdsecurity/crowdsec/pkg/acquisition/modules/journalctl" + kinesisacquisition "github.com/crowdsecurity/crowdsec/pkg/acquisition/modules/kinesis" syslogacquisition "github.com/crowdsecurity/crowdsec/pkg/acquisition/modules/syslog" "github.com/crowdsecurity/crowdsec/pkg/csconfig" "github.com/crowdsecurity/crowdsec/pkg/types" @@ -60,6 +61,10 @@ var AcquisitionSources = []struct { name: "docker", iface: func() DataSource { return &dockeracquisition.DockerSource{} }, }, + { + name: "kinesis", + iface: func() DataSource { return &kinesisacquisition.KinesisSource{} }, + }, } func GetDataSourceIface(dataSourceType string) DataSource { diff --git a/pkg/acquisition/modules/kinesis/kinesis.go b/pkg/acquisition/modules/kinesis/kinesis.go new file mode 100644 index 000000000..1e68f4f1a --- /dev/null +++ b/pkg/acquisition/modules/kinesis/kinesis.go @@ -0,0 +1,510 @@ +package kinesisacquisition + +import ( + "bytes" + "compress/gzip" + "encoding/json" + "fmt" + "io/ioutil" + "strings" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/arn" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/kinesis" + "github.com/crowdsecurity/crowdsec/pkg/acquisition/configuration" + "github.com/crowdsecurity/crowdsec/pkg/leakybucket" + "github.com/crowdsecurity/crowdsec/pkg/types" + "github.com/pkg/errors" + "github.com/prometheus/client_golang/prometheus" + log "github.com/sirupsen/logrus" + "gopkg.in/tomb.v2" + "gopkg.in/yaml.v2" +) + +type KinesisConfiguration struct { + configuration.DataSourceCommonCfg `yaml:",inline"` + StreamName string `yaml:"stream_name"` + StreamARN string `yaml:"stream_arn"` + UseEnhancedFanOut bool `yaml:"use_enhanced_fanout"` //Use RegisterStreamConsumer and SubscribeToShard instead of GetRecords + AwsProfile *string `yaml:"aws_profile"` + AwsRegion string `yaml:"aws_region"` + AwsEndpoint string `yaml:"aws_endpoint"` + ConsumerName string `yaml:"consumer_name"` + FromSubscription bool `yaml:"from_subscription"` + MaxRetries int `yaml:"max_retries"` +} + +type KinesisSource struct { + Config KinesisConfiguration + logger *log.Entry + kClient *kinesis.Kinesis + shardReaderTomb *tomb.Tomb +} + +type CloudWatchSubscriptionRecord struct { + MessageType string `json:"messageType"` + Owner string `json:"owner"` + LogGroup string `json:"logGroup"` + LogStream string `json:"logStream"` + SubscriptionFilters []string `json:"subscriptionFilters"` + LogEvents []CloudwatchSubscriptionLogEvent `json:"logEvents"` +} + +type CloudwatchSubscriptionLogEvent struct { + ID string `json:"id"` + Message string `json:"message"` + Timestamp int64 `json:"timestamp"` +} + +var linesRead = prometheus.NewCounterVec( + prometheus.CounterOpts{ + Name: "cs_kinesis_stream_hits_total", + Help: "Number of event read per stream.", + }, + []string{"stream"}, +) + +var linesReadShards = prometheus.NewCounterVec( + prometheus.CounterOpts{ + Name: "cs_kinesis_shards_hits_total", + Help: "Number of event read per shards.", + }, + []string{"stream", "shard"}, +) + +func (k *KinesisSource) newClient() error { + var sess *session.Session + + if k.Config.AwsProfile != nil { + sess = session.Must(session.NewSessionWithOptions(session.Options{ + SharedConfigState: session.SharedConfigEnable, + Profile: *k.Config.AwsProfile, + })) + } else { + sess = session.Must(session.NewSessionWithOptions(session.Options{ + SharedConfigState: session.SharedConfigEnable, + })) + } + + if sess == nil { + return fmt.Errorf("failed to create aws session") + } + config := aws.NewConfig() + if k.Config.AwsRegion != "" { + config = config.WithRegion(k.Config.AwsRegion) + } + if k.Config.AwsEndpoint != "" { + config = config.WithEndpoint(k.Config.AwsEndpoint) + } + k.kClient = kinesis.New(sess, config) + if k.kClient == nil { + return fmt.Errorf("failed to create kinesis client") + } + return nil +} + +func (k *KinesisSource) GetMetrics() []prometheus.Collector { + return []prometheus.Collector{linesRead, linesReadShards} + +} +func (k *KinesisSource) GetAggregMetrics() []prometheus.Collector { + return []prometheus.Collector{linesRead, linesReadShards} +} + +func (k *KinesisSource) Configure(yamlConfig []byte, logger *log.Entry) error { + config := KinesisConfiguration{} + k.logger = logger + err := yaml.UnmarshalStrict(yamlConfig, &config) + if err != nil { + return errors.Wrap(err, "Cannot parse kinesis datasource configuration") + } + if config.Mode == "" { + config.Mode = configuration.TAIL_MODE + } + k.Config = config + if k.Config.StreamName == "" && !k.Config.UseEnhancedFanOut { + return fmt.Errorf("stream_name is mandatory when use_enhanced_fanout is false") + } + if k.Config.StreamARN == "" && k.Config.UseEnhancedFanOut { + return fmt.Errorf("stream_arn is mandatory when use_enhanced_fanout is true") + } + if k.Config.ConsumerName == "" && k.Config.UseEnhancedFanOut { + return fmt.Errorf("consumer_name is mandatory when use_enhanced_fanout is true") + } + if k.Config.StreamARN != "" && k.Config.StreamName != "" { + return fmt.Errorf("stream_arn and stream_name are mutually exclusive") + } + if k.Config.MaxRetries <= 0 { + k.Config.MaxRetries = 10 + } + err = k.newClient() + if err != nil { + return errors.Wrap(err, "Cannot create kinesis client") + } + k.shardReaderTomb = &tomb.Tomb{} + return nil +} + +func (k *KinesisSource) ConfigureByDSN(string, map[string]string, *log.Entry) error { + return fmt.Errorf("kinesis datasource does not support command-line acquisition") +} + +func (k *KinesisSource) GetMode() string { + return k.Config.Mode +} + +func (k *KinesisSource) GetName() string { + return "kinesis" +} + +func (k *KinesisSource) OneShotAcquisition(out chan types.Event, t *tomb.Tomb) error { + return fmt.Errorf("kinesis datasource does not support one-shot acquisition") +} + +func (k *KinesisSource) decodeFromSubscription(record []byte) ([]CloudwatchSubscriptionLogEvent, error) { + b := bytes.NewBuffer(record) + r, err := gzip.NewReader(b) + + if err != nil { + k.logger.Error(err) + return nil, err + } + decompressed, err := ioutil.ReadAll(r) + if err != nil { + k.logger.Error(err) + return nil, err + } + var subscriptionRecord CloudWatchSubscriptionRecord + err = json.Unmarshal(decompressed, &subscriptionRecord) + if err != nil { + k.logger.Error(err) + return nil, err + } + return subscriptionRecord.LogEvents, nil +} + +func (k *KinesisSource) WaitForConsumerDeregistration(consumerName string, streamARN string) error { + maxTries := k.Config.MaxRetries + for i := 0; i < maxTries; i++ { + _, err := k.kClient.DescribeStreamConsumer(&kinesis.DescribeStreamConsumerInput{ + ConsumerName: aws.String(consumerName), + StreamARN: aws.String(streamARN), + }) + if err != nil { + switch err.(type) { + case *kinesis.ResourceNotFoundException: + return nil + default: + k.logger.Errorf("Error while waiting for consumer deregistration: %s", err) + return errors.Wrap(err, "Cannot describe stream consumer") + } + } + time.Sleep(time.Millisecond * 200 * time.Duration(i+1)) + } + return fmt.Errorf("consumer %s is not deregistered after %d tries", consumerName, maxTries) +} + +func (k *KinesisSource) DeregisterConsumer() error { + k.logger.Debugf("Deregistering consumer %s if it exists", k.Config.ConsumerName) + _, err := k.kClient.DeregisterStreamConsumer(&kinesis.DeregisterStreamConsumerInput{ + ConsumerName: aws.String(k.Config.ConsumerName), + StreamARN: aws.String(k.Config.StreamARN), + }) + if err != nil { + switch err.(type) { + case *kinesis.ResourceNotFoundException: + default: + return errors.Wrap(err, "Cannot deregister stream consumer") + } + } + err = k.WaitForConsumerDeregistration(k.Config.ConsumerName, k.Config.StreamARN) + if err != nil { + return errors.Wrap(err, "Cannot wait for consumer deregistration") + } + return nil +} + +func (k *KinesisSource) WaitForConsumerRegistration(consumerARN string) error { + maxTries := k.Config.MaxRetries + for i := 0; i < maxTries; i++ { + describeOutput, err := k.kClient.DescribeStreamConsumer(&kinesis.DescribeStreamConsumerInput{ + ConsumerARN: aws.String(consumerARN), + }) + if err != nil { + return errors.Wrap(err, "Cannot describe stream consumer") + } + if *describeOutput.ConsumerDescription.ConsumerStatus == "ACTIVE" { + k.logger.Debugf("Consumer %s is active", consumerARN) + return nil + } + time.Sleep(time.Millisecond * 200 * time.Duration(i+1)) + k.logger.Debugf("Waiting for consumer registration %d", i) + } + return fmt.Errorf("consumer %s is not active after %d tries", consumerARN, maxTries) +} + +func (k *KinesisSource) RegisterConsumer() (*kinesis.RegisterStreamConsumerOutput, error) { + k.logger.Debugf("Registering consumer %s", k.Config.ConsumerName) + streamConsumer, err := k.kClient.RegisterStreamConsumer(&kinesis.RegisterStreamConsumerInput{ + ConsumerName: aws.String(k.Config.ConsumerName), + StreamARN: aws.String(k.Config.StreamARN), + }) + if err != nil { + return nil, errors.Wrap(err, "Cannot register stream consumer") + } + err = k.WaitForConsumerRegistration(*streamConsumer.Consumer.ConsumerARN) + if err != nil { + return nil, errors.Wrap(err, "Timeout while waiting for consumer to be active") + } + return streamConsumer, nil +} + +func (k *KinesisSource) ParseAndPushRecords(records []*kinesis.Record, out chan types.Event, logger *log.Entry, shardId string) { + for _, record := range records { + if k.Config.StreamARN != "" { + linesReadShards.With(prometheus.Labels{"stream": k.Config.StreamARN, "shard": shardId}).Inc() + linesRead.With(prometheus.Labels{"stream": k.Config.StreamARN}).Inc() + } else { + linesReadShards.With(prometheus.Labels{"stream": k.Config.StreamName, "shard": shardId}).Inc() + linesRead.With(prometheus.Labels{"stream": k.Config.StreamName}).Inc() + } + var data []CloudwatchSubscriptionLogEvent + var err error + if k.Config.FromSubscription { + //The AWS docs says that the data is base64 encoded + //but apparently GetRecords decodes it for us ? + data, err = k.decodeFromSubscription(record.Data) + if err != nil { + logger.Errorf("Cannot decode data: %s", err) + continue + } + } else { + data = []CloudwatchSubscriptionLogEvent{{Message: string(record.Data)}} + } + for _, event := range data { + logger.Tracef("got record %s", event.Message) + l := types.Line{} + l.Raw = event.Message + l.Labels = k.Config.Labels + l.Time = time.Now() + l.Process = true + l.Module = k.GetName() + if k.Config.StreamARN != "" { + l.Src = k.Config.StreamARN + } else { + l.Src = k.Config.StreamName + } + evt := types.Event{Line: l, Process: true, Type: types.LOG, ExpectMode: leakybucket.LIVE} + out <- evt + } + } +} + +func (k *KinesisSource) ReadFromSubscription(reader kinesis.SubscribeToShardEventStreamReader, out chan types.Event, shardId string, streamName string) error { + logger := k.logger.WithFields(log.Fields{"shard_id": shardId}) + //ghetto sync, kinesis allows to subscribe to a closed shard, which will make the goroutine exit immediately + //and we won't be able to start a new one if this is the first one started by the tomb + //TODO: look into parent shards to see if a shard is closed before starting to read it ? + time.Sleep(time.Second) + for { + select { + case <-k.shardReaderTomb.Dying(): + logger.Infof("Subscribed shard reader is dying") + err := reader.Close() + if err != nil { + return errors.Wrap(err, "Cannot close kinesis subscribed shard reader") + } + return nil + case event, ok := <-reader.Events(): + if !ok { + logger.Infof("Event chan has been closed") + return nil + } + switch event := event.(type) { + case *kinesis.SubscribeToShardEvent: + k.ParseAndPushRecords(event.Records, out, logger, shardId) + case *kinesis.SubscribeToShardEventStreamUnknownEvent: + logger.Infof("got an unknown event, what to do ?") + } + } + } +} + +func (k *KinesisSource) SubscribeToShards(arn arn.ARN, streamConsumer *kinesis.RegisterStreamConsumerOutput, out chan types.Event) error { + shards, err := k.kClient.ListShards(&kinesis.ListShardsInput{ + StreamName: aws.String(arn.Resource[7:]), + }) + if err != nil { + return errors.Wrap(err, "Cannot list shards for enhanced_read") + } + + for _, shard := range shards.Shards { + shardId := *shard.ShardId + r, err := k.kClient.SubscribeToShard(&kinesis.SubscribeToShardInput{ + ShardId: aws.String(shardId), + StartingPosition: &kinesis.StartingPosition{Type: aws.String(kinesis.ShardIteratorTypeLatest)}, + ConsumerARN: streamConsumer.Consumer.ConsumerARN, + }) + if err != nil { + return errors.Wrap(err, "Cannot subscribe to shard") + } + k.shardReaderTomb.Go(func() error { + return k.ReadFromSubscription(r.GetEventStream().Reader, out, shardId, arn.Resource[7:]) + }) + } + return nil +} + +func (k *KinesisSource) EnhancedRead(out chan types.Event, t *tomb.Tomb) error { + parsedARN, err := arn.Parse(k.Config.StreamARN) + if err != nil { + return errors.Wrap(err, "Cannot parse stream ARN") + } + if !strings.HasPrefix(parsedARN.Resource, "stream/") { + return fmt.Errorf("resource part of stream ARN %s does not start with stream/", k.Config.StreamARN) + } + + k.logger = k.logger.WithFields(log.Fields{"stream": parsedARN.Resource[7:]}) + k.logger.Info("starting kinesis acquisition with enhanced fan-out") + err = k.DeregisterConsumer() + if err != nil { + return errors.Wrap(err, "Cannot deregister consumer") + } + + streamConsumer, err := k.RegisterConsumer() + if err != nil { + return errors.Wrap(err, "Cannot register consumer") + } + + for { + k.shardReaderTomb = &tomb.Tomb{} + + err = k.SubscribeToShards(parsedARN, streamConsumer, out) + if err != nil { + return errors.Wrap(err, "Cannot subscribe to shards") + } + select { + case <-t.Dying(): + k.logger.Infof("Kinesis source is dying") + k.shardReaderTomb.Kill(nil) + _ = k.shardReaderTomb.Wait() //we don't care about the error as we kill the tomb ourselves + err = k.DeregisterConsumer() + if err != nil { + return errors.Wrap(err, "Cannot deregister consumer") + } + return nil + case <-k.shardReaderTomb.Dying(): + k.logger.Debugf("Kinesis subscribed shard reader is dying") + if k.shardReaderTomb.Err() != nil { + return k.shardReaderTomb.Err() + } + //All goroutines have exited without error, so a resharding event, start again + k.logger.Debugf("All reader goroutines have exited, resharding event or periodic resubscribe") + continue + } + } +} + +func (k *KinesisSource) ReadFromShard(out chan types.Event, shardId string) error { + logger := k.logger.WithFields(log.Fields{"shard": shardId}) + logger.Debugf("Starting to read shard") + sharIt, err := k.kClient.GetShardIterator(&kinesis.GetShardIteratorInput{ShardId: aws.String(shardId), + StreamName: &k.Config.StreamName, + ShardIteratorType: aws.String(kinesis.ShardIteratorTypeLatest)}) + if err != nil { + logger.Errorf("Cannot get shard iterator: %s", err) + return errors.Wrap(err, "Cannot get shard iterator") + } + it := sharIt.ShardIterator + //AWS recommends to wait for a second between calls to GetRecords for a given shard + ticker := time.NewTicker(time.Second) + for { + select { + case <-ticker.C: + records, err := k.kClient.GetRecords(&kinesis.GetRecordsInput{ShardIterator: it}) + it = records.NextShardIterator + if err != nil { + switch err.(type) { + case *kinesis.ProvisionedThroughputExceededException: + logger.Warn("Provisioned throughput exceeded") + //TODO: implement exponential backoff + continue + case *kinesis.ExpiredIteratorException: + logger.Warn("Expired iterator") + continue + default: + logger.Error("Cannot get records") + return errors.Wrap(err, "Cannot get records") + } + } + k.ParseAndPushRecords(records.Records, out, logger, shardId) + + if it == nil { + logger.Warnf("Shard has been closed") + return nil + } + case <-k.shardReaderTomb.Dying(): + logger.Infof("shardReaderTomb is dying, exiting ReadFromShard") + ticker.Stop() + return nil + } + } +} + +func (k *KinesisSource) ReadFromStream(out chan types.Event, t *tomb.Tomb) error { + k.logger = k.logger.WithFields(log.Fields{"stream": k.Config.StreamName}) + k.logger.Info("starting kinesis acquisition from shards") + for { + shards, err := k.kClient.ListShards(&kinesis.ListShardsInput{ + StreamName: aws.String(k.Config.StreamName), + }) + if err != nil { + return errors.Wrap(err, "Cannot list shards") + } + k.shardReaderTomb = &tomb.Tomb{} + for _, shard := range shards.Shards { + shardId := *shard.ShardId + k.shardReaderTomb.Go(func() error { + defer types.CatchPanic("crowdsec/acquis/kinesis/streaming/shard") + return k.ReadFromShard(out, shardId) + }) + } + select { + case <-t.Dying(): + k.logger.Info("kinesis source is dying") + k.shardReaderTomb.Kill(nil) + _ = k.shardReaderTomb.Wait() //we don't care about the error as we kill the tomb ourselves + return nil + case <-k.shardReaderTomb.Dying(): + reason := k.shardReaderTomb.Err() + if reason != nil { + k.logger.Errorf("Unexpected error from shard reader : %s", reason) + return reason + } + k.logger.Infof("All shards have been closed, probably a resharding event, restarting acquisition") + continue + } + } +} + +func (k *KinesisSource) StreamingAcquisition(out chan types.Event, t *tomb.Tomb) error { + t.Go(func() error { + defer types.CatchPanic("crowdsec/acquis/kinesis/streaming") + if k.Config.UseEnhancedFanOut { + return k.EnhancedRead(out, t) + } else { + return k.ReadFromStream(out, t) + } + }) + return nil +} + +func (k *KinesisSource) CanRun() error { + return nil +} + +func (k *KinesisSource) Dump() interface{} { + return k +} diff --git a/pkg/acquisition/modules/kinesis/kinesis_test.go b/pkg/acquisition/modules/kinesis/kinesis_test.go new file mode 100644 index 000000000..c17e9a9ca --- /dev/null +++ b/pkg/acquisition/modules/kinesis/kinesis_test.go @@ -0,0 +1,325 @@ +package kinesisacquisition + +import ( + "bytes" + "compress/gzip" + "encoding/json" + "fmt" + "net" + "os" + "strings" + "testing" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/kinesis" + "github.com/crowdsecurity/crowdsec/pkg/types" + log "github.com/sirupsen/logrus" + "github.com/stretchr/testify/assert" + "gopkg.in/tomb.v2" +) + +func getLocalStackEndpoint() (string, error) { + endpoint := "http://localhost:4566" + if v := os.Getenv("AWS_ENDPOINT_FORCE"); v != "" { + v = strings.TrimPrefix(v, "http://") + _, err := net.Dial("tcp", v) + if err != nil { + return "", fmt.Errorf("while dialing %s : %s : aws endpoint isn't available", v, err) + } + } + return endpoint, nil +} + +func GenSubObject(i int) []byte { + r := CloudWatchSubscriptionRecord{ + MessageType: "subscription", + Owner: "test", + LogGroup: "test", + LogStream: "test", + SubscriptionFilters: []string{"filter1"}, + LogEvents: []CloudwatchSubscriptionLogEvent{ + { + ID: "testid", + Message: fmt.Sprintf("%d", i), + Timestamp: time.Now().Unix(), + }, + }, + } + body, err := json.Marshal(r) + if err != nil { + log.Fatal(err) + } + var b bytes.Buffer + gz := gzip.NewWriter(&b) + gz.Write(body) + gz.Close() + //AWS actually base64 encodes the data, but it looks like kinesis automatically decodes it at some point + //localstack does not do it, so let's just write a raw gzipped stream + return b.Bytes() +} + +func WriteToStream(streamName string, count int, shards int, sub bool) { + endpoint, err := getLocalStackEndpoint() + if err != nil { + log.Fatal(err) + } + sess := session.Must(session.NewSession()) + kinesisClient := kinesis.New(sess, aws.NewConfig().WithEndpoint(endpoint).WithRegion("us-east-1")) + for i := 0; i < count; i++ { + partition := "partition" + if shards != 1 { + partition = fmt.Sprintf("partition-%d", i%shards) + } + var data []byte + if sub { + data = GenSubObject(i) + } else { + data = []byte(fmt.Sprintf("%d", i)) + } + _, err = kinesisClient.PutRecord(&kinesis.PutRecordInput{ + Data: data, + PartitionKey: aws.String(partition), + StreamName: aws.String(streamName), + }) + if err != nil { + fmt.Printf("Error writing to stream: %s\n", err) + log.Fatal(err) + } + } +} + +func TestMain(m *testing.M) { + os.Setenv("AWS_ACCESS_KEY_ID", "foobar") + os.Setenv("AWS_SECRET_ACCESS_KEY", "foobar") + + //delete_streams() + //create_streams() + code := m.Run() + //delete_streams() + os.Exit(code) +} + +func TestBadConfiguration(t *testing.T) { + tests := []struct { + config string + expectedErr string + }{ + { + config: `source: kinesis`, + expectedErr: "stream_name is mandatory when use_enhanced_fanout is false", + }, + { + config: ` +source: kinesis +use_enhanced_fanout: true`, + expectedErr: "stream_arn is mandatory when use_enhanced_fanout is true", + }, + { + config: ` +source: kinesis +use_enhanced_fanout: true +stream_arn: arn:aws:kinesis:eu-west-1:123456789012:stream/my-stream`, + expectedErr: "consumer_name is mandatory when use_enhanced_fanout is true", + }, + { + config: ` +source: kinesis +stream_name: foobar +stream_arn: arn:aws:kinesis:eu-west-1:123456789012:stream/my-stream`, + expectedErr: "stream_arn and stream_name are mutually exclusive", + }, + } + + subLogger := log.WithFields(log.Fields{ + "type": "kinesis", + }) + for _, test := range tests { + f := KinesisSource{} + err := f.Configure([]byte(test.config), subLogger) + if test.expectedErr != "" && err == nil { + t.Fatalf("Expected err %s but got nil !", test.expectedErr) + } + if test.expectedErr != "" { + assert.Contains(t, err.Error(), test.expectedErr) + } + } +} + +func TestReadFromStream(t *testing.T) { + tests := []struct { + config string + count int + shards int + }{ + { + config: `source: kinesis +aws_endpoint: %s +aws_region: us-east-1 +stream_name: stream-1-shard`, + count: 10, + shards: 1, + }, + } + endpoint, _ := getLocalStackEndpoint() + for _, test := range tests { + f := KinesisSource{} + config := fmt.Sprintf(test.config, endpoint) + err := f.Configure([]byte(config), log.WithFields(log.Fields{ + "type": "kinesis", + })) + if err != nil { + t.Fatalf("Error configuring source: %s", err) + } + tomb := &tomb.Tomb{} + out := make(chan types.Event) + err = f.StreamingAcquisition(out, tomb) + if err != nil { + t.Fatalf("Error starting source: %s", err) + } + //Allow the datasource to start listening to the stream + time.Sleep(4 * time.Second) + WriteToStream(f.Config.StreamName, test.count, test.shards, false) + for i := 0; i < test.count; i++ { + e := <-out + assert.Equal(t, fmt.Sprintf("%d", i), e.Line.Raw) + } + tomb.Kill(nil) + tomb.Wait() + } +} + +func TestReadFromMultipleShards(t *testing.T) { + tests := []struct { + config string + count int + shards int + }{ + { + config: `source: kinesis +aws_endpoint: %s +aws_region: us-east-1 +stream_name: stream-2-shards`, + count: 10, + shards: 2, + }, + } + endpoint, _ := getLocalStackEndpoint() + for _, test := range tests { + f := KinesisSource{} + config := fmt.Sprintf(test.config, endpoint) + err := f.Configure([]byte(config), log.WithFields(log.Fields{ + "type": "kinesis", + })) + if err != nil { + t.Fatalf("Error configuring source: %s", err) + } + tomb := &tomb.Tomb{} + out := make(chan types.Event) + err = f.StreamingAcquisition(out, tomb) + if err != nil { + t.Fatalf("Error starting source: %s", err) + } + //Allow the datasource to start listening to the stream + time.Sleep(4 * time.Second) + WriteToStream(f.Config.StreamName, test.count, test.shards, false) + c := 0 + for i := 0; i < test.count; i++ { + <-out + c += 1 + } + assert.Equal(t, test.count, c) + tomb.Kill(nil) + tomb.Wait() + } +} + +func TestFromSubscription(t *testing.T) { + tests := []struct { + config string + count int + shards int + }{ + { + config: `source: kinesis +aws_endpoint: %s +aws_region: us-east-1 +stream_name: stream-1-shard +from_subscription: true`, + count: 10, + shards: 1, + }, + } + endpoint, _ := getLocalStackEndpoint() + for _, test := range tests { + f := KinesisSource{} + config := fmt.Sprintf(test.config, endpoint) + err := f.Configure([]byte(config), log.WithFields(log.Fields{ + "type": "kinesis", + })) + if err != nil { + t.Fatalf("Error configuring source: %s", err) + } + tomb := &tomb.Tomb{} + out := make(chan types.Event) + err = f.StreamingAcquisition(out, tomb) + if err != nil { + t.Fatalf("Error starting source: %s", err) + } + //Allow the datasource to start listening to the stream + time.Sleep(4 * time.Second) + WriteToStream(f.Config.StreamName, test.count, test.shards, true) + for i := 0; i < test.count; i++ { + e := <-out + assert.Equal(t, fmt.Sprintf("%d", i), e.Line.Raw) + } + tomb.Kill(nil) + tomb.Wait() + } +} + +/* +func TestSubscribeToStream(t *testing.T) { + tests := []struct { + config string + count int + shards int + }{ + { + config: `source: kinesis +aws_endpoint: %s +aws_region: us-east-1 +stream_arn: arn:aws:kinesis:us-east-1:000000000000:stream/stream-1-shard +consumer_name: consumer-1 +use_enhanced_fanout: true`, + count: 10, + shards: 1, + }, + } + endpoint, _ := getLocalStackEndpoint() + for _, test := range tests { + f := KinesisSource{} + config := fmt.Sprintf(test.config, endpoint) + err := f.Configure([]byte(config), log.WithFields(log.Fields{ + "type": "kinesis", + })) + if err != nil { + t.Fatalf("Error configuring source: %s", err) + } + tomb := &tomb.Tomb{} + out := make(chan types.Event) + err = f.StreamingAcquisition(out, tomb) + if err != nil { + t.Fatalf("Error starting source: %s", err) + } + //Allow the datasource to start listening to the stream + time.Sleep(10 * time.Second) + WriteToStream("stream-1-shard", test.count, test.shards) + for i := 0; i < test.count; i++ { + e := <-out + assert.Equal(t, fmt.Sprintf("%d", i), e.Line.Raw) + } + } +} +*/ From 3bca25fd6de0371843671988bf50c8e78a64bc09 Mon Sep 17 00:00:00 2001 From: "Thibault \"bui\" Koechlin" Date: Tue, 11 Jan 2022 14:31:51 +0100 Subject: [PATCH 13/14] lists support from central api (#1074) * lists support from central api Co-authored-by: Sebastien Blot --- cmd/crowdsec-cli/decisions.go | 6 ++ cmd/crowdsec-cli/utils.go | 1 - pkg/apiclient/alerts_service.go | 2 + pkg/apiserver/apic.go | 157 +++++++++++++++++++++++++++----- pkg/database/alerts.go | 10 +- 5 files changed, 148 insertions(+), 28 deletions(-) diff --git a/cmd/crowdsec-cli/decisions.go b/cmd/crowdsec-cli/decisions.go index b06879f15..b2fb4f9a6 100644 --- a/cmd/crowdsec-cli/decisions.go +++ b/cmd/crowdsec-cli/decisions.go @@ -161,6 +161,7 @@ func NewDecisionsCmd() *cobra.Command { ValueEquals: new(string), ScopeEquals: new(string), ScenarioEquals: new(string), + OriginEquals: new(string), IPEquals: new(string), RangeEquals: new(string), Since: new(string), @@ -243,6 +244,10 @@ cscli decisions list -t ban filter.RangeEquals = nil } + if *filter.OriginEquals == "" { + filter.OriginEquals = nil + } + if contained != nil && *contained { filter.Contains = new(bool) } @@ -264,6 +269,7 @@ cscli decisions list -t ban cmdDecisionsList.Flags().StringVar(filter.Until, "until", "", "restrict to alerts older than until (ie. 4h, 30d)") cmdDecisionsList.Flags().StringVarP(filter.TypeEquals, "type", "t", "", "restrict to this decision type (ie. ban,captcha)") cmdDecisionsList.Flags().StringVar(filter.ScopeEquals, "scope", "", "restrict to this scope (ie. ip,range,session)") + cmdDecisionsList.Flags().StringVar(filter.OriginEquals, "origin", "", "restrict to this origin (ie. lists,CAPI,cscli)") cmdDecisionsList.Flags().StringVarP(filter.ValueEquals, "value", "v", "", "restrict to this value (ie. 1.2.3.4,userName)") cmdDecisionsList.Flags().StringVarP(filter.ScenarioEquals, "scenario", "s", "", "restrict to this scenario (ie. crowdsecurity/ssh-bf)") cmdDecisionsList.Flags().StringVarP(filter.IPEquals, "ip", "i", "", "restrict to alerts from this source ip (shorthand for --scope ip --value )") diff --git a/cmd/crowdsec-cli/utils.go b/cmd/crowdsec-cli/utils.go index 8fa387bf8..fe3065ffd 100644 --- a/cmd/crowdsec-cli/utils.go +++ b/cmd/crowdsec-cli/utils.go @@ -68,7 +68,6 @@ func manageCliDecisionAlerts(ip *string, ipRange *string, scope *string, value * *scope = types.Country case "as": *scope = types.AS - } return nil } diff --git a/pkg/apiclient/alerts_service.go b/pkg/apiclient/alerts_service.go index bdbf6c34f..5ff1d1f2f 100644 --- a/pkg/apiclient/alerts_service.go +++ b/pkg/apiclient/alerts_service.go @@ -19,6 +19,7 @@ type AlertsListOpts struct { ScenarioEquals *string `url:"scenario,omitempty"` IPEquals *string `url:"ip,omitempty"` RangeEquals *string `url:"range,omitempty"` + OriginEquals *string `url:"origin,omitempty"` Since *string `url:"since,omitempty"` TypeEquals *string `url:"decision_type,omitempty"` Until *string `url:"until,omitempty"` @@ -38,6 +39,7 @@ type AlertsDeleteOpts struct { RangeEquals *string `url:"range,omitempty"` Since *string `url:"since,omitempty"` Until *string `url:"until,omitempty"` + OriginEquals *string `url:"origin,omitempty"` ActiveDecisionEquals *bool `url:"has_active_decision,omitempty"` SourceEquals *string `url:"alert_source,omitempty"` Contains *bool `url:"contains,omitempty"` diff --git a/pkg/apiserver/apic.go b/pkg/apiserver/apic.go index ac59d3128..715bf3e14 100644 --- a/pkg/apiserver/apic.go +++ b/pkg/apiserver/apic.go @@ -234,6 +234,10 @@ func (a *apic) Send(cacheOrig *models.AddSignalsRequest) { } } +var SCOPE_CAPI string = "CAPI" +var SCOPE_CAPI_ALIAS string = "crowdsecurity/community-blocklist" //we don't use "CAPI" directly, to make it less confusing for the user +var SCOPE_LISTS string = "lists" + func (a *apic) PullTop() error { var err error @@ -256,10 +260,28 @@ func (a *apic) PullTop() error { if a.startup { a.startup = false } - // process deleted decisions + /*to count additions/deletions accross lists*/ + var add_counters map[string]map[string]int + var delete_counters map[string]map[string]int + + add_counters = make(map[string]map[string]int) + add_counters[SCOPE_CAPI] = make(map[string]int) + add_counters[SCOPE_LISTS] = make(map[string]int) + delete_counters = make(map[string]map[string]int) + delete_counters[SCOPE_CAPI] = make(map[string]int) + delete_counters[SCOPE_LISTS] = make(map[string]int) var filter map[string][]string var nbDeleted int + // process deleted decisions for _, decision := range data.Deleted { + //count individual deletions + if *decision.Origin == SCOPE_CAPI { + delete_counters[SCOPE_CAPI][*decision.Scenario]++ + } else if *decision.Origin == SCOPE_LISTS { + delete_counters[SCOPE_LISTS][*decision.Scenario]++ + } else { + log.Warningf("Unknown origin %s", *decision.Origin) + } if strings.ToLower(*decision.Scope) == "ip" { filter = make(map[string][]string, 1) filter["value"] = []string{*decision.Value} @@ -287,23 +309,77 @@ func (a *apic) PullTop() error { return nil } - capiPullTopX := models.Alert{} - capiPullTopX.Scenario = types.StrPtr(fmt.Sprintf("update : +%d/-%d IPs", len(data.New), len(data.Deleted))) - capiPullTopX.Message = types.StrPtr("") - capiPullTopX.Source = &models.Source{} - capiPullTopX.Source.Scope = types.StrPtr("crowdsec/community-blocklist") - capiPullTopX.Source.Value = types.StrPtr("") - capiPullTopX.StartAt = types.StrPtr(time.Now().Format(time.RFC3339)) - capiPullTopX.StopAt = types.StrPtr(time.Now().Format(time.RFC3339)) - capiPullTopX.Capacity = types.Int32Ptr(0) - capiPullTopX.Simulated = types.BoolPtr(false) - capiPullTopX.EventsCount = types.Int32Ptr(int32(len(data.New))) - capiPullTopX.Leakspeed = types.StrPtr("") - capiPullTopX.ScenarioHash = types.StrPtr("") - capiPullTopX.ScenarioVersion = types.StrPtr("") - capiPullTopX.MachineID = database.CapiMachineID - // process new decisions + //we receive only one list of decisions, that we need to break-up : + // one alert for "community blocklist" + // one alert per list we're subscribed to + var alertsFromCapi []*models.Alert + alertsFromCapi = make([]*models.Alert, 0) + + //iterate over all new decisions, and simply create corresponding alerts for _, decision := range data.New { + found := false + for _, sub := range alertsFromCapi { + if sub.Source.Scope == nil { + log.Warningf("nil scope in %+v", sub) + continue + } + if *decision.Origin == SCOPE_CAPI { + if *sub.Source.Scope == SCOPE_CAPI { + found = true + break + } + } else if *decision.Origin == SCOPE_LISTS { + if *sub.Source.Scope == *decision.Origin { + if sub.Scenario == nil { + log.Warningf("nil scenario in %+v", sub) + } + if *sub.Scenario == *decision.Scenario { + found = true + break + } + } + } else { + log.Warningf("unknown origin %s : %+v", *decision.Origin, decision) + } + } + if !found { + log.Debugf("Create entry for origin:%s scenario:%s", *decision.Origin, *decision.Scenario) + newAlert := models.Alert{} + newAlert.Message = types.StrPtr("") + newAlert.Source = &models.Source{} + if *decision.Origin == SCOPE_CAPI { //to make things more user friendly, we replace CAPI with community-blocklist + newAlert.Source.Scope = types.StrPtr(SCOPE_CAPI) + newAlert.Scenario = types.StrPtr(SCOPE_CAPI) + } else if *decision.Origin == SCOPE_LISTS { + newAlert.Source.Scope = types.StrPtr(SCOPE_LISTS) + newAlert.Scenario = types.StrPtr(*decision.Scenario) + } else { + log.Warningf("unknown origin %s", *decision.Origin) + } + newAlert.Source.Value = types.StrPtr("") + newAlert.StartAt = types.StrPtr(time.Now().Format(time.RFC3339)) + newAlert.StopAt = types.StrPtr(time.Now().Format(time.RFC3339)) + newAlert.Capacity = types.Int32Ptr(0) + newAlert.Simulated = types.BoolPtr(false) + newAlert.EventsCount = types.Int32Ptr(int32(len(data.New))) + newAlert.Leakspeed = types.StrPtr("") + newAlert.ScenarioHash = types.StrPtr("") + newAlert.ScenarioVersion = types.StrPtr("") + newAlert.MachineID = database.CapiMachineID + alertsFromCapi = append(alertsFromCapi, &newAlert) + } + } + + //iterate a second time and fill the alerts with the new decisions + for _, decision := range data.New { + //count and create separate alerts for each list + if *decision.Origin == SCOPE_CAPI { + add_counters[SCOPE_CAPI]["all"]++ + } else if *decision.Origin == SCOPE_LISTS { + add_counters[SCOPE_LISTS][*decision.Scenario]++ + } else { + log.Warningf("Unknown origin %s", *decision.Origin) + } /*CAPI might send lower case scopes, unify it.*/ switch strings.ToLower(*decision.Scope) { @@ -312,17 +388,48 @@ func (a *apic) PullTop() error { case "range": *decision.Scope = types.Range } - - capiPullTopX.Decisions = append(capiPullTopX.Decisions, decision) + found := false + //add the individual decisions to the right list + for idx, alert := range alertsFromCapi { + if *decision.Origin == SCOPE_CAPI { + if *alert.Source.Scope == SCOPE_CAPI { + alertsFromCapi[idx].Decisions = append(alertsFromCapi[idx].Decisions, decision) + found = true + break + } + } else if *decision.Origin == SCOPE_LISTS { + if *alert.Source.Scope == SCOPE_LISTS && *alert.Scenario == *decision.Scenario { + alertsFromCapi[idx].Decisions = append(alertsFromCapi[idx].Decisions, decision) + found = true + break + } + } else { + log.Warningf("unknown origin %s", *decision.Origin) + } + } + if !found { + log.Warningf("Orphaned decision for %s - %s", *decision.Origin, *decision.Scenario) + } } - alertID, inserted, deleted, err := a.dbClient.UpdateCommunityBlocklist(&capiPullTopX) - if err != nil { - return errors.Wrap(err, "while saving alert from capi/community-blocklist") + for idx, alert := range alertsFromCapi { + formatted_update := "" + + if *alertsFromCapi[idx].Source.Scope == SCOPE_CAPI { + *alertsFromCapi[idx].Source.Scope = SCOPE_CAPI_ALIAS + formatted_update = fmt.Sprintf("update : +%d/-%d IPs", add_counters[SCOPE_CAPI]["all"], delete_counters[SCOPE_CAPI]["all"]) + } else if *alertsFromCapi[idx].Source.Scope == SCOPE_LISTS { + *alertsFromCapi[idx].Source.Scope = fmt.Sprintf("%s:%s", SCOPE_LISTS, *alertsFromCapi[idx].Scenario) + formatted_update = fmt.Sprintf("update : +%d/-%d IPs", add_counters[SCOPE_LISTS][*alert.Scenario], delete_counters[SCOPE_LISTS][*alert.Scenario]) + } + alertsFromCapi[idx].Scenario = types.StrPtr(formatted_update) + log.Debugf("%s has %d decisions", *alertsFromCapi[idx].Source.Scope, len(alertsFromCapi[idx].Decisions)) + alertID, inserted, deleted, err := a.dbClient.UpdateCommunityBlocklist(alertsFromCapi[idx]) + if err != nil { + return errors.Wrapf(err, "while saving alert from %s", *alertsFromCapi[idx].Source.Scope) + } + log.Printf("%s : added %d entries, deleted %d entries (alert:%d)", *alertsFromCapi[idx].Source.Scope, inserted, deleted, alertID) } - - log.Printf("capi/community-blocklist : added %d entries, deleted %d entries (alert:%d)", inserted, deleted, alertID) - return nil } diff --git a/pkg/database/alerts.go b/pkg/database/alerts.go index f4116c818..cc6d4bbe7 100644 --- a/pkg/database/alerts.go +++ b/pkg/database/alerts.go @@ -561,6 +561,10 @@ func BuildAlertRequestFromFilter(alerts *ent.AlertQuery, filter map[string][]str delete(filter, "simulated") } + if _, ok := filter["origin"]; ok { + filter["include_capi"] = []string{"true"} + } + for param, value := range filter { switch param { case "contains": @@ -579,7 +583,7 @@ func BuildAlertRequestFromFilter(alerts *ent.AlertQuery, filter map[string][]str case "value": alerts = alerts.Where(alert.SourceValueEQ(value[0])) case "scenario": - alerts = alerts.Where(alert.ScenarioEQ(value[0])) + alerts = alerts.Where(alert.HasDecisionsWith(decision.ScenarioEQ(value[0]))) case "ip", "range": ip_sz, start_ip, start_sfx, end_ip, end_sfx, err = types.Addr2Ints(value[0]) if err != nil { @@ -617,9 +621,11 @@ func BuildAlertRequestFromFilter(alerts *ent.AlertQuery, filter map[string][]str alerts = alerts.Where(alert.StartedAtLTE(until)) case "decision_type": alerts = alerts.Where(alert.HasDecisionsWith(decision.TypeEQ(value[0]))) + case "origin": + alerts = alerts.Where(alert.HasDecisionsWith(decision.OriginEQ(value[0]))) case "include_capi": //allows to exclude one or more specific origins if value[0] == "false" { - alerts = alerts.Where(alert.HasDecisionsWith(decision.OriginNEQ(CapiMachineID))) + alerts = alerts.Where(alert.HasDecisionsWith(decision.Or(decision.OriginEQ("crowdsec"), decision.OriginEQ("cscli")))) } else if value[0] != "true" { log.Errorf("Invalid bool '%s' for include_capi", value[0]) } From cc72800f50d6d17fb8a866e314500bda94846f19 Mon Sep 17 00:00:00 2001 From: blotus Date: Tue, 11 Jan 2022 16:45:34 +0100 Subject: [PATCH 14/14] Update LAPI swagger (#1155) --- go.mod | 2 +- go.sum | 2 + pkg/apiserver/alerts_test.go | 2 +- pkg/models/add_alerts_request.go | 28 ++++ pkg/models/add_alerts_response.go | 7 + pkg/models/add_signals_request.go | 28 ++++ pkg/models/add_signals_request_item.go | 34 +++++ pkg/models/alert.go | 146 ++++++++++++++++++++- pkg/models/decision.go | 38 ++++++ pkg/models/decisions_stream_response.go | 54 +++++++- pkg/models/delete_alerts_response.go | 7 + pkg/models/delete_decision_response.go | 7 + pkg/models/error_response.go | 7 + pkg/models/event.go | 32 +++++ pkg/models/get_alerts_response.go | 28 ++++ pkg/models/get_decisions_response.go | 28 ++++ pkg/models/localapi_swagger.yaml | 11 +- pkg/models/meta.go | 33 +++++ pkg/models/metrics.go | 63 +++++++++ pkg/models/metrics_soft_info.go | 7 + pkg/models/source.go | 7 + pkg/models/watcher_auth_request.go | 7 + pkg/models/watcher_auth_response.go | 7 + pkg/models/watcher_registration_request.go | 7 + 24 files changed, 585 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index f0334a5ea..a16af68b0 100644 --- a/go.mod +++ b/go.mod @@ -24,7 +24,7 @@ require ( github.com/fsnotify/fsnotify v1.4.9 github.com/gin-gonic/gin v1.6.3 github.com/go-co-op/gocron v1.9.0 - github.com/go-openapi/errors v0.19.9 + github.com/go-openapi/errors v0.20.1 github.com/go-openapi/strfmt v0.19.11 github.com/go-openapi/swag v0.19.12 github.com/go-openapi/validate v0.20.0 diff --git a/go.sum b/go.sum index b94c5ff3b..8a7d357cf 100644 --- a/go.sum +++ b/go.sum @@ -202,6 +202,8 @@ github.com/go-openapi/errors v0.19.7/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpX github.com/go-openapi/errors v0.19.8/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= github.com/go-openapi/errors v0.19.9 h1:9SnKdGhiPZHF3ttwFMiCBEb8jQ4IDdrK+5+a0oTygA4= github.com/go-openapi/errors v0.19.9/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= +github.com/go-openapi/errors v0.20.1 h1:j23mMDtRxMwIobkpId7sWh7Ddcx4ivaoqUbfXx5P+a8= +github.com/go-openapi/errors v0.20.1/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= github.com/go-openapi/inflect v0.19.0/go.mod h1:lHpZVlpIQqLyKwJ4N+YSc9hchQy/i12fJykb83CRBH4= github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= diff --git a/pkg/apiserver/alerts_test.go b/pkg/apiserver/alerts_test.go index 5b3f4fcaa..8c49fa0fd 100644 --- a/pkg/apiserver/alerts_test.go +++ b/pkg/apiserver/alerts_test.go @@ -123,7 +123,7 @@ func TestCreateAlert(t *testing.T) { router.ServeHTTP(w, req) assert.Equal(t, 500, w.Code) - assert.Equal(t, "{\"message\":\"validation failure list:\\nscenario in body is required\\nscenario_hash in body is required\\nscenario_version in body is required\\nsimulated in body is required\\nsource in body is required\"}", w.Body.String()) + assert.Equal(t, "{\"message\":\"validation failure list:\\n0.scenario in body is required\\n0.scenario_hash in body is required\\n0.scenario_version in body is required\\n0.simulated in body is required\\n0.source in body is required\"}", w.Body.String()) // Create Valid Alert alertContentBytes, err = ioutil.ReadFile("./tests/alert_sample.json") diff --git a/pkg/models/add_alerts_request.go b/pkg/models/add_alerts_request.go index ead8662f6..fd7246be0 100644 --- a/pkg/models/add_alerts_request.go +++ b/pkg/models/add_alerts_request.go @@ -6,6 +6,7 @@ package models // Editing this file might prove futile when you re-run the swagger generate command import ( + "context" "strconv" "github.com/go-openapi/errors" @@ -31,6 +32,33 @@ func (m AddAlertsRequest) Validate(formats strfmt.Registry) error { if err := m[i].Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName(strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName(strconv.Itoa(i)) + } + return err + } + } + + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +// ContextValidate validate this add alerts request based on the context it is used +func (m AddAlertsRequest) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + for i := 0; i < len(m); i++ { + + if m[i] != nil { + if err := m[i].ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName(strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName(strconv.Itoa(i)) } return err } diff --git a/pkg/models/add_alerts_response.go b/pkg/models/add_alerts_response.go index aeb70bb77..cd8c98f77 100644 --- a/pkg/models/add_alerts_response.go +++ b/pkg/models/add_alerts_response.go @@ -6,6 +6,8 @@ package models // Editing this file might prove futile when you re-run the swagger generate command import ( + "context" + "github.com/go-openapi/strfmt" ) @@ -18,3 +20,8 @@ type AddAlertsResponse []string func (m AddAlertsResponse) Validate(formats strfmt.Registry) error { return nil } + +// ContextValidate validates this add alerts response based on context it is used +func (m AddAlertsResponse) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} diff --git a/pkg/models/add_signals_request.go b/pkg/models/add_signals_request.go index 87b47807d..d9c2b363a 100644 --- a/pkg/models/add_signals_request.go +++ b/pkg/models/add_signals_request.go @@ -6,6 +6,7 @@ package models // Editing this file might prove futile when you re-run the swagger generate command import ( + "context" "strconv" "github.com/go-openapi/errors" @@ -33,6 +34,33 @@ func (m AddSignalsRequest) Validate(formats strfmt.Registry) error { if err := m[i].Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName(strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName(strconv.Itoa(i)) + } + return err + } + } + + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +// ContextValidate validate this add signals request based on the context it is used +func (m AddSignalsRequest) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + for i := 0; i < len(m); i++ { + + if m[i] != nil { + if err := m[i].ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName(strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName(strconv.Itoa(i)) } return err } diff --git a/pkg/models/add_signals_request_item.go b/pkg/models/add_signals_request_item.go index 555566cfb..c03930603 100644 --- a/pkg/models/add_signals_request_item.go +++ b/pkg/models/add_signals_request_item.go @@ -6,6 +6,8 @@ package models // Editing this file might prove futile when you re-run the swagger generate command import ( + "context" + "github.com/go-openapi/errors" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" @@ -136,6 +138,8 @@ func (m *AddSignalsRequestItem) validateSource(formats strfmt.Registry) error { if err := m.Source.Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("source") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("source") } return err } @@ -162,6 +166,36 @@ func (m *AddSignalsRequestItem) validateStopAt(formats strfmt.Registry) error { return nil } +// ContextValidate validate this add signals request item based on the context it is used +func (m *AddSignalsRequestItem) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + if err := m.contextValidateSource(ctx, formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *AddSignalsRequestItem) contextValidateSource(ctx context.Context, formats strfmt.Registry) error { + + if m.Source != nil { + if err := m.Source.ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("source") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("source") + } + return err + } + } + + return nil +} + // MarshalBinary interface implementation func (m *AddSignalsRequestItem) MarshalBinary() ([]byte, error) { if m == nil { diff --git a/pkg/models/alert.go b/pkg/models/alert.go index b9a4c84c2..3d20fa603 100644 --- a/pkg/models/alert.go +++ b/pkg/models/alert.go @@ -6,6 +6,7 @@ package models // Editing this file might prove futile when you re-run the swagger generate command import ( + "context" "strconv" "github.com/go-openapi/errors" @@ -168,7 +169,6 @@ func (m *Alert) validateCapacity(formats strfmt.Registry) error { } func (m *Alert) validateDecisions(formats strfmt.Registry) error { - if swag.IsZero(m.Decisions) { // not required return nil } @@ -182,6 +182,8 @@ func (m *Alert) validateDecisions(formats strfmt.Registry) error { if err := m.Decisions[i].Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("decisions" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("decisions" + "." + strconv.Itoa(i)) } return err } @@ -207,6 +209,8 @@ func (m *Alert) validateEvents(formats strfmt.Registry) error { if err := m.Events[i].Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("events" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("events" + "." + strconv.Itoa(i)) } return err } @@ -245,7 +249,6 @@ func (m *Alert) validateMessage(formats strfmt.Registry) error { } func (m *Alert) validateMeta(formats strfmt.Registry) error { - if swag.IsZero(m.Meta) { // not required return nil } @@ -253,6 +256,8 @@ func (m *Alert) validateMeta(formats strfmt.Registry) error { if err := m.Meta.Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("meta") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("meta") } return err } @@ -306,6 +311,8 @@ func (m *Alert) validateSource(formats strfmt.Registry) error { if err := m.Source.Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("source") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("source") } return err } @@ -332,6 +339,141 @@ func (m *Alert) validateStopAt(formats strfmt.Registry) error { return nil } +// ContextValidate validate this alert based on the context it is used +func (m *Alert) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + if err := m.contextValidateCreatedAt(ctx, formats); err != nil { + res = append(res, err) + } + + if err := m.contextValidateDecisions(ctx, formats); err != nil { + res = append(res, err) + } + + if err := m.contextValidateEvents(ctx, formats); err != nil { + res = append(res, err) + } + + if err := m.contextValidateID(ctx, formats); err != nil { + res = append(res, err) + } + + if err := m.contextValidateMachineID(ctx, formats); err != nil { + res = append(res, err) + } + + if err := m.contextValidateMeta(ctx, formats); err != nil { + res = append(res, err) + } + + if err := m.contextValidateSource(ctx, formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *Alert) contextValidateCreatedAt(ctx context.Context, formats strfmt.Registry) error { + + if err := validate.ReadOnly(ctx, "created_at", "body", string(m.CreatedAt)); err != nil { + return err + } + + return nil +} + +func (m *Alert) contextValidateDecisions(ctx context.Context, formats strfmt.Registry) error { + + for i := 0; i < len(m.Decisions); i++ { + + if m.Decisions[i] != nil { + if err := m.Decisions[i].ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("decisions" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("decisions" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + + return nil +} + +func (m *Alert) contextValidateEvents(ctx context.Context, formats strfmt.Registry) error { + + for i := 0; i < len(m.Events); i++ { + + if m.Events[i] != nil { + if err := m.Events[i].ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("events" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("events" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + + return nil +} + +func (m *Alert) contextValidateID(ctx context.Context, formats strfmt.Registry) error { + + if err := validate.ReadOnly(ctx, "id", "body", int64(m.ID)); err != nil { + return err + } + + return nil +} + +func (m *Alert) contextValidateMachineID(ctx context.Context, formats strfmt.Registry) error { + + if err := validate.ReadOnly(ctx, "machine_id", "body", string(m.MachineID)); err != nil { + return err + } + + return nil +} + +func (m *Alert) contextValidateMeta(ctx context.Context, formats strfmt.Registry) error { + + if err := m.Meta.ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("meta") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("meta") + } + return err + } + + return nil +} + +func (m *Alert) contextValidateSource(ctx context.Context, formats strfmt.Registry) error { + + if m.Source != nil { + if err := m.Source.ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("source") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("source") + } + return err + } + } + + return nil +} + // MarshalBinary interface implementation func (m *Alert) MarshalBinary() ([]byte, error) { if m == nil { diff --git a/pkg/models/decision.go b/pkg/models/decision.go index de9a78edc..5e317d73f 100644 --- a/pkg/models/decision.go +++ b/pkg/models/decision.go @@ -6,6 +6,8 @@ package models // Editing this file might prove futile when you re-run the swagger generate command import ( + "context" + "github.com/go-openapi/errors" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" @@ -138,6 +140,42 @@ func (m *Decision) validateValue(formats strfmt.Registry) error { return nil } +// ContextValidate validate this decision based on the context it is used +func (m *Decision) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + if err := m.contextValidateID(ctx, formats); err != nil { + res = append(res, err) + } + + if err := m.contextValidateSimulated(ctx, formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *Decision) contextValidateID(ctx context.Context, formats strfmt.Registry) error { + + if err := validate.ReadOnly(ctx, "id", "body", int64(m.ID)); err != nil { + return err + } + + return nil +} + +func (m *Decision) contextValidateSimulated(ctx context.Context, formats strfmt.Registry) error { + + if err := validate.ReadOnly(ctx, "simulated", "body", m.Simulated); err != nil { + return err + } + + return nil +} + // MarshalBinary interface implementation func (m *Decision) MarshalBinary() ([]byte, error) { if m == nil { diff --git a/pkg/models/decisions_stream_response.go b/pkg/models/decisions_stream_response.go index 132681cb5..8ec0c6914 100644 --- a/pkg/models/decisions_stream_response.go +++ b/pkg/models/decisions_stream_response.go @@ -6,6 +6,8 @@ package models // Editing this file might prove futile when you re-run the swagger generate command import ( + "context" + "github.com/go-openapi/errors" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" @@ -42,7 +44,6 @@ func (m *DecisionsStreamResponse) Validate(formats strfmt.Registry) error { } func (m *DecisionsStreamResponse) validateDeleted(formats strfmt.Registry) error { - if swag.IsZero(m.Deleted) { // not required return nil } @@ -50,6 +51,8 @@ func (m *DecisionsStreamResponse) validateDeleted(formats strfmt.Registry) error if err := m.Deleted.Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("deleted") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("deleted") } return err } @@ -58,7 +61,6 @@ func (m *DecisionsStreamResponse) validateDeleted(formats strfmt.Registry) error } func (m *DecisionsStreamResponse) validateNew(formats strfmt.Registry) error { - if swag.IsZero(m.New) { // not required return nil } @@ -66,6 +68,54 @@ func (m *DecisionsStreamResponse) validateNew(formats strfmt.Registry) error { if err := m.New.Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("new") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("new") + } + return err + } + + return nil +} + +// ContextValidate validate this decisions stream response based on the context it is used +func (m *DecisionsStreamResponse) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + if err := m.contextValidateDeleted(ctx, formats); err != nil { + res = append(res, err) + } + + if err := m.contextValidateNew(ctx, formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *DecisionsStreamResponse) contextValidateDeleted(ctx context.Context, formats strfmt.Registry) error { + + if err := m.Deleted.ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("deleted") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("deleted") + } + return err + } + + return nil +} + +func (m *DecisionsStreamResponse) contextValidateNew(ctx context.Context, formats strfmt.Registry) error { + + if err := m.New.ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("new") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("new") } return err } diff --git a/pkg/models/delete_alerts_response.go b/pkg/models/delete_alerts_response.go index f13688954..67306e449 100644 --- a/pkg/models/delete_alerts_response.go +++ b/pkg/models/delete_alerts_response.go @@ -6,6 +6,8 @@ package models // Editing this file might prove futile when you re-run the swagger generate command import ( + "context" + "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" ) @@ -24,6 +26,11 @@ func (m *DeleteAlertsResponse) Validate(formats strfmt.Registry) error { return nil } +// ContextValidate validates this delete alerts response based on context it is used +func (m *DeleteAlertsResponse) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + // MarshalBinary interface implementation func (m *DeleteAlertsResponse) MarshalBinary() ([]byte, error) { if m == nil { diff --git a/pkg/models/delete_decision_response.go b/pkg/models/delete_decision_response.go index d934bd56a..70423d661 100644 --- a/pkg/models/delete_decision_response.go +++ b/pkg/models/delete_decision_response.go @@ -6,6 +6,8 @@ package models // Editing this file might prove futile when you re-run the swagger generate command import ( + "context" + "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" ) @@ -24,6 +26,11 @@ func (m *DeleteDecisionResponse) Validate(formats strfmt.Registry) error { return nil } +// ContextValidate validates this delete decision response based on context it is used +func (m *DeleteDecisionResponse) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + // MarshalBinary interface implementation func (m *DeleteDecisionResponse) MarshalBinary() ([]byte, error) { if m == nil { diff --git a/pkg/models/error_response.go b/pkg/models/error_response.go index 2ef64302e..db62a4d98 100644 --- a/pkg/models/error_response.go +++ b/pkg/models/error_response.go @@ -6,6 +6,8 @@ package models // Editing this file might prove futile when you re-run the swagger generate command import ( + "context" + "github.com/go-openapi/errors" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" @@ -50,6 +52,11 @@ func (m *ErrorResponse) validateMessage(formats strfmt.Registry) error { return nil } +// ContextValidate validates this error response based on context it is used +func (m *ErrorResponse) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + // MarshalBinary interface implementation func (m *ErrorResponse) MarshalBinary() ([]byte, error) { if m == nil { diff --git a/pkg/models/event.go b/pkg/models/event.go index b9ff31917..6e165eb9b 100644 --- a/pkg/models/event.go +++ b/pkg/models/event.go @@ -6,6 +6,8 @@ package models // Editing this file might prove futile when you re-run the swagger generate command import ( + "context" + "github.com/go-openapi/errors" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" @@ -53,6 +55,8 @@ func (m *Event) validateMeta(formats strfmt.Registry) error { if err := m.Meta.Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("meta") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("meta") } return err } @@ -69,6 +73,34 @@ func (m *Event) validateTimestamp(formats strfmt.Registry) error { return nil } +// ContextValidate validate this event based on the context it is used +func (m *Event) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + if err := m.contextValidateMeta(ctx, formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *Event) contextValidateMeta(ctx context.Context, formats strfmt.Registry) error { + + if err := m.Meta.ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("meta") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("meta") + } + return err + } + + return nil +} + // MarshalBinary interface implementation func (m *Event) MarshalBinary() ([]byte, error) { if m == nil { diff --git a/pkg/models/get_alerts_response.go b/pkg/models/get_alerts_response.go index 2c673d650..41b9d5afd 100644 --- a/pkg/models/get_alerts_response.go +++ b/pkg/models/get_alerts_response.go @@ -6,6 +6,7 @@ package models // Editing this file might prove futile when you re-run the swagger generate command import ( + "context" "strconv" "github.com/go-openapi/errors" @@ -31,6 +32,33 @@ func (m GetAlertsResponse) Validate(formats strfmt.Registry) error { if err := m[i].Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName(strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName(strconv.Itoa(i)) + } + return err + } + } + + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +// ContextValidate validate this get alerts response based on the context it is used +func (m GetAlertsResponse) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + for i := 0; i < len(m); i++ { + + if m[i] != nil { + if err := m[i].ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName(strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName(strconv.Itoa(i)) } return err } diff --git a/pkg/models/get_decisions_response.go b/pkg/models/get_decisions_response.go index 7113212ba..b65b950fc 100644 --- a/pkg/models/get_decisions_response.go +++ b/pkg/models/get_decisions_response.go @@ -6,6 +6,7 @@ package models // Editing this file might prove futile when you re-run the swagger generate command import ( + "context" "strconv" "github.com/go-openapi/errors" @@ -31,6 +32,33 @@ func (m GetDecisionsResponse) Validate(formats strfmt.Registry) error { if err := m[i].Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName(strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName(strconv.Itoa(i)) + } + return err + } + } + + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +// ContextValidate validate this get decisions response based on the context it is used +func (m GetDecisionsResponse) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + for i := 0; i < len(m); i++ { + + if m[i] != nil { + if err := m[i].ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName(strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName(strconv.Itoa(i)) } return err } diff --git a/pkg/models/localapi_swagger.yaml b/pkg/models/localapi_swagger.yaml index 680f39a47..b30036fc2 100644 --- a/pkg/models/localapi_swagger.yaml +++ b/pkg/models/localapi_swagger.yaml @@ -45,7 +45,6 @@ paths: required: false type: string description: 'Comma separated scopes of decisions to fetch' - example: ip,range,country responses: '200': description: successful operation @@ -406,6 +405,11 @@ paths: required: false type: number description: 'number of alerts to return' + - name: origin + in: query + required: false + type: string + description: 'restrict results to this origin (ie. lists,CAPI,cscli)' responses: '200': description: successful operation @@ -485,6 +489,11 @@ paths: required: false type: number description: 'number of alerts to return' + - name: origin + in: query + required: false + type: string + description: 'restrict results to this origin (ie. lists,CAPI,cscli)' responses: '200': description: successful operation diff --git a/pkg/models/meta.go b/pkg/models/meta.go index 74e7532fc..6ad20856d 100644 --- a/pkg/models/meta.go +++ b/pkg/models/meta.go @@ -6,6 +6,7 @@ package models // Editing this file might prove futile when you re-run the swagger generate command import ( + "context" "strconv" "github.com/go-openapi/errors" @@ -33,6 +34,33 @@ func (m Meta) Validate(formats strfmt.Registry) error { if err := m[i].Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName(strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName(strconv.Itoa(i)) + } + return err + } + } + + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +// ContextValidate validate this meta based on the context it is used +func (m Meta) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + for i := 0; i < len(m); i++ { + + if m[i] != nil { + if err := m[i].ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName(strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName(strconv.Itoa(i)) } return err } @@ -63,6 +91,11 @@ func (m *MetaItems0) Validate(formats strfmt.Registry) error { return nil } +// ContextValidate validates this meta items0 based on context it is used +func (m *MetaItems0) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + // MarshalBinary interface implementation func (m *MetaItems0) MarshalBinary() ([]byte, error) { if m == nil { diff --git a/pkg/models/metrics.go b/pkg/models/metrics.go index 1e96d37c1..5b647bfbd 100644 --- a/pkg/models/metrics.go +++ b/pkg/models/metrics.go @@ -6,6 +6,7 @@ package models // Editing this file might prove futile when you re-run the swagger generate command import ( + "context" "strconv" "github.com/go-openapi/errors" @@ -78,6 +79,8 @@ func (m *Metrics) validateBouncers(formats strfmt.Registry) error { if err := m.Bouncers[i].Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("bouncers" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("bouncers" + "." + strconv.Itoa(i)) } return err } @@ -103,6 +106,66 @@ func (m *Metrics) validateMachines(formats strfmt.Registry) error { if err := m.Machines[i].Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("machines" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("machines" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + + return nil +} + +// ContextValidate validate this metrics based on the context it is used +func (m *Metrics) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + if err := m.contextValidateBouncers(ctx, formats); err != nil { + res = append(res, err) + } + + if err := m.contextValidateMachines(ctx, formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *Metrics) contextValidateBouncers(ctx context.Context, formats strfmt.Registry) error { + + for i := 0; i < len(m.Bouncers); i++ { + + if m.Bouncers[i] != nil { + if err := m.Bouncers[i].ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("bouncers" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("bouncers" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + + return nil +} + +func (m *Metrics) contextValidateMachines(ctx context.Context, formats strfmt.Registry) error { + + for i := 0; i < len(m.Machines); i++ { + + if m.Machines[i] != nil { + if err := m.Machines[i].ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("machines" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("machines" + "." + strconv.Itoa(i)) } return err } diff --git a/pkg/models/metrics_soft_info.go b/pkg/models/metrics_soft_info.go index 60aa727bb..cd5be2c07 100644 --- a/pkg/models/metrics_soft_info.go +++ b/pkg/models/metrics_soft_info.go @@ -6,6 +6,8 @@ package models // Editing this file might prove futile when you re-run the swagger generate command import ( + "context" + "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" ) @@ -29,6 +31,11 @@ func (m *MetricsSoftInfo) Validate(formats strfmt.Registry) error { return nil } +// ContextValidate validates this metrics soft info based on context it is used +func (m *MetricsSoftInfo) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + // MarshalBinary interface implementation func (m *MetricsSoftInfo) MarshalBinary() ([]byte, error) { if m == nil { diff --git a/pkg/models/source.go b/pkg/models/source.go index c6dc57905..8b3a81aeb 100644 --- a/pkg/models/source.go +++ b/pkg/models/source.go @@ -6,6 +6,8 @@ package models // Editing this file might prove futile when you re-run the swagger generate command import ( + "context" + "github.com/go-openapi/errors" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" @@ -83,6 +85,11 @@ func (m *Source) validateValue(formats strfmt.Registry) error { return nil } +// ContextValidate validates this source based on context it is used +func (m *Source) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + // MarshalBinary interface implementation func (m *Source) MarshalBinary() ([]byte, error) { if m == nil { diff --git a/pkg/models/watcher_auth_request.go b/pkg/models/watcher_auth_request.go index 1be134a8b..52347c581 100644 --- a/pkg/models/watcher_auth_request.go +++ b/pkg/models/watcher_auth_request.go @@ -6,6 +6,8 @@ package models // Editing this file might prove futile when you re-run the swagger generate command import ( + "context" + "github.com/go-openapi/errors" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" @@ -70,6 +72,11 @@ func (m *WatcherAuthRequest) validatePassword(formats strfmt.Registry) error { return nil } +// ContextValidate validates this watcher auth request based on context it is used +func (m *WatcherAuthRequest) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + // MarshalBinary interface implementation func (m *WatcherAuthRequest) MarshalBinary() ([]byte, error) { if m == nil { diff --git a/pkg/models/watcher_auth_response.go b/pkg/models/watcher_auth_response.go index 185aa875e..296812dc8 100644 --- a/pkg/models/watcher_auth_response.go +++ b/pkg/models/watcher_auth_response.go @@ -6,6 +6,8 @@ package models // Editing this file might prove futile when you re-run the swagger generate command import ( + "context" + "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" ) @@ -32,6 +34,11 @@ func (m *WatcherAuthResponse) Validate(formats strfmt.Registry) error { return nil } +// ContextValidate validates this watcher auth response based on context it is used +func (m *WatcherAuthResponse) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + // MarshalBinary interface implementation func (m *WatcherAuthResponse) MarshalBinary() ([]byte, error) { if m == nil { diff --git a/pkg/models/watcher_registration_request.go b/pkg/models/watcher_registration_request.go index 194a1e715..8be802ea3 100644 --- a/pkg/models/watcher_registration_request.go +++ b/pkg/models/watcher_registration_request.go @@ -6,6 +6,8 @@ package models // Editing this file might prove futile when you re-run the swagger generate command import ( + "context" + "github.com/go-openapi/errors" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" @@ -67,6 +69,11 @@ func (m *WatcherRegistrationRequest) validatePassword(formats strfmt.Registry) e return nil } +// ContextValidate validates this watcher registration request based on context it is used +func (m *WatcherRegistrationRequest) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + // MarshalBinary interface implementation func (m *WatcherRegistrationRequest) MarshalBinary() ([]byte, error) { if m == nil {