From 4c20a27e7578859cf534c2147cd9f2fe3d59dfde Mon Sep 17 00:00:00 2001 From: yo Date: Thu, 4 Feb 2021 16:12:06 +0100 Subject: [PATCH 001/100] Support minus (-) in hostnames --- postfix-log-parser.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/postfix-log-parser.go b/postfix-log-parser.go index 739c2ec..47e4dbf 100644 --- a/postfix-log-parser.go +++ b/postfix-log-parser.go @@ -9,7 +9,7 @@ const ( TimeFormat = "Jan 2 15:04:05" TimeFormatISO8601 = "2006-01-02T15:04:05.999999-07:00" TimeRegexpFormat = `([A-Za-z]{3}\s*[0-9]{1,2} [0-9]{2}:[0-9]{2}:[0-9]{2}|^\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+(?:[+-][0-2]\d:[0-5]\d|Z))` - HostRegexpFormat = `([0-9A-Za-z\.]*)` + HostRegexpFormat = `([0-9A-Za-z\-\.]*)` ProcessRegexpFormat = `(postfix/[a-z]*\[[0-9]{1,5}\])?` QueueIdRegexpFormat = `([0-9A-Z]*)` MessageDetailsRegexpFormat = `((?:client=(.+)\[(.+)\])?(?:message-id=<(.+)>)?(?:from=<(.+@.+)>)?(?:to=<(.+@.+)>.*status=([a-z]+))?.*)` From 5e29f9ca3431b71dade2a7715fb3373de0c6a6da Mon Sep 17 00:00:00 2001 From: yo Date: Thu, 4 Feb 2021 16:22:22 +0100 Subject: [PATCH 002/100] Support postfix-$instance/submission/smtpd[$PID] log lines --- postfix-log-parser.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/postfix-log-parser.go b/postfix-log-parser.go index 47e4dbf..92a70a9 100644 --- a/postfix-log-parser.go +++ b/postfix-log-parser.go @@ -10,7 +10,7 @@ const ( TimeFormatISO8601 = "2006-01-02T15:04:05.999999-07:00" TimeRegexpFormat = `([A-Za-z]{3}\s*[0-9]{1,2} [0-9]{2}:[0-9]{2}:[0-9]{2}|^\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+(?:[+-][0-2]\d:[0-5]\d|Z))` HostRegexpFormat = `([0-9A-Za-z\-\.]*)` - ProcessRegexpFormat = `(postfix/[a-z]*\[[0-9]{1,5}\])?` + ProcessRegexpFormat = `(postfix.*(\/[a-z]*)+\[[0-9]{1,5}\])?` QueueIdRegexpFormat = `([0-9A-Z]*)` MessageDetailsRegexpFormat = `((?:client=(.+)\[(.+)\])?(?:message-id=<(.+)>)?(?:from=<(.+@.+)>)?(?:to=<(.+@.+)>.*status=([a-z]+))?.*)` RegexpFormat = TimeRegexpFormat + ` ` + HostRegexpFormat + ` ` + ProcessRegexpFormat + `: ` + QueueIdRegexpFormat + `(?:\: )?` + MessageDetailsRegexpFormat @@ -59,14 +59,14 @@ func (p *PostfixLog) Parse(text []byte) (LogFormat, error) { Time: &t, Hostname: string(group[2]), Process: string(group[3]), - QueueId: string(group[4]), - Messages: string(group[5]), - ClientHostname: string(group[6]), - ClinetIp: string(group[7]), - MessageId: string(group[8]), - From: string(group[9]), - To: string(group[10]), - Status: string(group[11]), + QueueId: string(group[5]), + Messages: string(group[6]), + ClientHostname: string(group[7]), + ClinetIp: string(group[8]), + MessageId: string(group[9]), + From: string(group[10]), + To: string(group[11]), + Status: string(group[12]), } return logFormat, nil From 23cb54943d37c24a9b7735972e4521407757861f Mon Sep 17 00:00:00 2001 From: yo Date: Thu, 4 Feb 2021 16:23:39 +0100 Subject: [PATCH 003/100] Rsyslog 8 do not add colon after PID --- postfix-log-parser.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/postfix-log-parser.go b/postfix-log-parser.go index 92a70a9..236c3c8 100644 --- a/postfix-log-parser.go +++ b/postfix-log-parser.go @@ -13,7 +13,7 @@ const ( ProcessRegexpFormat = `(postfix.*(\/[a-z]*)+\[[0-9]{1,5}\])?` QueueIdRegexpFormat = `([0-9A-Z]*)` MessageDetailsRegexpFormat = `((?:client=(.+)\[(.+)\])?(?:message-id=<(.+)>)?(?:from=<(.+@.+)>)?(?:to=<(.+@.+)>.*status=([a-z]+))?.*)` - RegexpFormat = TimeRegexpFormat + ` ` + HostRegexpFormat + ` ` + ProcessRegexpFormat + `: ` + QueueIdRegexpFormat + `(?:\: )?` + MessageDetailsRegexpFormat + RegexpFormat = TimeRegexpFormat + ` ` + HostRegexpFormat + ` ` + ProcessRegexpFormat + `:? ` + QueueIdRegexpFormat + `(?:\: )?` + MessageDetailsRegexpFormat ) type ( From 02c64968eb4db87b8b1b80f2eb3a2be4935aeb96 Mon Sep 17 00:00:00 2001 From: yo Date: Thu, 4 Feb 2021 16:25:53 +0100 Subject: [PATCH 004/100] Just skip over non compliant lines --- postfix-log-parser.go | 5 +++++ postfix-log-parser/cmd/root.go | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/postfix-log-parser.go b/postfix-log-parser.go index 236c3c8..d8e5f85 100644 --- a/postfix-log-parser.go +++ b/postfix-log-parser.go @@ -1,6 +1,7 @@ package postfixlog import ( + "errors" "regexp" "time" ) @@ -46,6 +47,10 @@ func NewPostfixLog() *PostfixLog { func (p *PostfixLog) Parse(text []byte) (LogFormat, error) { re := p.Regexp.Copy() group := re.FindSubmatch(text) + if len(group) == 0 { + err := errors.New("Error: Line do not match regex") + return LogFormat{}, err + } var t time.Time t, err := time.ParseInLocation(TimeFormat, string(group[1]), time.Local) if err != nil { diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index 22d5187..f7ddff1 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -58,6 +58,10 @@ func NewCmdRoot() *cobra.Command { // parse log logFormat, err := p.Parse(scanner.Bytes()) if err != nil { + // Incorrect line, just skip it + if err.Error() == "Error: Line do not match regex" { + continue + } cmd.SetOutput(os.Stderr) cmd.Println(err) os.Exit(1) From 76c0b4ce8ff02723d31f9c6a9d101fafdeb7aea5 Mon Sep 17 00:00:00 2001 From: yo Date: Thu, 4 Feb 2021 16:56:57 +0100 Subject: [PATCH 005/100] Point to yo000 repository --- go.mod | 2 +- postfix-log-parser/cmd/root.go | 2 +- postfix-log-parser/main.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 6e5eadf..22dcf41 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/youyo/postfix-log-parser +module github.com/yo000/postfix-log-parser require ( github.com/spf13/cobra v0.0.3 diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index f7ddff1..02227cf 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -9,7 +9,7 @@ import ( "time" "github.com/spf13/cobra" - postfixlog "github.com/youyo/postfix-log-parser" + postfixlog "github.com/yo000/postfix-log-parser" ) func init() {} diff --git a/postfix-log-parser/main.go b/postfix-log-parser/main.go index b37ca2b..8cc0a71 100644 --- a/postfix-log-parser/main.go +++ b/postfix-log-parser/main.go @@ -20,7 +20,7 @@ package main -import "github.com/youyo/postfix-log-parser/postfix-log-parser/cmd" +import "github.com/yo000/postfix-log-parser/postfix-log-parser/cmd" func main() { cmd.Execute() From c205065060d71f1b46f99f864ab6fefdc09e788b Mon Sep 17 00:00:00 2001 From: yo Date: Thu, 4 Feb 2021 19:46:46 +0100 Subject: [PATCH 006/100] Flag -f to flatten output json, so it can be sent to syslog tools --- postfix-log-parser/cmd/root.go | 66 ++++++++++++++++++++++++++++++---- 1 file changed, 60 insertions(+), 6 deletions(-) diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index 02227cf..1f6d5ad 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -33,9 +33,50 @@ type ( Status string `json:"status"` Message string `json:"message"` } + + PostfixLogParserFlat struct { + Time *time.Time `json:"timestamp"` + Hostname string `json:"hostname"` + Process string `json:"process"` + QueueId string `json:"queue_id"` + ClientHostname string `json:"client_hostname"` + ClinetIp string `json:"client_ip"` + MessageId string `json:"message_id"` + From string `json:"from"` + TimeSent *time.Time `json:"time_sent"` + To string `json:"to"` + Status string `json:"status"` + Message string `json:"message"` + } ) +func PlpToFlat(plp *PostfixLogParser) []PostfixLogParserFlat { + //plpf := [len(plp.Messages)]PostfixLogParserFlat{} + var plpf = make([]PostfixLogParserFlat, len(plp.Messages)) + + for i := range plp.Messages { + plpf[i] = PostfixLogParserFlat{ + Time: plp.Time, + Hostname: plp.Hostname, + Process: plp.Process, + QueueId: plp.QueueId, + ClientHostname: plp.ClientHostname, + ClinetIp: plp.ClinetIp, + MessageId: plp.MessageId, + From: plp.From, + TimeSent: plp.Messages[i].Time, + To: plp.Messages[i].To, + Status: plp.Messages[i].Status, + Message: plp.Messages[i].Message, + } + } + + return plpf +} + func NewCmdRoot() *cobra.Command { + var flatten bool + cmd := &cobra.Command{ Use: "postfix-log-parser", Short: "Parse postfix log, and output json format", @@ -121,13 +162,24 @@ func NewCmdRoot() *cobra.Command { // "removed" message is end of logs. then flush. if logFormat.Messages == "removed" { if plp, ok := m[logFormat.QueueId]; ok { - jsonBytes, err := json.Marshal(plp) - if err != nil { - log.Fatal(err) + if flatten { + // Flatten the structure, then print each message + for _, plpf := range PlpToFlat(plp) { + jsonBytes, err := json.Marshal(plpf) + if err != nil { + log.Fatal(err) + } + fmt.Fprintln(wtr, string(jsonBytes)) + wtr.Flush() + } + } else { + jsonBytes, err := json.Marshal(plp) + if err != nil { + log.Fatal(err) + } + fmt.Fprintln(wtr, string(jsonBytes)) + wtr.Flush() } - - fmt.Fprintln(wtr, string(jsonBytes)) - wtr.Flush() } } @@ -135,6 +187,8 @@ func NewCmdRoot() *cobra.Command { }, } + cmd.Flags().BoolVarP(&flatten, "flatten", "f", false, "Flatten output for using with syslog") + cobra.OnInitialize(initConfig) return cmd } From e87e4e4dbf63f65cb79743c120f89f1d7aea7360 Mon Sep 17 00:00:00 2001 From: yo Date: Thu, 4 Feb 2021 19:51:27 +0100 Subject: [PATCH 007/100] Usage update --- README.md | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/README.md b/README.md index dcd9b12..5edfa9f 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,45 @@ Input postfix logs as os stdin. . ``` +Use -f flag to flatten json structure: + +``` console +# cat /var/log/maillog | ./postfix-log-parser +{ + "time": "0000-10-10T15:59:29+09:00", + "hostname": "mail", + "process": "postfix/smtpd[1827]", + "queue_id": "3D74ADB7400B", + "client_hostname": "example.com", + "client_ip": "127.0.0.1", + "message_id": "f93388828093534f92d85ffe21b2a719@example.info", + "from": "test2@example.info", + "time_sent": "0000-10-10T15:59:30+09:00", + "to": "test@example.to", + "status": "sent", + "message": "to=, relay=example.to[192.168.0.20]:25, delay=1.7, delays=0.02/0/1.7/0.06, dsn=2.0.0, status=sent (250 [Sniper] OK 1539154772 snipe-queue 10549)" +} +{ + "time": "0000-10-10T15:59:29+09:00", + "hostname": "mail", + "process": "postfix/smtpd[1827]", + "queue_id": "3D74ADB7400B", + "client_hostname": "example.com", + "client_ip": "127.0.0.1", + "message_id": "f93388828093534f92d85ffe21b2a719@example.info", + "from": "test2@example.info", + "time_sent": "0000-10-10T15:59:30+09:00", + "to": "test2@example.to", + "status": "sent", + "message": "to=, relay=example.to[192.168.0.20]:25, delay=1.7, delays=0.02/0/1.7/0.06, dsn=2.0.0, status=sent (250 [Sniper] OK 1539154772 snipe-queue 10549)" + } + ] +} +. +. +. +``` + ## Library usage ``` From ef4c5961c27a949b3f66a50bce0b13024be9d014 Mon Sep 17 00:00:00 2001 From: yo Date: Thu, 4 Feb 2021 19:52:24 +0100 Subject: [PATCH 008/100] Usage update --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5edfa9f..07ab830 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Download the latest release from github. https://github.com/youyo/postfix-log-pa Input postfix logs as os stdin. ``` console -# cat /var/log/maillog | ./postfix-log-parser +# cat /var/log/maillog | ./postfix-log-parser | jq { "time": "0000-10-10T15:59:29+09:00", "hostname": "mail", @@ -45,7 +45,7 @@ Input postfix logs as os stdin. Use -f flag to flatten json structure: ``` console -# cat /var/log/maillog | ./postfix-log-parser +# cat /var/log/maillog | ./postfix-log-parser | jq { "time": "0000-10-10T15:59:29+09:00", "hostname": "mail", From 22c0349ea8b123f6477596d20f371f5d0ad172ed Mon Sep 17 00:00:00 2001 From: yo Date: Thu, 4 Feb 2021 19:53:08 +0100 Subject: [PATCH 009/100] Usage update --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 07ab830..88487ff 100644 --- a/README.md +++ b/README.md @@ -73,8 +73,6 @@ Use -f flag to flatten json structure: "to": "test2@example.to", "status": "sent", "message": "to=, relay=example.to[192.168.0.20]:25, delay=1.7, delays=0.02/0/1.7/0.06, dsn=2.0.0, status=sent (250 [Sniper] OK 1539154772 snipe-queue 10549)" - } - ] } . . From cc3d482938b5609b7bc9622f6583fc84d93de0d5 Mon Sep 17 00:00:00 2001 From: yo Date: Thu, 4 Feb 2021 19:53:42 +0100 Subject: [PATCH 010/100] Usage update --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 88487ff..6f05e5f 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ Input postfix logs as os stdin. Use -f flag to flatten json structure: ``` console -# cat /var/log/maillog | ./postfix-log-parser | jq +# cat /var/log/maillog | ./postfix-log-parser -f | jq { "time": "0000-10-10T15:59:29+09:00", "hostname": "mail", From fcf156ee2d8502c3e5137737a3064246340ba42d Mon Sep 17 00:00:00 2001 From: yo Date: Thu, 4 Feb 2021 20:52:01 +0100 Subject: [PATCH 011/100] Option -o to write to file --- postfix-log-parser/cmd/root.go | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index 1f6d5ad..2c6fa1a 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -51,7 +51,6 @@ type ( ) func PlpToFlat(plp *PostfixLogParser) []PostfixLogParserFlat { - //plpf := [len(plp.Messages)]PostfixLogParserFlat{} var plpf = make([]PostfixLogParserFlat, len(plp.Messages)) for i := range plp.Messages { @@ -76,6 +75,8 @@ func PlpToFlat(plp *PostfixLogParser) []PostfixLogParserFlat { func NewCmdRoot() *cobra.Command { var flatten bool + var outputFile string + var wtr *bufio.Writer cmd := &cobra.Command{ Use: "postfix-log-parser", @@ -89,8 +90,25 @@ func NewCmdRoot() *cobra.Command { // initialize p := postfixlog.NewPostfixLog() - // writer - wtr := bufio.NewWriter(os.Stdout) + // writer, either file or stdout + if len(outputFile) > 0 { + var f *os.File + var err error + if _, err = os.Stat(outputFile); err == nil { + f, err = os.OpenFile(outputFile, os.O_APPEND|os.O_WRONLY, 0640) + } else if os.IsNotExist(err) { + f, err = os.OpenFile(outputFile, os.O_CREATE|os.O_WRONLY, 0640) + } + if err != nil { + cmd.SetOutput(os.Stderr) + cmd.Println(err) + os.Exit(1) + } + wtr = bufio.NewWriter(f) + defer f.Close() + } else { + wtr = bufio.NewWriter(os.Stdout) + } // input stdin scanner := bufio.NewScanner(os.Stdin) @@ -188,6 +206,7 @@ func NewCmdRoot() *cobra.Command { } cmd.Flags().BoolVarP(&flatten, "flatten", "f", false, "Flatten output for using with syslog") + cmd.Flags().StringVarP(&outputFile, "out", "o", "", "Output to file, append if exists") cobra.OnInitialize(initConfig) return cmd From 82ca75ee2136795bf66c8591bc852d04dcaccffb Mon Sep 17 00:00:00 2001 From: yo Date: Thu, 4 Feb 2021 20:59:53 +0100 Subject: [PATCH 012/100] Usage : piping rsyslog to postfxi-log-parser --- README.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/README.md b/README.md index 6f05e5f..f791bd4 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,25 @@ Use -f flag to flatten json structure: . ``` +Use "-o filename.json" to write output to file. + +## Piping rsyslog to postfix-log-parser + +You can feed syslog to postfix-log-parser by using "omprog" rsyslog module, with template "RSYSLOG_FileFormat" : +``` console +module(load="omprog") +[...] +mail.info /var/log/maillog +& action( + type="omprog" + binary="/usr/local/bin/postfix-log-parser -f -o /var/log/maillog.json" + template="RSYSLOG_FileFormat") + +& stop + +``` + + ## Library usage ``` From 0d25a65b03b30777f0c213e9aa634b8b33f60b88 Mon Sep 17 00:00:00 2001 From: yo Date: Thu, 4 Feb 2021 22:09:26 +0100 Subject: [PATCH 013/100] Support sasl auth + fixed useless capturing group in ProcessRegexpFormat --- postfix-log-parser.go | 24 ++++++++++++++---------- postfix-log-parser/cmd/root.go | 8 ++++++++ 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/postfix-log-parser.go b/postfix-log-parser.go index d8e5f85..367e305 100644 --- a/postfix-log-parser.go +++ b/postfix-log-parser.go @@ -11,9 +11,9 @@ const ( TimeFormatISO8601 = "2006-01-02T15:04:05.999999-07:00" TimeRegexpFormat = `([A-Za-z]{3}\s*[0-9]{1,2} [0-9]{2}:[0-9]{2}:[0-9]{2}|^\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+(?:[+-][0-2]\d:[0-5]\d|Z))` HostRegexpFormat = `([0-9A-Za-z\-\.]*)` - ProcessRegexpFormat = `(postfix.*(\/[a-z]*)+\[[0-9]{1,5}\])?` + ProcessRegexpFormat = `(postfix.*(?:\/[a-z]*)+\[[0-9]{1,5}\])?` QueueIdRegexpFormat = `([0-9A-Z]*)` - MessageDetailsRegexpFormat = `((?:client=(.+)\[(.+)\])?(?:message-id=<(.+)>)?(?:from=<(.+@.+)>)?(?:to=<(.+@.+)>.*status=([a-z]+))?.*)` + MessageDetailsRegexpFormat = `((?:client=(.+)\[(.+)\](?:, sasl_method=(.+), sasl_username=(.+))?)?(?:message-id=<(.+)>)?(?:from=<(.+@.+)>)?(?:to=<(.+@.+)>.*status=([a-z]+))?.*)` RegexpFormat = TimeRegexpFormat + ` ` + HostRegexpFormat + ` ` + ProcessRegexpFormat + `:? ` + QueueIdRegexpFormat + `(?:\: )?` + MessageDetailsRegexpFormat ) @@ -31,6 +31,8 @@ type ( Messages string `json:"messages"` ClientHostname string `json:"client_hostname"` ClinetIp string `json:"client_ip"` + SaslMethod string `json:"sasl_method"` + SaslUsername string `json:"sasl_username"` MessageId string `json:"message_id"` From string `json:"from"` To string `json:"to"` @@ -64,14 +66,16 @@ func (p *PostfixLog) Parse(text []byte) (LogFormat, error) { Time: &t, Hostname: string(group[2]), Process: string(group[3]), - QueueId: string(group[5]), - Messages: string(group[6]), - ClientHostname: string(group[7]), - ClinetIp: string(group[8]), - MessageId: string(group[9]), - From: string(group[10]), - To: string(group[11]), - Status: string(group[12]), + QueueId: string(group[4]), + Messages: string(group[5]), + ClientHostname: string(group[6]), + ClinetIp: string(group[7]), + SaslMethod: string(group[8]), + SaslUsername: string(group[9]), + MessageId: string(group[10]), + From: string(group[11]), + To: string(group[12]), + Status: string(group[13]), } return logFormat, nil diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index 2c6fa1a..9d28040 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -22,6 +22,8 @@ type ( QueueId string `json:"queue_id"` ClientHostname string `json:"client_hostname"` ClinetIp string `json:"client_ip"` + SaslMethod string `json:"sasl_method"` + SaslUsername string `json:"sasl_username"` MessageId string `json:"message_id"` From string `json:"from"` Messages []Message `json:"messages"` @@ -41,6 +43,8 @@ type ( QueueId string `json:"queue_id"` ClientHostname string `json:"client_hostname"` ClinetIp string `json:"client_ip"` + SaslMethod string `json:"sasl_method"` + SaslUsername string `json:"sasl_username"` MessageId string `json:"message_id"` From string `json:"from"` TimeSent *time.Time `json:"time_sent"` @@ -61,6 +65,8 @@ func PlpToFlat(plp *PostfixLogParser) []PostfixLogParserFlat { QueueId: plp.QueueId, ClientHostname: plp.ClientHostname, ClinetIp: plp.ClinetIp, + SaslMethod: plp.SaslMethod, + SaslUsername: plp.SaslUsername, MessageId: plp.MessageId, From: plp.From, TimeSent: plp.Messages[i].Time, @@ -137,6 +143,8 @@ func NewCmdRoot() *cobra.Command { QueueId: logFormat.QueueId, ClientHostname: logFormat.ClientHostname, ClinetIp: logFormat.ClinetIp, + SaslMethod: logFormat.SaslMethod, + SaslUsername: logFormat.SaslUsername, } } From 5b195898b63f7cfe70ac40e8001c8c0f2131f0f0 Mon Sep 17 00:00:00 2001 From: yo Date: Thu, 4 Feb 2021 22:12:41 +0100 Subject: [PATCH 014/100] Update README --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index f791bd4..8404937 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,8 @@ Input postfix logs as os stdin. "queue_id": "3D74ADB7400B", "client_hostname": "example.com", "client_ip": "127.0.0.1", + "sasl_method": "PLAIN", + "sasl_username": "test2@smtp.example.info", "message_id": "f93388828093534f92d85ffe21b2a719@example.info", "from": "test2@example.info", "messages": [ @@ -53,6 +55,8 @@ Use -f flag to flatten json structure: "queue_id": "3D74ADB7400B", "client_hostname": "example.com", "client_ip": "127.0.0.1", + "sasl_method": "PLAIN", + "sasl_username": "test2@smtp.example.info", "message_id": "f93388828093534f92d85ffe21b2a719@example.info", "from": "test2@example.info", "time_sent": "0000-10-10T15:59:30+09:00", @@ -67,6 +71,8 @@ Use -f flag to flatten json structure: "queue_id": "3D74ADB7400B", "client_hostname": "example.com", "client_ip": "127.0.0.1", + "sasl_method": "PLAIN", + "sasl_username": "test2@smtp.example.info", "message_id": "f93388828093534f92d85ffe21b2a719@example.info", "from": "test2@example.info", "time_sent": "0000-10-10T15:59:30+09:00", @@ -131,6 +137,8 @@ postfixlog.LogFormat{ Messages: "to=, relay=mail.example-to.com[192.168.0.10]:25, delay=5.3, delays=0.26/0/0.31/4.7, dsn=2.0.0, status=sent (250 2.0.0 Ok: queued as C598F1B0002D)", ClientHostname: "", ClinetIp: "", + SaslMethod: "", + SaslUsername: "", MessageId: "", From: "", To: "test@example-to.com", From e925489dae9b302ec85fce195b668692ad00545e Mon Sep 17 00:00:00 2001 From: yo Date: Thu, 4 Feb 2021 22:29:25 +0100 Subject: [PATCH 015/100] Add size and nrcpt values --- README.md | 8 ++++++++ postfix-log-parser.go | 10 +++++++--- postfix-log-parser/cmd/root.go | 10 +++++++++- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 8404937..88ee1a5 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,8 @@ Input postfix logs as os stdin. "sasl_username": "test2@smtp.example.info", "message_id": "f93388828093534f92d85ffe21b2a719@example.info", "from": "test2@example.info", + "size": "1988", + "nrcpt": "1", "messages": [ { "time": "0000-10-10T15:59:30+09:00", @@ -59,6 +61,8 @@ Use -f flag to flatten json structure: "sasl_username": "test2@smtp.example.info", "message_id": "f93388828093534f92d85ffe21b2a719@example.info", "from": "test2@example.info", + "size": "1988", + "nrcpt": "1", "time_sent": "0000-10-10T15:59:30+09:00", "to": "test@example.to", "status": "sent", @@ -75,6 +79,8 @@ Use -f flag to flatten json structure: "sasl_username": "test2@smtp.example.info", "message_id": "f93388828093534f92d85ffe21b2a719@example.info", "from": "test2@example.info", + "size": "1988", + "nrcpt": "1", "time_sent": "0000-10-10T15:59:30+09:00", "to": "test2@example.to", "status": "sent", @@ -141,6 +147,8 @@ postfixlog.LogFormat{ SaslUsername: "", MessageId: "", From: "", + Size: "", + NRcpt: "", To: "test@example-to.com", Status: "sent", } diff --git a/postfix-log-parser.go b/postfix-log-parser.go index 367e305..4f24e15 100644 --- a/postfix-log-parser.go +++ b/postfix-log-parser.go @@ -13,7 +13,7 @@ const ( HostRegexpFormat = `([0-9A-Za-z\-\.]*)` ProcessRegexpFormat = `(postfix.*(?:\/[a-z]*)+\[[0-9]{1,5}\])?` QueueIdRegexpFormat = `([0-9A-Z]*)` - MessageDetailsRegexpFormat = `((?:client=(.+)\[(.+)\](?:, sasl_method=(.+), sasl_username=(.+))?)?(?:message-id=<(.+)>)?(?:from=<(.+@.+)>)?(?:to=<(.+@.+)>.*status=([a-z]+))?.*)` + MessageDetailsRegexpFormat = `((?:client=(.+)\[(.+)\](?:, sasl_method=(.+), sasl_username=(.+))?)?(?:message-id=<(.+)>)?(?:from=<(.+@.+)>(?:, size=(\d+), nrcpt=(\d+))?)?(?:to=<(.+@.+)>.*status=([a-z]+))?.*)` RegexpFormat = TimeRegexpFormat + ` ` + HostRegexpFormat + ` ` + ProcessRegexpFormat + `:? ` + QueueIdRegexpFormat + `(?:\: )?` + MessageDetailsRegexpFormat ) @@ -35,6 +35,8 @@ type ( SaslUsername string `json:"sasl_username"` MessageId string `json:"message_id"` From string `json:"from"` + Size string `json:"size"` + NRcpt string `json:"nrcpt"` To string `json:"to"` Status string `json:"status"` } @@ -74,8 +76,10 @@ func (p *PostfixLog) Parse(text []byte) (LogFormat, error) { SaslUsername: string(group[9]), MessageId: string(group[10]), From: string(group[11]), - To: string(group[12]), - Status: string(group[13]), + Size: string(group[12]), + NRcpt: string(group[13]), + To: string(group[14]), + Status: string(group[15]), } return logFormat, nil diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index 9d28040..dff8e8e 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -26,6 +26,8 @@ type ( SaslUsername string `json:"sasl_username"` MessageId string `json:"message_id"` From string `json:"from"` + Size string `json:"size"` + NRcpt string `json:"nrcpt"` Messages []Message `json:"messages"` } @@ -47,6 +49,8 @@ type ( SaslUsername string `json:"sasl_username"` MessageId string `json:"message_id"` From string `json:"from"` + Size string `json:"size"` + NRcpt string `json:"nrcpt"` TimeSent *time.Time `json:"time_sent"` To string `json:"to"` Status string `json:"status"` @@ -69,6 +73,8 @@ func PlpToFlat(plp *PostfixLogParser) []PostfixLogParserFlat { SaslUsername: plp.SaslUsername, MessageId: plp.MessageId, From: plp.From, + Size: plp.Size, + NRcpt: plp.NRcpt, TimeSent: plp.Messages[i].Time, To: plp.Messages[i].To, Status: plp.Messages[i].Status, @@ -133,7 +139,7 @@ func NewCmdRoot() *cobra.Command { } /* - Oct 10 04:02:02 mail.example.com postfix/smtpd[22941]: DFBEFDBF00C5: client=example.net[127.0.0.1] + Oct 10 04:02:02 mail.example.com postfix/smtpd[22941]: DFBEFDBF00C5: client=example.net[127.0.0.1], sasl_method=PLAIN, sasl_username=user@example.com */ if logFormat.ClientHostname != "" { m[logFormat.QueueId] = &PostfixLogParser{ @@ -163,6 +169,8 @@ func NewCmdRoot() *cobra.Command { if logFormat.From != "" { if plp, ok := m[logFormat.QueueId]; ok { plp.From = logFormat.From + plp.Size = logFormat.Size + plp.NRcpt = logFormat.NRcpt } } From 751c404baba70c40cbbd01a184996b91618b7908 Mon Sep 17 00:00:00 2001 From: yo Date: Fri, 5 Feb 2021 13:49:15 +0100 Subject: [PATCH 016/100] Renamed json field "timestamp" to "time" --- postfix-log-parser/cmd/root.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index dff8e8e..6a5778f 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -39,7 +39,7 @@ type ( } PostfixLogParserFlat struct { - Time *time.Time `json:"timestamp"` + Time *time.Time `json:"time"` Hostname string `json:"hostname"` Process string `json:"process"` QueueId string `json:"queue_id"` From a3fe494ccdc7c10957c9f8b559c7a13ef3fba4b3 Mon Sep 17 00:00:00 2001 From: yo Date: Fri, 5 Feb 2021 16:37:06 +0100 Subject: [PATCH 017/100] Support syslog format (frame starting with ) --- postfix-log-parser.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/postfix-log-parser.go b/postfix-log-parser.go index 4f24e15..c2794dd 100644 --- a/postfix-log-parser.go +++ b/postfix-log-parser.go @@ -7,14 +7,15 @@ import ( ) const ( + SyslogPri = `(?:<\d{1,3}>)?` TimeFormat = "Jan 2 15:04:05" TimeFormatISO8601 = "2006-01-02T15:04:05.999999-07:00" - TimeRegexpFormat = `([A-Za-z]{3}\s*[0-9]{1,2} [0-9]{2}:[0-9]{2}:[0-9]{2}|^\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+(?:[+-][0-2]\d:[0-5]\d|Z))` + TimeRegexpFormat = `([A-Za-z]{3}\s*[0-9]{1,2} [0-9]{2}:[0-9]{2}:[0-9]{2}|^\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d(?:\.\d+)?(?:[+-][0-2]\d:[0-5]\d|Z))` HostRegexpFormat = `([0-9A-Za-z\-\.]*)` ProcessRegexpFormat = `(postfix.*(?:\/[a-z]*)+\[[0-9]{1,5}\])?` QueueIdRegexpFormat = `([0-9A-Z]*)` MessageDetailsRegexpFormat = `((?:client=(.+)\[(.+)\](?:, sasl_method=(.+), sasl_username=(.+))?)?(?:message-id=<(.+)>)?(?:from=<(.+@.+)>(?:, size=(\d+), nrcpt=(\d+))?)?(?:to=<(.+@.+)>.*status=([a-z]+))?.*)` - RegexpFormat = TimeRegexpFormat + ` ` + HostRegexpFormat + ` ` + ProcessRegexpFormat + `:? ` + QueueIdRegexpFormat + `(?:\: )?` + MessageDetailsRegexpFormat + RegexpFormat = SyslogPri + TimeRegexpFormat + ` ` + HostRegexpFormat + ` ` + ProcessRegexpFormat + `:? ` + QueueIdRegexpFormat + `(?:\: )?` + MessageDetailsRegexpFormat ) type ( From 9f4797c01addce1ad4d0fb0343a2e72f69c712c9 Mon Sep 17 00:00:00 2001 From: yo Date: Fri, 5 Feb 2021 20:27:05 +0100 Subject: [PATCH 018/100] Get bounce Queue ID --- postfix-log-parser.go | 12 +++++++-- postfix-log-parser/cmd/root.go | 45 ++++++++++++++++++++++++++++------ 2 files changed, 47 insertions(+), 10 deletions(-) diff --git a/postfix-log-parser.go b/postfix-log-parser.go index c2794dd..2899d9a 100644 --- a/postfix-log-parser.go +++ b/postfix-log-parser.go @@ -14,8 +14,14 @@ const ( HostRegexpFormat = `([0-9A-Za-z\-\.]*)` ProcessRegexpFormat = `(postfix.*(?:\/[a-z]*)+\[[0-9]{1,5}\])?` QueueIdRegexpFormat = `([0-9A-Z]*)` - MessageDetailsRegexpFormat = `((?:client=(.+)\[(.+)\](?:, sasl_method=(.+), sasl_username=(.+))?)?(?:message-id=<(.+)>)?(?:from=<(.+@.+)>(?:, size=(\d+), nrcpt=(\d+))?)?(?:to=<(.+@.+)>.*status=([a-z]+))?.*)` - RegexpFormat = SyslogPri + TimeRegexpFormat + ` ` + HostRegexpFormat + ` ` + ProcessRegexpFormat + `:? ` + QueueIdRegexpFormat + `(?:\: )?` + MessageDetailsRegexpFormat + ClientRegexpFormat = `(?:client=(.+)\[(.+)\](?:, sasl_method=(.+), sasl_username=(.+))?)?` + MessageIdRegexpFormat = `(?:message-id=<(.+)>)?` + FromRegexpFormat = `(?:from=<(.+@.+)>(?:, size=(\d+), nrcpt=(\d+))?)?` + ToRegexpFormat = `(?:to=<(.+@.+)>.*status=([a-z]+))?` + SenderNDNRegexpFormat = `(?:sender non-delivery notification: ([0-9A-Z]*))?` + MessageDetailsRegexpFormat = `(` + ClientRegexpFormat + MessageIdRegexpFormat + FromRegexpFormat + ToRegexpFormat + SenderNDNRegexpFormat + `.*)` + //MessageDetailsRegexpFormat = `((?:client=(.+)\[(.+)\](?:, sasl_method=(.+), sasl_username=(.+))?)?(?:message-id=<(.+)>)?(?:from=<(.+@.+)>(?:, size=(\d+), nrcpt=(\d+))?)?(?:to=<(.+@.+)>.*status=([a-z]+))?(?:sender non-delivery notification: ([0-9A-Z]*))?.*)` + RegexpFormat = SyslogPri + TimeRegexpFormat + ` ` + HostRegexpFormat + ` ` + ProcessRegexpFormat + `:? ` + QueueIdRegexpFormat + `(?:\: )?` + MessageDetailsRegexpFormat ) type ( @@ -40,6 +46,7 @@ type ( NRcpt string `json:"nrcpt"` To string `json:"to"` Status string `json:"status"` + BounceId string `json:"bounce_id"` } ) @@ -81,6 +88,7 @@ func (p *PostfixLog) Parse(text []byte) (LogFormat, error) { NRcpt: string(group[13]), To: string(group[14]), Status: string(group[15]), + BounceId: string(group[16]), } return logFormat, nil diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index 6a5778f..78bde5c 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -32,10 +32,11 @@ type ( } Message struct { - Time *time.Time `json:"time"` - To string `json:"to"` - Status string `json:"status"` - Message string `json:"message"` + Time *time.Time `json:"time"` + To string `json:"to"` + Status string `json:"status"` + Message string `json:"message"` + BounceId string `json:"bounce_id"` } PostfixLogParserFlat struct { @@ -55,6 +56,7 @@ type ( To string `json:"to"` Status string `json:"status"` Message string `json:"message"` + BounceId string `json:"bounce_id"` } ) @@ -79,6 +81,7 @@ func PlpToFlat(plp *PostfixLogParser) []PostfixLogParserFlat { To: plp.Messages[i].To, Status: plp.Messages[i].Status, Message: plp.Messages[i].Message, + BounceId: plp.Messages[i].BounceId, } } @@ -180,16 +183,42 @@ func NewCmdRoot() *cobra.Command { if logFormat.To != "" { if plp, ok := m[logFormat.QueueId]; ok { message := Message{ - Time: logFormat.Time, - To: logFormat.To, - Status: logFormat.Status, - Message: logFormat.Messages, + Time: logFormat.Time, + To: logFormat.To, + Status: logFormat.Status, + Message: logFormat.Messages, + BounceId: "", } plp.Messages = append(plp.Messages, message) } } + /* + 2021-02-05T17:25:03+01:00 mail.example.com postfix/bounce[39258]: 006B056E6: sender non-delivery notification: 642E456E9 + */ + if logFormat.BounceId != "" { + if plp, ok := m[logFormat.QueueId]; ok { + // Get the matching Message by Status=bounced + for i, msg := range plp.Messages { + //fmt.Println("Dans le parcours message de ", plp.QueueId) + // Need to manage more than one bounce for the same queue_id. This is flawy as we just rely on order to match + if msg.Status == "bounced" && len(msg.BounceId) == 0 { + message := Message{ + Time: msg.Time, + To: msg.To, + Status: msg.Status, + Message: msg.Message, + BounceId: logFormat.BounceId, + } + // Delete old message, put new at the end + copy(plp.Messages[i:], plp.Messages[i+1:]) + plp.Messages[len(plp.Messages)-1] = message + break + } + } + } + } /* Oct 10 04:02:08 mail.example.com postfix/qmgr[18719]: DFBEFDBF00C5: removed */ From f8c3e94c4b95818970277294f5fc6b9017e3f23b Mon Sep 17 00:00:00 2001 From: yo Date: Fri, 5 Feb 2021 23:01:13 +0100 Subject: [PATCH 019/100] Support milter-reject --- postfix-log-parser.go | 48 ++++++++++++++++----------- postfix-log-parser/cmd/root.go | 60 ++++++++++++++++++++-------------- 2 files changed, 65 insertions(+), 43 deletions(-) diff --git a/postfix-log-parser.go b/postfix-log-parser.go index 2899d9a..2e70bd9 100644 --- a/postfix-log-parser.go +++ b/postfix-log-parser.go @@ -19,9 +19,9 @@ const ( FromRegexpFormat = `(?:from=<(.+@.+)>(?:, size=(\d+), nrcpt=(\d+))?)?` ToRegexpFormat = `(?:to=<(.+@.+)>.*status=([a-z]+))?` SenderNDNRegexpFormat = `(?:sender non-delivery notification: ([0-9A-Z]*))?` - MessageDetailsRegexpFormat = `(` + ClientRegexpFormat + MessageIdRegexpFormat + FromRegexpFormat + ToRegexpFormat + SenderNDNRegexpFormat + `.*)` - //MessageDetailsRegexpFormat = `((?:client=(.+)\[(.+)\](?:, sasl_method=(.+), sasl_username=(.+))?)?(?:message-id=<(.+)>)?(?:from=<(.+@.+)>(?:, size=(\d+), nrcpt=(\d+))?)?(?:to=<(.+@.+)>.*status=([a-z]+))?(?:sender non-delivery notification: ([0-9A-Z]*))?.*)` - RegexpFormat = SyslogPri + TimeRegexpFormat + ` ` + HostRegexpFormat + ` ` + ProcessRegexpFormat + `:? ` + QueueIdRegexpFormat + `(?:\: )?` + MessageDetailsRegexpFormat + MilterRejectRegexpFormat = `(?:milter-reject: .* from (.+)\[(.+)\]: .*from=<(.+@.+)> to=<(.+@.+)> .*)?` + MessageDetailsRegexpFormat = `(` + ClientRegexpFormat + MessageIdRegexpFormat + FromRegexpFormat + ToRegexpFormat + SenderNDNRegexpFormat + MilterRejectRegexpFormat + `.*)` + RegexpFormat = SyslogPri + TimeRegexpFormat + ` ` + HostRegexpFormat + ` ` + ProcessRegexpFormat + `:? ` + QueueIdRegexpFormat + `(?:\: )?` + MessageDetailsRegexpFormat ) type ( @@ -73,22 +73,32 @@ func (p *PostfixLog) Parse(text []byte) (LogFormat, error) { } logFormat := LogFormat{ - Time: &t, - Hostname: string(group[2]), - Process: string(group[3]), - QueueId: string(group[4]), - Messages: string(group[5]), - ClientHostname: string(group[6]), - ClinetIp: string(group[7]), - SaslMethod: string(group[8]), - SaslUsername: string(group[9]), - MessageId: string(group[10]), - From: string(group[11]), - Size: string(group[12]), - NRcpt: string(group[13]), - To: string(group[14]), - Status: string(group[15]), - BounceId: string(group[16]), + Time: &t, + Hostname: string(group[2]), + Process: string(group[3]), + QueueId: string(group[4]), + Messages: string(group[5]), + SaslMethod: string(group[8]), + SaslUsername: string(group[9]), + MessageId: string(group[10]), + Size: string(group[12]), + NRcpt: string(group[13]), + BounceId: string(group[16]), + } + + // Milter reject put values far in the group + if len(group[17]) > 0 { + logFormat.Status = "milter-reject" + logFormat.ClientHostname = string(group[17]) + logFormat.ClinetIp = string(group[18]) + logFormat.From = string(group[19]) + logFormat.To = string(group[20]) + } else { + logFormat.Status = string(group[15]) + logFormat.ClientHostname = string(group[6]) + logFormat.ClinetIp = string(group[7]) + logFormat.From = string(group[11]) + logFormat.To = string(group[14]) } return logFormat, nil diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index 78bde5c..75170fc 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -88,10 +88,32 @@ func PlpToFlat(plp *PostfixLogParser) []PostfixLogParserFlat { return plpf } +func NewWriter(file string) (*bufio.Writer, error) { + var wtr *bufio.Writer + + if len(file) > 0 { + var f *os.File + var err error + if _, err = os.Stat(file); err == nil { + f, err = os.OpenFile(file, os.O_APPEND|os.O_WRONLY, 0640) + } else if os.IsNotExist(err) { + f, err = os.OpenFile(file, os.O_CREATE|os.O_WRONLY, 0640) + } + if err != nil { + return nil, err + } + wtr = bufio.NewWriter(f) + defer f.Close() + } else { + wtr = bufio.NewWriter(os.Stdout) + } + + return wtr, nil +} + func NewCmdRoot() *cobra.Command { var flatten bool var outputFile string - var wtr *bufio.Writer cmd := &cobra.Command{ Use: "postfix-log-parser", @@ -105,24 +127,12 @@ func NewCmdRoot() *cobra.Command { // initialize p := postfixlog.NewPostfixLog() - // writer, either file or stdout - if len(outputFile) > 0 { - var f *os.File - var err error - if _, err = os.Stat(outputFile); err == nil { - f, err = os.OpenFile(outputFile, os.O_APPEND|os.O_WRONLY, 0640) - } else if os.IsNotExist(err) { - f, err = os.OpenFile(outputFile, os.O_CREATE|os.O_WRONLY, 0640) - } - if err != nil { - cmd.SetOutput(os.Stderr) - cmd.Println(err) - os.Exit(1) - } - wtr = bufio.NewWriter(f) - defer f.Close() - } else { - wtr = bufio.NewWriter(os.Stdout) + // Get a writer, file or stdout + writer, err := NewWriter(outputFile) + if err != nil { + cmd.SetOutput(os.Stderr) + cmd.Println(err) + os.Exit(1) } // input stdin @@ -221,9 +231,11 @@ func NewCmdRoot() *cobra.Command { } /* Oct 10 04:02:08 mail.example.com postfix/qmgr[18719]: DFBEFDBF00C5: removed + or + 2021-02-05T14:17:51+01:00 smtp.server.com postfix/cleanup[38982]: D8C136A3A: milter-reject: END-OF-MESSAGE from unknown[1.2.3.4]: 4.7.1 Greylisting in action, try again later; from= to= proto=ESMTP helo= */ // "removed" message is end of logs. then flush. - if logFormat.Messages == "removed" { + if logFormat.Messages == "removed" || logFormat.Status == "milter-reject" { if plp, ok := m[logFormat.QueueId]; ok { if flatten { // Flatten the structure, then print each message @@ -232,16 +244,16 @@ func NewCmdRoot() *cobra.Command { if err != nil { log.Fatal(err) } - fmt.Fprintln(wtr, string(jsonBytes)) - wtr.Flush() + fmt.Fprintln(writer, string(jsonBytes)) + writer.Flush() } } else { jsonBytes, err := json.Marshal(plp) if err != nil { log.Fatal(err) } - fmt.Fprintln(wtr, string(jsonBytes)) - wtr.Flush() + fmt.Fprintln(writer, string(jsonBytes)) + writer.Flush() } } } From d10dd501c4a73ab220a46ce2d246a47856bcb4b3 Mon Sep 17 00:00:00 2001 From: yo Date: Sat, 6 Feb 2021 11:15:04 +0100 Subject: [PATCH 020/100] Bugfix : last commit broke writing to file --- postfix-log-parser/cmd/root.go | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index 75170fc..cef4d0f 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -88,7 +88,7 @@ func PlpToFlat(plp *PostfixLogParser) []PostfixLogParserFlat { return plpf } -func NewWriter(file string) (*bufio.Writer, error) { +func NewWriter(file string) (*bufio.Writer, *os.File, error) { var wtr *bufio.Writer if len(file) > 0 { @@ -100,15 +100,14 @@ func NewWriter(file string) (*bufio.Writer, error) { f, err = os.OpenFile(file, os.O_CREATE|os.O_WRONLY, 0640) } if err != nil { - return nil, err + return nil, nil, err } wtr = bufio.NewWriter(f) - defer f.Close() + return wtr, f, nil } else { wtr = bufio.NewWriter(os.Stdout) + return wtr, nil, nil } - - return wtr, nil } func NewCmdRoot() *cobra.Command { @@ -128,7 +127,7 @@ func NewCmdRoot() *cobra.Command { p := postfixlog.NewPostfixLog() // Get a writer, file or stdout - writer, err := NewWriter(outputFile) + writer, file, err := NewWriter(outputFile) if err != nil { cmd.SetOutput(os.Stderr) cmd.Println(err) @@ -211,7 +210,6 @@ func NewCmdRoot() *cobra.Command { if plp, ok := m[logFormat.QueueId]; ok { // Get the matching Message by Status=bounced for i, msg := range plp.Messages { - //fmt.Println("Dans le parcours message de ", plp.QueueId) // Need to manage more than one bounce for the same queue_id. This is flawy as we just rely on order to match if msg.Status == "bounced" && len(msg.BounceId) == 0 { message := Message{ @@ -259,6 +257,9 @@ func NewCmdRoot() *cobra.Command { } } + if file != nil { + file.Close() + } }, } From 8cc2565ac149c433e58e27dd84a474428817e8a7 Mon Sep 17 00:00:00 2001 From: yo Date: Sat, 6 Feb 2021 11:35:57 +0100 Subject: [PATCH 021/100] Moar tests --- test/test-iso8601.log | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/test/test-iso8601.log b/test/test-iso8601.log index 433315f..82f790c 100644 --- a/test/test-iso8601.log +++ b/test/test-iso8601.log @@ -20,3 +20,23 @@ 2020-01-30T06:26:08.404714+09:00 mail postfix/qmgr[18719]: 6526CDB7400B: removed 2020-01-30T06:26:09.404714+09:00 mail postfix/smtp[1874]: C6E0DDB74006: to=, relay=example.ddd[192.168.0.30]:25, delay=3.4, delays=0.11/0/0.38/2.9, dsn=2.0.0, status=sent (250 2.0.0 OK 1539154772 az9-v6si5976496plb.190 - gsmtp) 2020-01-30T06:26:09.404714+09:00 mail postfix/qmgr[18719]: C6E0DDB74006: removed +2021-02-06T10:57:57+01:00 mail.server.com postfix/smtpd[59633]: D6DDA23B0F: client=unknown[99.88.77.66] +2021-02-06T10:57:57+01:00 mail.server.com postfix/cleanup[59586]: D6DDA23B0F: message-id=<20210206095814.2B34E19A19F6@host.sender.domain> +2021-02-06T10:57:58+01:00 mail.server.com postfix/cleanup[59586]: D6DDA23B0F: milter-reject: END-OF-MESSAGE from unknown[99.88.77.66]: 4.7.1 Greylisting in action, try again later; from= to= proto=ESMTP helo= +2021-02-06T09:58:59.184925+01:00 mail.server.com postfix/submission/smtpd[99525] 2D19728491: client=unknown[10.11.12.13], sasl_method=PLAIN, sasl_username=user1@domain.example.com +2021-02-06T09:58:59.194384+01:00 mail.server.com postfix/cleanup[7821] 2D19728491: replace: header Received: from localhost (unknown [10.11.12.13])??(using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits)?? key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256)??(N from unknown[10.11.12.13]; from= to= proto=ESMTP helo=: Received: from localhost (unknown [10.11.12.13])??(using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits)?? key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256)??(No client certificate requested)??(Authenticated sender: user1)??by mail.server.com (Postfix) with ESMTPSA id 2D19728491??for ; Sat, 6 Feb 2021 09:58:49 +0100 (CET) +2021-02-06T09:58:59.194419+01:00 mail.server.com postfix/cleanup[7821] 2D19728491: message-id=<1612601937133741374.16589280196177750880@user1.domain.example.com> +2021-02-06T09:58:59.401801+01:00 mail.server.com postfix/qmgr[9086] 2D19728491: from=, size=11991, nrcpt=1 (queue active) +2021-02-06T09:58:59.560624+01:00 mail.server.com postfix/smtp[8160] 2D19728491: to=, relay=mail2.domain.com[2b00:128:64::1]:25, delay=9.7, delays=9.5/0.01/0.03/0.12, dsn=2.0.0, status=sent (250 2.0.0 Ok: queued as 702A02A5BF) +2021-02-06T09:58:59.560956+01:00 mail.server.com postfix/qmgr[9086] 2D19728491: removed +2021-02-06T11:19:18.130932+01:00 mail2.server.com postfix/smtpd[61691] 1FEE23DED3: client=unknown[2b00:128:64::42] +2021-02-06T11:19:18.131299+01:00 mail2.server.com postfix/cleanup[51214] 1FEE23DED3: message-id=<20210206101916.672CC1B4E5@sender.example.com> +2021-02-06T11:19:18.132307+01:00 mail2.server.com postfix/qmgr[10190] 1FEE23DED3: from=<>, size=22738, nrcpt=1 (queue active) +2021-02-06T11:19:18.189624+01:00 mail2.server.com postfix/smtp[63135] 1FEE23DED3: to=, relay=relay.domain.com[22.33.44.55]:25, delay=0.06, delays=0/0/0.05/0.01, dsn=5.7.1, status=bounced (host relay.domain.com[22.33.44.55] said: 554 5.7.1 : Relay access denied (in reply to RCPT TO command)) +2021-02-06T11:19:18.190959+01:00 mail2.server.com postfix/qmgr[10190] 1FEE23DED3: removed +2021-02-06T11:19:25+01:00 mail1.example.com postfix/smtpd[64346]: 42E683DD0B: client=mail.sender.com[85.42.66.4] +2021-02-06T11:19:25+01:00 mail1.example.com postfix/cleanup[64327]: 42E683DD0B: message-id=<0tgs2nv56aexq9wn-dz6hijmzyfbesxxz-bcd8-493db@some.com> +2021-02-06T11:19:25+01:00 mail1.example.com postfix/qmgr[31205]: 42E683DD0B: from=<18546-55426-655542-8520-recipient1=sender1@mail.some.com>, size=8703, nrcpt=1 (queue active) +2021-02-06T11:19:26+01:00 mail1.example.com postfix/smtp[63622]: 42E683DD0B: to=, relay=aspmx.l.google.com[2a00:1450:400c:c04::1b]:25, delay=0.59, delays=0.23/0/0.17/0.19, dsn=5.7.26, status=bounced (host aspmx.l.google.com[2a00:1450:400c:c04::1b] said: 550-5.7.26 This message does not have authentication information or fails to 550-5.7.26 pass authentication checks. To best protect our users from spam, the 550-5.7.26 message has been blocked. Please visit 550-5.7.26 https://support.google.com/mail/answer/81126#authentication for more 550 5.7.26 information. a42si85499772wrx.219 - gsmtp (in reply to end of DATA command)) +2021-02-06T11:19:26+01:00 mail1.example.com postfix/bounce[64352]: 42E683DD0B: sender non-delivery notification: 1F99E4BC9A +2021-02-06T11:19:26+01:00 mail1.example.com postfix/qmgr[31205]: 42E683DD0B: removed From dd723e8d947abc8834e9d2fef7976a961c434366 Mon Sep 17 00:00:00 2001 From: yo Date: Mon, 8 Feb 2021 22:00:09 +0100 Subject: [PATCH 022/100] Daemonizing : Create PID file, recreate output when receiving SIGUSR1 (for newsyslog integration) --- postfix-log-parser/cmd/root.go | 95 +++++++++++++++++++++++++++++----- 1 file changed, 81 insertions(+), 14 deletions(-) diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index cef4d0f..9df84af 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -6,9 +6,13 @@ import ( "fmt" "log" "os" + "os/signal" + "sync" + "syscall" "time" "github.com/spf13/cobra" + "github.com/tabalt/pidfile" postfixlog "github.com/yo000/postfix-log-parser" ) @@ -60,6 +64,9 @@ type ( } ) +var File os.File +var Writer *bufio.Writer + func PlpToFlat(plp *PostfixLogParser) []PostfixLogParserFlat { var plpf = make([]PostfixLogParserFlat, len(plp.Messages)) @@ -89,8 +96,6 @@ func PlpToFlat(plp *PostfixLogParser) []PostfixLogParserFlat { } func NewWriter(file string) (*bufio.Writer, *os.File, error) { - var wtr *bufio.Writer - if len(file) > 0 { var f *os.File var err error @@ -102,17 +107,42 @@ func NewWriter(file string) (*bufio.Writer, *os.File, error) { if err != nil { return nil, nil, err } - wtr = bufio.NewWriter(f) - return wtr, f, nil + Writer = bufio.NewWriter(f) + return Writer, f, nil } else { - wtr = bufio.NewWriter(os.Stdout) - return wtr, nil, nil + Writer = bufio.NewWriter(os.Stdout) + return Writer, nil, nil + } +} + +//func writeOut(wrt *bufio.Writer, msg string, filename string, m sync.Mutex) error { +func writeOut(msg string, filename string, m sync.Mutex) error { + if len(filename) > 0 { + m.Lock() + } + _, err := fmt.Fprintln(Writer, msg) + if err != nil { + if len(filename) > 0 { + m.Unlock() + } + return err + } + err = Writer.Flush() + if len(filename) > 0 { + m.Unlock() + } + if err != nil { + return err } + + return nil } func NewCmdRoot() *cobra.Command { var flatten bool var outputFile string + var pidfilepath string + var mtx sync.Mutex cmd := &cobra.Command{ Use: "postfix-log-parser", @@ -120,6 +150,14 @@ func NewCmdRoot() *cobra.Command { //Long: ``, Run: func(cmd *cobra.Command, args []string) { + if len(pidfilepath) > 0 { + if pid, err := pidfile.Create(pidfilepath); err != nil { + log.Fatal(err) + } else { + defer pid.Clear() + } + } + // create queue m := make(map[string]*PostfixLogParser) @@ -127,13 +165,34 @@ func NewCmdRoot() *cobra.Command { p := postfixlog.NewPostfixLog() // Get a writer, file or stdout - writer, file, err := NewWriter(outputFile) + Writer, File, err := NewWriter(outputFile) if err != nil { cmd.SetOutput(os.Stderr) cmd.Println(err) os.Exit(1) } + // Manage output file rotation when receiving SIGUSR1 + if len(outputFile) > 0 { + sig := make(chan os.Signal) + signal.Notify(sig, syscall.SIGUSR1) + go func() { + for { + <-sig + fmt.Println("SIGUSR1 received, recreate output file") + mtx.Lock() + File.Close() + Writer, File, err = NewWriter(outputFile) + if err != nil { + cmd.SetOutput(os.Stderr) + cmd.Println(err) + os.Exit(1) + } + mtx.Unlock() + } + }() + } + // input stdin scanner := bufio.NewScanner(os.Stdin) for scanner.Scan() { @@ -242,29 +301,37 @@ func NewCmdRoot() *cobra.Command { if err != nil { log.Fatal(err) } - fmt.Fprintln(writer, string(jsonBytes)) - writer.Flush() + //err = writeOut(Writer, string(jsonBytes), outputFile, mtx) + err = writeOut(string(jsonBytes), outputFile, mtx) + if err != nil { + log.Fatal(err) + } } } else { jsonBytes, err := json.Marshal(plp) if err != nil { log.Fatal(err) } - fmt.Fprintln(writer, string(jsonBytes)) - writer.Flush() + //err = writeOut(Writer, string(jsonBytes), outputFile, mtx) + err = writeOut(string(jsonBytes), outputFile, mtx) + if err != nil { + log.Fatal(err) + } } } } - } - if file != nil { - file.Close() + if File != nil { + mtx.Lock() + File.Close() + mtx.Unlock() } }, } cmd.Flags().BoolVarP(&flatten, "flatten", "f", false, "Flatten output for using with syslog") cmd.Flags().StringVarP(&outputFile, "out", "o", "", "Output to file, append if exists") + cmd.Flags().StringVarP(&pidfilepath, "pidfile", "p", "", "pid file path") cobra.OnInitialize(initConfig) return cmd From 9f30737c0951347db34dbbf34607a4f60caa7632 Mon Sep 17 00:00:00 2001 From: yo Date: Tue, 9 Feb 2021 22:12:41 +0100 Subject: [PATCH 023/100] Add deferred output as soon as it occurs, fix mutex usage when rotating output file --- postfix-log-parser/cmd/root.go | 74 ++++++++++++++++++++++++---------- 1 file changed, 52 insertions(+), 22 deletions(-) diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index 9df84af..d485762 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -115,26 +115,11 @@ func NewWriter(file string) (*bufio.Writer, *os.File, error) { } } -//func writeOut(wrt *bufio.Writer, msg string, filename string, m sync.Mutex) error { -func writeOut(msg string, filename string, m sync.Mutex) error { - if len(filename) > 0 { - m.Lock() - } +func writeOut(msg string, filename string) error { _, err := fmt.Fprintln(Writer, msg) if err != nil { - if len(filename) > 0 { - m.Unlock() - } return err } - err = Writer.Flush() - if len(filename) > 0 { - m.Unlock() - } - if err != nil { - return err - } - return nil } @@ -179,11 +164,13 @@ func NewCmdRoot() *cobra.Command { go func() { for { <-sig - fmt.Println("SIGUSR1 received, recreate output file") mtx.Lock() + fmt.Println("SIGUSR1 received, recreating output file") + //Writer.Flush() // Done by File.CLose() File.Close() Writer, File, err = NewWriter(outputFile) if err != nil { + mtx.Unlock() cmd.SetOutput(os.Stderr) cmd.Println(err) os.Exit(1) @@ -258,7 +245,48 @@ func NewCmdRoot() *cobra.Command { BounceId: "", } - plp.Messages = append(plp.Messages, message) + /* When a message is deferred, it won't be written out until it is either sent, expired, or generates a non delivery notification. + We want to know instantly when a message is deferred, so we handle this case by emiting output for this message, and not appending this occurence + to the list of Messages + */ + if logFormat.Status == "deferred" { + tmpplp := PostfixLogParser{ + Time: plp.Time, + Hostname: plp.Hostname, + Process: plp.Process, + QueueId: plp.QueueId, + ClientHostname: plp.ClientHostname, + ClinetIp: plp.ClinetIp, + SaslMethod: plp.SaslMethod, + SaslUsername: plp.SaslUsername, + MessageId: plp.MessageId, + From: plp.From, + Size: plp.Size, + NRcpt: plp.NRcpt, + } + tmpplp.Messages = append(tmpplp.Messages, message) + + var jsonBytes []byte + if flatten { + jsonBytes, err = json.Marshal(PlpToFlat(&tmpplp)) + } else { + jsonBytes, err = json.Marshal(tmpplp) + } + if err != nil { + log.Fatal(err) + } + mtx.Lock() + err = writeOut(string(jsonBytes), outputFile) + mtx.Unlock() + if err != nil { + log.Fatal(err) + } + tmpplp.Messages = nil + // cannot use nil as type PostfixLogParser in assignment + //tmpplp = nil + } else { + plp.Messages = append(plp.Messages, message) + } } } @@ -301,8 +329,9 @@ func NewCmdRoot() *cobra.Command { if err != nil { log.Fatal(err) } - //err = writeOut(Writer, string(jsonBytes), outputFile, mtx) - err = writeOut(string(jsonBytes), outputFile, mtx) + mtx.Lock() + err = writeOut(string(jsonBytes), outputFile) + mtx.Unlock() if err != nil { log.Fatal(err) } @@ -312,8 +341,9 @@ func NewCmdRoot() *cobra.Command { if err != nil { log.Fatal(err) } - //err = writeOut(Writer, string(jsonBytes), outputFile, mtx) - err = writeOut(string(jsonBytes), outputFile, mtx) + mtx.Lock() + err = writeOut(string(jsonBytes), outputFile) + mtx.Unlock() if err != nil { log.Fatal(err) } From e94831229bf65ee6828a807e27d2465987f3f61a Mon Sep 17 00:00:00 2001 From: yo Date: Tue, 9 Feb 2021 22:44:35 +0100 Subject: [PATCH 024/100] Update Readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 88ee1a5..0f77f73 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Parse postfix log, and output json format ## Install Place a `postfix-log-parser` command to your PATH and set an executable flag. -Download the latest release from github. https://github.com/youyo/postfix-log-parser/releases/latest +Download the latest release from github. https://github.com/yo000/postfix-log-parser/releases/latest ## Usage From 80f09d156588d387fbdad3d3238a9272b2c65760 Mon Sep 17 00:00:00 2001 From: yo Date: Fri, 12 Feb 2021 18:39:47 +0100 Subject: [PATCH 025/100] Flush after each line (is back) --- postfix-log-parser/cmd/root.go | 1 + 1 file changed, 1 insertion(+) diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index d485762..c5db387 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -117,6 +117,7 @@ func NewWriter(file string) (*bufio.Writer, *os.File, error) { func writeOut(msg string, filename string) error { _, err := fmt.Fprintln(Writer, msg) + Writer.Flush() if err != nil { return err } From 3d859fed2ef45cf6ad8f96e88820f67565ec65b7 Mon Sep 17 00:00:00 2001 From: yo Date: Sat, 20 Feb 2021 11:40:47 +0100 Subject: [PATCH 026/100] Support milter-hold --- postfix-log-parser.go | 16 ++++++++-------- postfix-log-parser/cmd/root.go | 3 ++- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/postfix-log-parser.go b/postfix-log-parser.go index 2e70bd9..51d5e2c 100644 --- a/postfix-log-parser.go +++ b/postfix-log-parser.go @@ -19,8 +19,8 @@ const ( FromRegexpFormat = `(?:from=<(.+@.+)>(?:, size=(\d+), nrcpt=(\d+))?)?` ToRegexpFormat = `(?:to=<(.+@.+)>.*status=([a-z]+))?` SenderNDNRegexpFormat = `(?:sender non-delivery notification: ([0-9A-Z]*))?` - MilterRejectRegexpFormat = `(?:milter-reject: .* from (.+)\[(.+)\]: .*from=<(.+@.+)> to=<(.+@.+)> .*)?` - MessageDetailsRegexpFormat = `(` + ClientRegexpFormat + MessageIdRegexpFormat + FromRegexpFormat + ToRegexpFormat + SenderNDNRegexpFormat + MilterRejectRegexpFormat + `.*)` + MilterRegexpFormat = `(?:(milter-.*): .* from (.+)\[(.+)\]: .*from=<(.+@.+)?> to=<(.+@.+)> .*)?` + MessageDetailsRegexpFormat = `(` + ClientRegexpFormat + MessageIdRegexpFormat + FromRegexpFormat + ToRegexpFormat + SenderNDNRegexpFormat + MilterRegexpFormat + `.*)` RegexpFormat = SyslogPri + TimeRegexpFormat + ` ` + HostRegexpFormat + ` ` + ProcessRegexpFormat + `:? ` + QueueIdRegexpFormat + `(?:\: )?` + MessageDetailsRegexpFormat ) @@ -86,13 +86,13 @@ func (p *PostfixLog) Parse(text []byte) (LogFormat, error) { BounceId: string(group[16]), } - // Milter reject put values far in the group + // Milter reject|hold put values far in the group if len(group[17]) > 0 { - logFormat.Status = "milter-reject" - logFormat.ClientHostname = string(group[17]) - logFormat.ClinetIp = string(group[18]) - logFormat.From = string(group[19]) - logFormat.To = string(group[20]) + logFormat.Status = string(group[17]) + logFormat.ClientHostname = string(group[18]) + logFormat.ClinetIp = string(group[19]) + logFormat.From = string(group[20]) + logFormat.To = string(group[21]) } else { logFormat.Status = string(group[15]) logFormat.ClientHostname = string(group[6]) diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index c5db387..1b8ef08 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -7,6 +7,7 @@ import ( "log" "os" "os/signal" + "strings" "sync" "syscall" "time" @@ -321,7 +322,7 @@ func NewCmdRoot() *cobra.Command { 2021-02-05T14:17:51+01:00 smtp.server.com postfix/cleanup[38982]: D8C136A3A: milter-reject: END-OF-MESSAGE from unknown[1.2.3.4]: 4.7.1 Greylisting in action, try again later; from= to= proto=ESMTP helo= */ // "removed" message is end of logs. then flush. - if logFormat.Messages == "removed" || logFormat.Status == "milter-reject" { + if logFormat.Messages == "removed" || strings.HasPrefix(logFormat.Status, "milter-") { if plp, ok := m[logFormat.QueueId]; ok { if flatten { // Flatten the structure, then print each message From 56ae1aef56b1c459acf3e3246e0c7a81d672e6f6 Mon Sep 17 00:00:00 2001 From: yo Date: Sat, 20 Feb 2021 11:41:52 +0100 Subject: [PATCH 027/100] Add specific tests for milter --- test/test-milter.log | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 test/test-milter.log diff --git a/test/test-milter.log b/test/test-milter.log new file mode 100644 index 0000000..e21d33d --- /dev/null +++ b/test/test-milter.log @@ -0,0 +1,6 @@ +2021-02-06T10:57:57+01:00 mail.server.com postfix/smtpd[59633]: D6DDA23B0F: client=unknown[99.88.77.66] +2021-02-06T10:57:57+01:00 mail.server.com postfix/cleanup[59586]: D6DDA23B0F: message-id=<20210206095814.2B34E19A19F6@host.sender.domain> +2021-02-06T10:57:58+01:00 mail.server.com postfix/cleanup[59586]: D6DDA23B0F: milter-reject: END-OF-MESSAGE from unknown[99.88.77.66]: 4.7.1 Greylisting in action, try again later; from= to= proto=ESMTP helo= +2021-02-19T14:32:14.936091+01:00 smtp01.example.org postfix/smtpd[58176] E89FA2DEDA: client=srv05.source.com[10.11.12.13] +2021-02-19T14:32:14.938357+01:00 smtp01.example.org postfix/cleanup[52106] E89FA2DEDA: message-id=<20210219133213.5D10C499CA@srv.domain.org> +2021-02-19T14:32:14.976132+01:00 smtp01.example.org postfix/cleanup[52106] E89FA2DEDA: milter-hold: END-OF-MESSAGE from srv05.source.com[10.11.12.13]: milter triggers HOLD action; from=<> to= proto=ESMTP helo= From 35bc814d8171ff298a183872760f52be0ee3ec7a Mon Sep 17 00:00:00 2001 From: yo Date: Sat, 20 Feb 2021 11:42:25 +0100 Subject: [PATCH 028/100] move milter to test-milter.log --- test/test-iso8601.log | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/test-iso8601.log b/test/test-iso8601.log index 82f790c..0b51d12 100644 --- a/test/test-iso8601.log +++ b/test/test-iso8601.log @@ -20,9 +20,6 @@ 2020-01-30T06:26:08.404714+09:00 mail postfix/qmgr[18719]: 6526CDB7400B: removed 2020-01-30T06:26:09.404714+09:00 mail postfix/smtp[1874]: C6E0DDB74006: to=, relay=example.ddd[192.168.0.30]:25, delay=3.4, delays=0.11/0/0.38/2.9, dsn=2.0.0, status=sent (250 2.0.0 OK 1539154772 az9-v6si5976496plb.190 - gsmtp) 2020-01-30T06:26:09.404714+09:00 mail postfix/qmgr[18719]: C6E0DDB74006: removed -2021-02-06T10:57:57+01:00 mail.server.com postfix/smtpd[59633]: D6DDA23B0F: client=unknown[99.88.77.66] -2021-02-06T10:57:57+01:00 mail.server.com postfix/cleanup[59586]: D6DDA23B0F: message-id=<20210206095814.2B34E19A19F6@host.sender.domain> -2021-02-06T10:57:58+01:00 mail.server.com postfix/cleanup[59586]: D6DDA23B0F: milter-reject: END-OF-MESSAGE from unknown[99.88.77.66]: 4.7.1 Greylisting in action, try again later; from= to= proto=ESMTP helo= 2021-02-06T09:58:59.184925+01:00 mail.server.com postfix/submission/smtpd[99525] 2D19728491: client=unknown[10.11.12.13], sasl_method=PLAIN, sasl_username=user1@domain.example.com 2021-02-06T09:58:59.194384+01:00 mail.server.com postfix/cleanup[7821] 2D19728491: replace: header Received: from localhost (unknown [10.11.12.13])??(using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits)?? key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256)??(N from unknown[10.11.12.13]; from= to= proto=ESMTP helo=: Received: from localhost (unknown [10.11.12.13])??(using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits)?? key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256)??(No client certificate requested)??(Authenticated sender: user1)??by mail.server.com (Postfix) with ESMTPSA id 2D19728491??for ; Sat, 6 Feb 2021 09:58:49 +0100 (CET) 2021-02-06T09:58:59.194419+01:00 mail.server.com postfix/cleanup[7821] 2D19728491: message-id=<1612601937133741374.16589280196177750880@user1.domain.example.com> From d511ca010294928a8d45b455f7a8bec6746fce75 Mon Sep 17 00:00:00 2001 From: yo Date: Sat, 20 Feb 2021 13:36:42 +0100 Subject: [PATCH 029/100] BUGFIX : deferred was printed as an array with 1 msg --- postfix-log-parser/cmd/root.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index 1b8ef08..37525ae 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -270,7 +270,7 @@ func NewCmdRoot() *cobra.Command { var jsonBytes []byte if flatten { - jsonBytes, err = json.Marshal(PlpToFlat(&tmpplp)) + jsonBytes, err = json.Marshal(PlpToFlat(&tmpplp)[0]) } else { jsonBytes, err = json.Marshal(tmpplp) } From cc3a95099417ede5a6c444440055471e87b9c48c Mon Sep 17 00:00:00 2001 From: yo Date: Sat, 20 Feb 2021 13:37:09 +0100 Subject: [PATCH 030/100] Add deferred test --- test/test-iso8601.log | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/test-iso8601.log b/test/test-iso8601.log index 0b51d12..fe43cbf 100644 --- a/test/test-iso8601.log +++ b/test/test-iso8601.log @@ -20,6 +20,9 @@ 2020-01-30T06:26:08.404714+09:00 mail postfix/qmgr[18719]: 6526CDB7400B: removed 2020-01-30T06:26:09.404714+09:00 mail postfix/smtp[1874]: C6E0DDB74006: to=, relay=example.ddd[192.168.0.30]:25, delay=3.4, delays=0.11/0/0.38/2.9, dsn=2.0.0, status=sent (250 2.0.0 OK 1539154772 az9-v6si5976496plb.190 - gsmtp) 2020-01-30T06:26:09.404714+09:00 mail postfix/qmgr[18719]: C6E0DDB74006: removed +2021-02-06T10:57:57+01:00 mail.server.com postfix/smtpd[59633]: D6DDA23B0F: client=unknown[99.88.77.66] +2021-02-06T10:57:57+01:00 mail.server.com postfix/cleanup[59586]: D6DDA23B0F: message-id=<20210206095814.2B34E19A19F6@host.sender.domain> +2021-02-06T10:57:58+01:00 mail.server.com postfix/cleanup[59586]: D6DDA23B0F: milter-reject: END-OF-MESSAGE from unknown[99.88.77.66]: 4.7.1 Greylisting in action, try again later; from= to= proto=ESMTP helo= 2021-02-06T09:58:59.184925+01:00 mail.server.com postfix/submission/smtpd[99525] 2D19728491: client=unknown[10.11.12.13], sasl_method=PLAIN, sasl_username=user1@domain.example.com 2021-02-06T09:58:59.194384+01:00 mail.server.com postfix/cleanup[7821] 2D19728491: replace: header Received: from localhost (unknown [10.11.12.13])??(using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits)?? key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256)??(N from unknown[10.11.12.13]; from= to= proto=ESMTP helo=: Received: from localhost (unknown [10.11.12.13])??(using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits)?? key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256)??(No client certificate requested)??(Authenticated sender: user1)??by mail.server.com (Postfix) with ESMTPSA id 2D19728491??for ; Sat, 6 Feb 2021 09:58:49 +0100 (CET) 2021-02-06T09:58:59.194419+01:00 mail.server.com postfix/cleanup[7821] 2D19728491: message-id=<1612601937133741374.16589280196177750880@user1.domain.example.com> @@ -37,3 +40,8 @@ 2021-02-06T11:19:26+01:00 mail1.example.com postfix/smtp[63622]: 42E683DD0B: to=, relay=aspmx.l.google.com[2a00:1450:400c:c04::1b]:25, delay=0.59, delays=0.23/0/0.17/0.19, dsn=5.7.26, status=bounced (host aspmx.l.google.com[2a00:1450:400c:c04::1b] said: 550-5.7.26 This message does not have authentication information or fails to 550-5.7.26 pass authentication checks. To best protect our users from spam, the 550-5.7.26 message has been blocked. Please visit 550-5.7.26 https://support.google.com/mail/answer/81126#authentication for more 550 5.7.26 information. a42si85499772wrx.219 - gsmtp (in reply to end of DATA command)) 2021-02-06T11:19:26+01:00 mail1.example.com postfix/bounce[64352]: 42E683DD0B: sender non-delivery notification: 1F99E4BC9A 2021-02-06T11:19:26+01:00 mail1.example.com postfix/qmgr[31205]: 42E683DD0B: removed +2021-02-20T13:18:56+01:00 srv-smtp postfix/smtpd[63060]: 9ABCD1BCA9: client=mailout.domain.com[10.11.12.13] +2021-02-20T13:18:56+01:00 srv-smtp postfix/cleanup[62622]: 9ABCD1BCA9: message-id=<20210220121856.17088.40693@internal.domain.lan> +2021-02-20T13:18:56+01:00 srv-smtp postfix/qmgr[28825]: 9ABCD1BCA9: from=, size=5712, nrcpt=1 (queue active) +2021-02-20T13:18:56+01:00 srv-smtp postfix/smtp[62771]: 9ABCD1BCA9: host gmail-smtp-in.l.google.com[74.125.140.27] said: 450-4.2.1 The user you are trying to contact is receiving mail at a rate that 450-4.2.1 prevents additional messages from being delivered. Please resend your 450-4.2.1 message at a later time. If the user is able to receive mail at that 450-4.2.1 time, your message will be delivered. For more information, please 450-4.2.1 visit 450 4.2.1 https://support.google.com/mail/?p=ReceivingRate f22si54329842vxg.12 - gsmtp (in reply to RCPT TO command) +2021-02-20T13:18:57+01:00 srv-smtp postfix/smtp[62771]: 9ABCD1BCA9: to=, relay=gmail-smtp-in.l.google.com[2a00:1450:400c:c02::1b]:25, delay=0.41, delays=0.19/0/0.19/0.03, dsn=4.2.1, status=deferred (host gmail-smtp-in.l.google.com[2a00:1450:400c:c02::1b] said: 450-4.2.1 The user you are trying to contact is receiving mail at a rate that 450-4.2.1 prevents additional messages from being delivered. Please resend your 450-4.2.1 message at a later time. If the user is able to receive mail at that 450-4.2.1 time, your message will be delivered. For more information, please 450-4.2.1 visit 450 4.2.1 https://support.google.com/mail/?p=ReceivingRate f22si54329842vxg.042 - gsmtp (in reply to RCPT TO command)) From 29f3eb22305875af054902edb6ab81c334aac73a Mon Sep 17 00:00:00 2001 From: yo Date: Sat, 20 Feb 2021 19:26:15 +0100 Subject: [PATCH 031/100] Optionally exports statistics to prometheus --- postfix-log-parser/cmd/root.go | 104 +++++++++++++++++++++++++++++---- 1 file changed, 94 insertions(+), 10 deletions(-) diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index 37525ae..3520b2d 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "log" + "net/http" "os" "os/signal" "strings" @@ -12,6 +13,9 @@ import ( "syscall" "time" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" + "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/spf13/cobra" "github.com/tabalt/pidfile" postfixlog "github.com/yo000/postfix-log-parser" @@ -65,8 +69,47 @@ type ( } ) -var File os.File -var Writer *bufio.Writer +var ( + File os.File + Writer *bufio.Writer + + StartTime = promauto.NewGauge(prometheus.GaugeOpts{ + Name: "postfixlogparser_uptime_seconds", + Help: "Process uptime in seconds", + }) + LineReadCnt = promauto.NewCounter(prometheus.CounterOpts{ + Name: "postfixlogparser_line_read_count", + Help: "Number of lines read", + }) + LineIncorrectCnt = promauto.NewCounter(prometheus.CounterOpts{ + Name: "postfixlogparser_line_incorrect_count", + Help: "Number of lines with incorrect format", + }) + LineOutCnt = promauto.NewCounter(prometheus.CounterOpts{ + Name: "postfixlogparser_line_out_count", + Help: "Number of lines written to ouput", + }) + MsgSentCnt = promauto.NewCounter(prometheus.CounterOpts{ + Name: "postfixlogparser_msg_sent_count", + Help: "Number of mails sent", + }) + MsgDeferredCnt = promauto.NewCounter(prometheus.CounterOpts{ + Name: "postfixlogparser_msg_deferred_count", + Help: "Number of mails deferred", + }) + MsgBouncedCnt = promauto.NewCounter(prometheus.CounterOpts{ + Name: "postfixlogparser_msg_bounced_count", + Help: "Number of mails bounced", + }) + MsgRejectedCnt = promauto.NewCounter(prometheus.CounterOpts{ + Name: "postfixlogparser_msg_rejected_count", + Help: "Number of mails rejected", + }) + MsgHoldCnt = promauto.NewCounter(prometheus.CounterOpts{ + Name: "postfixlogparser_msg_hold_count", + Help: "Number of mails hold", + }) +) func PlpToFlat(plp *PostfixLogParser) []PostfixLogParserFlat { var plpf = make([]PostfixLogParserFlat, len(plp.Messages)) @@ -122,13 +165,16 @@ func writeOut(msg string, filename string) error { if err != nil { return err } + LineOutCnt.Inc() return nil } func NewCmdRoot() *cobra.Command { var flatten bool var outputFile string - var pidfilepath string + var pidFilePath string + var promListenAddress string + var promMetricPath string var mtx sync.Mutex cmd := &cobra.Command{ @@ -137,8 +183,29 @@ func NewCmdRoot() *cobra.Command { //Long: ``, Run: func(cmd *cobra.Command, args []string) { - if len(pidfilepath) > 0 { - if pid, err := pidfile.Create(pidfilepath); err != nil { + StartTime.Set(float64(time.Now().Unix())) + + // Prometheus exporter + if promListenAddress != "do-not-listen" { + go func() { + http.Handle(promMetricPath, promhttp.Handler()) + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte(` + + Postfix-log-parser Exporter + +

Postfix-log-parser Exporter

+

Metrics

+ + `)) + }) + log.Fatal(http.ListenAndServe(promListenAddress, nil)) + }() + } + + // Create PID file + if len(pidFilePath) > 0 { + if pid, err := pidfile.Create(pidFilePath); err != nil { log.Fatal(err) } else { defer pid.Clear() @@ -185,12 +252,14 @@ func NewCmdRoot() *cobra.Command { // input stdin scanner := bufio.NewScanner(os.Stdin) for scanner.Scan() { + LineReadCnt.Inc() // parse log logFormat, err := p.Parse(scanner.Bytes()) if err != nil { // Incorrect line, just skip it if err.Error() == "Error: Line do not match regex" { + LineIncorrectCnt.Inc() continue } cmd.SetOutput(os.Stderr) @@ -252,6 +321,7 @@ func NewCmdRoot() *cobra.Command { to the list of Messages */ if logFormat.Status == "deferred" { + MsgDeferredCnt.Inc() tmpplp := PostfixLogParser{ Time: plp.Time, Hostname: plp.Hostname, @@ -324,9 +394,19 @@ func NewCmdRoot() *cobra.Command { // "removed" message is end of logs. then flush. if logFormat.Messages == "removed" || strings.HasPrefix(logFormat.Status, "milter-") { if plp, ok := m[logFormat.QueueId]; ok { - if flatten { - // Flatten the structure, then print each message - for _, plpf := range PlpToFlat(plp) { + for _, plpf := range PlpToFlat(plp) { + switch plpf.Status { + case "sent": + MsgSentCnt.Inc() + case "milter-reject": + MsgRejectedCnt.Inc() + case "milter-hold": + MsgHoldCnt.Inc() + case "bounced": + MsgBouncedCnt.Inc() + } + + if flatten { jsonBytes, err := json.Marshal(plpf) if err != nil { log.Fatal(err) @@ -338,7 +418,9 @@ func NewCmdRoot() *cobra.Command { log.Fatal(err) } } - } else { + } + + if !flatten { jsonBytes, err := json.Marshal(plp) if err != nil { log.Fatal(err) @@ -363,7 +445,9 @@ func NewCmdRoot() *cobra.Command { cmd.Flags().BoolVarP(&flatten, "flatten", "f", false, "Flatten output for using with syslog") cmd.Flags().StringVarP(&outputFile, "out", "o", "", "Output to file, append if exists") - cmd.Flags().StringVarP(&pidfilepath, "pidfile", "p", "", "pid file path") + cmd.Flags().StringVarP(&pidFilePath, "pidfile", "p", "", "pid file path") + cmd.Flags().StringVarP(&promListenAddress, "web.listen-address", "l", "do-not-listen", "Address to listen on for web interface and telemetry") + cmd.Flags().StringVarP(&promMetricPath, "web.telemetry-path", "m", "/metrics", "Path under which to expose metrics.") cobra.OnInitialize(initConfig) return cmd From e4aa2075b59c7d03e9ffec06fac78a5ee16732b7 Mon Sep 17 00:00:00 2001 From: yo Date: Sun, 21 Feb 2021 11:02:11 +0100 Subject: [PATCH 032/100] Msg counters by host (useful with centralized syslog), build version as a prometheus label --- postfix-log-parser/cmd/root.go | 42 ++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index 3520b2d..831b978 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -8,6 +8,7 @@ import ( "net/http" "os" "os/signal" + "runtime" "strings" "sync" "syscall" @@ -73,9 +74,15 @@ var ( File os.File Writer *bufio.Writer + Version = "1.2.5" + + BuildInfo = promauto.NewGaugeVec(prometheus.GaugeOpts{ + Name: "postfixlogparser_build_info", + Help: "Constant 1 value labeled by version and goversion from which postfix-log-parser was built", + }, []string{"version", "goversion"}) StartTime = promauto.NewGauge(prometheus.GaugeOpts{ - Name: "postfixlogparser_uptime_seconds", - Help: "Process uptime in seconds", + Name: "postfixlogparser_time_start_seconds", + Help: "Process start time in UNIX timestamp (seconds)", }) LineReadCnt = promauto.NewCounter(prometheus.CounterOpts{ Name: "postfixlogparser_line_read_count", @@ -89,26 +96,26 @@ var ( Name: "postfixlogparser_line_out_count", Help: "Number of lines written to ouput", }) - MsgSentCnt = promauto.NewCounter(prometheus.CounterOpts{ + MsgSentCnt = promauto.NewCounterVec(prometheus.CounterOpts{ Name: "postfixlogparser_msg_sent_count", Help: "Number of mails sent", - }) - MsgDeferredCnt = promauto.NewCounter(prometheus.CounterOpts{ + }, []string{"host"}) + MsgDeferredCnt = promauto.NewCounterVec(prometheus.CounterOpts{ Name: "postfixlogparser_msg_deferred_count", Help: "Number of mails deferred", - }) - MsgBouncedCnt = promauto.NewCounter(prometheus.CounterOpts{ + }, []string{"host"}) + MsgBouncedCnt = promauto.NewCounterVec(prometheus.CounterOpts{ Name: "postfixlogparser_msg_bounced_count", Help: "Number of mails bounced", - }) - MsgRejectedCnt = promauto.NewCounter(prometheus.CounterOpts{ + }, []string{"host"}) + MsgRejectedCnt = promauto.NewCounterVec(prometheus.CounterOpts{ Name: "postfixlogparser_msg_rejected_count", Help: "Number of mails rejected", - }) - MsgHoldCnt = promauto.NewCounter(prometheus.CounterOpts{ + }, []string{"host"}) + MsgHoldCnt = promauto.NewCounterVec(prometheus.CounterOpts{ Name: "postfixlogparser_msg_hold_count", Help: "Number of mails hold", - }) + }, []string{"host"}) ) func PlpToFlat(plp *PostfixLogParser) []PostfixLogParserFlat { @@ -183,6 +190,7 @@ func NewCmdRoot() *cobra.Command { //Long: ``, Run: func(cmd *cobra.Command, args []string) { + BuildInfo.WithLabelValues(Version, runtime.Version()).Set(1) StartTime.Set(float64(time.Now().Unix())) // Prometheus exporter @@ -321,7 +329,7 @@ func NewCmdRoot() *cobra.Command { to the list of Messages */ if logFormat.Status == "deferred" { - MsgDeferredCnt.Inc() + MsgDeferredCnt.WithLabelValues(plp.Hostname).Inc() tmpplp := PostfixLogParser{ Time: plp.Time, Hostname: plp.Hostname, @@ -397,13 +405,13 @@ func NewCmdRoot() *cobra.Command { for _, plpf := range PlpToFlat(plp) { switch plpf.Status { case "sent": - MsgSentCnt.Inc() + MsgSentCnt.WithLabelValues(plpf.Hostname).Inc() case "milter-reject": - MsgRejectedCnt.Inc() + MsgRejectedCnt.WithLabelValues(plpf.Hostname).Inc() case "milter-hold": - MsgHoldCnt.Inc() + MsgHoldCnt.WithLabelValues(plpf.Hostname).Inc() case "bounced": - MsgBouncedCnt.Inc() + MsgBouncedCnt.WithLabelValues(plpf.Hostname).Inc() } if flatten { From 25bb9761c87d8b6b67ee6ca82656e60eccdc43d5 Mon Sep 17 00:00:00 2001 From: yo Date: Sun, 21 Feb 2021 11:38:17 +0100 Subject: [PATCH 033/100] Count line_out by server --- postfix-log-parser/cmd/root.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index 831b978..513ce55 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -92,10 +92,10 @@ var ( Name: "postfixlogparser_line_incorrect_count", Help: "Number of lines with incorrect format", }) - LineOutCnt = promauto.NewCounter(prometheus.CounterOpts{ + LineOutCnt = promauto.NewCounterVec(prometheus.CounterOpts{ Name: "postfixlogparser_line_out_count", Help: "Number of lines written to ouput", - }) + }, []string{"host"}) MsgSentCnt = promauto.NewCounterVec(prometheus.CounterOpts{ Name: "postfixlogparser_msg_sent_count", Help: "Number of mails sent", @@ -172,7 +172,11 @@ func writeOut(msg string, filename string) error { if err != nil { return err } - LineOutCnt.Inc() + + var tmpPlp PostfixLogParser + json.Unmarshal([]byte(msg), &tmpPlp) + LineOutCnt.WithLabelValues(tmpPlp.Hostname).Inc() + return nil } From e13380d92e778167f75abcf301c31e6291feed25 Mon Sep 17 00:00:00 2001 From: yo Date: Sun, 21 Feb 2021 16:40:46 +0100 Subject: [PATCH 034/100] Bump version --- postfix-log-parser/cmd/root.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index 513ce55..ff2ecd8 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -74,7 +74,7 @@ var ( File os.File Writer *bufio.Writer - Version = "1.2.5" + Version = "1.2.5.1" BuildInfo = promauto.NewGaugeVec(prometheus.GaugeOpts{ Name: "postfixlogparser_build_info", From 937af4d3f2e23f10bf60d30257ec6fd6f8436e64 Mon Sep 17 00:00:00 2001 From: Johan Vaucourt Date: Wed, 23 Jun 2021 17:00:39 +0200 Subject: [PATCH 035/100] Added counter for msg accepted by smtpd --- postfix-log-parser/cmd/root.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index ff2ecd8..cd574c4 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -74,7 +74,7 @@ var ( File os.File Writer *bufio.Writer - Version = "1.2.5.1" + Version = "1.2.5.2" BuildInfo = promauto.NewGaugeVec(prometheus.GaugeOpts{ Name: "postfixlogparser_build_info", @@ -96,6 +96,10 @@ var ( Name: "postfixlogparser_line_out_count", Help: "Number of lines written to ouput", }, []string{"host"}) + MsgInCnt = promauto.NewCounterVec(prometheus.CounterOpts{ + Name: "postfixlogparser_msg_in_count", + Help: "Number of mails accepted by smtpd", + }, []string{"host"}) MsgSentCnt = promauto.NewCounterVec(prometheus.CounterOpts{ Name: "postfixlogparser_msg_sent_count", Help: "Number of mails sent", @@ -293,6 +297,7 @@ func NewCmdRoot() *cobra.Command { SaslMethod: logFormat.SaslMethod, SaslUsername: logFormat.SaslUsername, } + MsgInCnt.WithLabelValues(logFormat.Hostname).Inc() } /* From 3a32f52e38fcf890da264f09ad78119774ac7ed6 Mon Sep 17 00:00:00 2001 From: Johan Vaucourt Date: Wed, 23 Jun 2021 17:00:54 +0200 Subject: [PATCH 036/100] dependencies update --- go.mod | 18 +- go.sum | 569 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 585 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 22dcf41..e066c23 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,22 @@ module github.com/yo000/postfix-log-parser require ( - github.com/spf13/cobra v0.0.3 - github.com/spf13/pflag v1.0.3 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.1.1 // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/google/go-cmp v0.5.6 // indirect + github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect + github.com/prometheus/client_golang v1.11.0 + github.com/prometheus/client_model v0.2.0 // indirect + github.com/prometheus/common v0.29.0 // indirect + github.com/prometheus/procfs v0.6.0 // indirect + github.com/spf13/cobra v1.1.3 + github.com/spf13/pflag v1.0.5 // indirect + github.com/tabalt/pidfile v1.1.0 + golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 // indirect + golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect + google.golang.org/protobuf v1.26.0 // indirect ) go 1.13 diff --git a/go.sum b/go.sum index 385007d..fd948a3 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,573 @@ +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 v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +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/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +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/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +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/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/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 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +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/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +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/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.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +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/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/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/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +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/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +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-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +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-20191227052852-215e87163ea7/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/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +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.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +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/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +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.4.1/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.1/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-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +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/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +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/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +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/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +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-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/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/sdk v0.1.1/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-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-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.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/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +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.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +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/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +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.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/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +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/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 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/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/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/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +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/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/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ= +github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/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.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= +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.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/common v0.29.0 h1:3jqPBvKT4OHAbje2Ql7KeaaSicDBCxMYwEJU1zRJceE= +github.com/prometheus/common v0.29.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/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.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +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/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +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.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +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/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 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v1.1.3 h1:xghbfqPkxzxP3C/f3n5DdpAbdKLj4ZE4BWQI362l53M= +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.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +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/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/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/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/tabalt/pidfile v1.1.0 h1:Q7qQGZ4MoAXE+rvM5tB4/eAIrawewYewByhMiPoDE50= +github.com/tabalt/pidfile v1.1.0/go.mod h1:7F1QwNrjfAApsuX4Nyah3RsbHVAdY/D9qZWp0nnJ/Uw= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +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.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +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-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/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/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/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +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/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +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.1.1-0.20191107180719-034126e5016b/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/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-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-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-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-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/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-20200222125558-5a598a2470a0/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-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/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-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20210525063256-abc453219eb5/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= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +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-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-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/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-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-20190215142949-d0b11bdaac8a/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-20190412213103-97732733099d/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-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/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-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/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-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 h1:RqytpXGR1iVNX7psjB3ff8y7sNFinVFvkx1c8SjBkio= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +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.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +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-20180917221912-90fa682c2a6e/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-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-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-20190606124116-d0a3d012864b/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-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-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/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-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +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 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +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/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +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/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +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-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-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/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.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +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/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +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/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +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.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +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= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= From 78a8611bae455a42718fcf13ad1e7fd7c50897b6 Mon Sep 17 00:00:00 2001 From: Johan Vaucourt Date: Wed, 23 Jun 2021 17:57:19 +0200 Subject: [PATCH 037/100] Count msg in more precisely, with nrcpt --- postfix-log-parser/cmd/root.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index cd574c4..367b460 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -10,6 +10,7 @@ import ( "os/signal" "runtime" "strings" + "strconv" "sync" "syscall" "time" @@ -74,7 +75,7 @@ var ( File os.File Writer *bufio.Writer - Version = "1.2.5.2" + Version = "1.2.5.3" BuildInfo = promauto.NewGaugeVec(prometheus.GaugeOpts{ Name: "postfixlogparser_build_info", @@ -297,7 +298,6 @@ func NewCmdRoot() *cobra.Command { SaslMethod: logFormat.SaslMethod, SaslUsername: logFormat.SaslUsername, } - MsgInCnt.WithLabelValues(logFormat.Hostname).Inc() } /* @@ -318,6 +318,8 @@ func NewCmdRoot() *cobra.Command { plp.Size = logFormat.Size plp.NRcpt = logFormat.NRcpt } + nrcpt,_ := strconv.ParseFloat(logFormat.NRcpt, 64) + MsgInCnt.WithLabelValues(logFormat.Hostname).Add(nrcpt) } /* From 2eda3e2d4e8e31daf01a49ab0c171ab0ac5b2753 Mon Sep 17 00:00:00 2001 From: yo Date: Tue, 12 Apr 2022 20:44:53 +0200 Subject: [PATCH 038/100] Test milter-reject --- test/test-milter.log | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/test/test-milter.log b/test/test-milter.log index e21d33d..f397ef9 100644 --- a/test/test-milter.log +++ b/test/test-milter.log @@ -1,6 +1,16 @@ -2021-02-06T10:57:57+01:00 mail.server.com postfix/smtpd[59633]: D6DDA23B0F: client=unknown[99.88.77.66] +021-02-06T10:57:57+01:00 mail.server.com postfix/smtpd[59633]: D6DDA23B0F: client=unknown[99.88.77.66] 2021-02-06T10:57:57+01:00 mail.server.com postfix/cleanup[59586]: D6DDA23B0F: message-id=<20210206095814.2B34E19A19F6@host.sender.domain> 2021-02-06T10:57:58+01:00 mail.server.com postfix/cleanup[59586]: D6DDA23B0F: milter-reject: END-OF-MESSAGE from unknown[99.88.77.66]: 4.7.1 Greylisting in action, try again later; from= to= proto=ESMTP helo= 2021-02-19T14:32:14.936091+01:00 smtp01.example.org postfix/smtpd[58176] E89FA2DEDA: client=srv05.source.com[10.11.12.13] 2021-02-19T14:32:14.938357+01:00 smtp01.example.org postfix/cleanup[52106] E89FA2DEDA: message-id=<20210219133213.5D10C499CA@srv.domain.org> 2021-02-19T14:32:14.976132+01:00 smtp01.example.org postfix/cleanup[52106] E89FA2DEDA: milter-hold: END-OF-MESSAGE from srv05.source.com[10.11.12.13]: milter triggers HOLD action; from=<> to= proto=ESMTP helo= + + +2021-12-03T06:27:32.797930+01:00 smtp01.example.org postfix/smtpd[50419] connect from 42-64-53-170.subs.proxad.net[42.64.53.170] +2021-12-03T06:27:33.887389+01:00 smtp01.example.org postfix/smtpd[50419] B42E77AB32: client=42-64-53-170.subs.proxad.net[42.64.53.170], sasl_method=PLAIN, sasl_username=user42@smtp01.example.com +2021-12-03T06:27:33.923496+01:00 smtp01.example.org postfix/smtpd[50419] B42E77AB32: filter: RCPT from 42-64-53-170.subs.proxad.net[42.64.53.170]: : Recipient address triggers FILTER relay:[smtp01.example.org]; from= to= proto=ESMTP helo=<[172.18.0.2]> +2021-12-03T06:27:33.965888+01:00 smtp01.example.org postfix/cleanup[19657] B42E77AB32: replace: header Received: from [172.18.0.2] (42-64-53-170.subs.proxad.net [42.64.53.170])??(Authenticated sender: user42@smtp01.example.com)??by smtp01.example.org (Postfix) with ESMTPA id D89B from 42-64-53-170.subs.proxad.net[42.64.53.170]; from= to= proto=ESMTP helo=<[172.18.0.2]>: Received: from [172.18.0.2] (42-64-53-170.subs.proxad.net [42.64.53.170])??(Authenticated sender: youssef)??by smtp01.example.org (Postfix) with ESMTPA id B42E77AB32;??Wed, 22 Dec 2021 13:59:32 +0100 (CET) +2021-12-03T06:27:33.965947+01:00 smtp01.example.org postfix/cleanup[19657] B42E77AB32: message-id=<164017759823.25920.10144256874843932242@12f2bb757669> +2021-12-03T06:27:34.105536+01:00 smtp01.example.org postfix/cleanup[19657] B42E77AB32: milter-reject: END-OF-MESSAGE from 42-64-53-170.subs.proxad.net[42.64.53.170]: 4.7.1 Ratelimit "05" exceeded; from= to= proto=ESMTP helo=<[172.18.0.2]> +2021-12-03T06:27:34.135099+01:00 smtp01.example.org postfix/smtpd[50419] disconnect from 42-64-53-170.subs.proxad.net[42.64.53.170] ehlo=1 auth=1 mail=1 rcpt=2 data=0/1 rset=1 quit=1 commands=7/8 + From 2916cd72bd86bc0a628264bfad00182154538f31 Mon Sep 17 00:00:00 2001 From: yo Date: Tue, 12 Apr 2022 20:45:19 +0200 Subject: [PATCH 039/100] Periodically clean mqueue --- postfix-log-parser/cmd/root.go | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index 367b460..89f54b4 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -75,7 +75,7 @@ var ( File os.File Writer *bufio.Writer - Version = "1.2.5.3" + Version = "1.2.6" BuildInfo = promauto.NewGaugeVec(prometheus.GaugeOpts{ Name: "postfixlogparser_build_info", @@ -185,6 +185,32 @@ func writeOut(msg string, filename string) error { return nil } + +// Every 24H, remove sent, milter-rejected and deferred that entered queue more than 5 days ago +func periodicallyCleanMQueue(mqueue map[string]*PostfixLogParser) { + var ok int + + for range time.Tick(time.Hour * 24) { + for _, inmail := range mqueue { + ok = 0 + // Check all mails were sent (multiple destinations mails) + // or rejected + for _, outmail := range inmail.Messages { + if outmail.Status == "sent" || outmail.Status == "milter-reject" { + ok += 1 + } else if outmail.Status == "deferred" { + if inmail.Time.Add(time.Hour * 5 * 24).Before(time.Now()) { + ok += 1 + } + } + } + if ok == len(inmail.Messages) { + delete(mqueue, inmail.MessageId) + } + } + } +} + func NewCmdRoot() *cobra.Command { var flatten bool var outputFile string @@ -266,6 +292,9 @@ func NewCmdRoot() *cobra.Command { }() } + // Cleaner thread + go periodicallyCleanMQueue(m) + // input stdin scanner := bufio.NewScanner(os.Stdin) for scanner.Scan() { @@ -287,7 +316,7 @@ func NewCmdRoot() *cobra.Command { /* Oct 10 04:02:02 mail.example.com postfix/smtpd[22941]: DFBEFDBF00C5: client=example.net[127.0.0.1], sasl_method=PLAIN, sasl_username=user@example.com */ - if logFormat.ClientHostname != "" { + if logFormat.ClientHostname != "" && !strings.HasPrefix(logFormat.Messages, "milter-reject:") { m[logFormat.QueueId] = &PostfixLogParser{ Time: logFormat.Time, Hostname: logFormat.Hostname, From bead604fd160a9b1d8ec3fac7435ed1f3f78a6f4 Mon Sep 17 00:00:00 2001 From: yo Date: Mon, 25 Apr 2022 10:35:57 +0200 Subject: [PATCH 040/100] Cleaning --- postfix-log-parser/cmd/root.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index 89f54b4..1cc6eaa 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -262,7 +262,7 @@ func NewCmdRoot() *cobra.Command { p := postfixlog.NewPostfixLog() // Get a writer, file or stdout - Writer, File, err := NewWriter(outputFile) + _, File, err := NewWriter(outputFile) if err != nil { cmd.SetOutput(os.Stderr) cmd.Println(err) @@ -278,9 +278,8 @@ func NewCmdRoot() *cobra.Command { <-sig mtx.Lock() fmt.Println("SIGUSR1 received, recreating output file") - //Writer.Flush() // Done by File.CLose() File.Close() - Writer, File, err = NewWriter(outputFile) + _, File, err = NewWriter(outputFile) if err != nil { mtx.Unlock() cmd.SetOutput(os.Stderr) From c40ba78cfc335f2d236349e07c43984496a1c127 Mon Sep 17 00:00:00 2001 From: yo Date: Tue, 7 Jun 2022 13:13:04 +0200 Subject: [PATCH 041/100] RootCmd reorg --- postfix-log-parser/cmd/execute.go | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 postfix-log-parser/cmd/execute.go diff --git a/postfix-log-parser/cmd/execute.go b/postfix-log-parser/cmd/execute.go deleted file mode 100644 index 530f8a7..0000000 --- a/postfix-log-parser/cmd/execute.go +++ /dev/null @@ -1,15 +0,0 @@ -package cmd - -import "os" - -func initConfig() {} - -func Execute() { - cmd := NewCmdRoot() - cmd.SetOutput(os.Stdout) - if err := cmd.Execute(); err != nil { - cmd.SetOutput(os.Stderr) - cmd.Println(err) - os.Exit(1) - } -} From 854f3fb9cf12e939fd271bc305294a8c4cf5d2ea Mon Sep 17 00:00:00 2001 From: yo Date: Tue, 7 Jun 2022 13:13:46 +0200 Subject: [PATCH 042/100] Accept input from TCP port with -s switch. Only 1 client supported. --- postfix-log-parser/cmd/root.go | 566 ++++++++++++++++++--------------- 1 file changed, 313 insertions(+), 253 deletions(-) diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index 1cc6eaa..4191ae9 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -5,12 +5,13 @@ import ( "encoding/json" "fmt" "log" + "net" "net/http" "os" "os/signal" "runtime" - "strings" "strconv" + "strings" "sync" "syscall" "time" @@ -75,7 +76,7 @@ var ( File os.File Writer *bufio.Writer - Version = "1.2.6" + Version = "1.2.7c" BuildInfo = promauto.NewGaugeVec(prometheus.GaugeOpts{ Name: "postfixlogparser_build_info", @@ -121,8 +122,32 @@ var ( Name: "postfixlogparser_msg_hold_count", Help: "Number of mails hold", }, []string{"host"}) + + rootCmd = &cobra.Command{ + Use: "postfix-log-parser", + Short: "Postfix Log Parser v" + Version + ". Parse postfix log, and output json format", + //Long: ``, + Run: func(cmd *cobra.Command, args []string) { + processLogs(cmd, args) + }, + } + + gFlatten bool + gOutputFile string + gPidFilePath string + gSyslogListenAddress string + gPromListenAddress string + gPromMetricPath string ) +func Execute() { + if err := rootCmd.Execute(); err != nil { + rootCmd.SetOutput(os.Stderr) + rootCmd.Println(err) + os.Exit(1) + } +} + func PlpToFlat(plp *PostfixLogParser) []PostfixLogParserFlat { var plpf = make([]PostfixLogParserFlat, len(plp.Messages)) @@ -185,7 +210,6 @@ func writeOut(msg string, filename string) error { return nil } - // Every 24H, remove sent, milter-rejected and deferred that entered queue more than 5 days ago func periodicallyCleanMQueue(mqueue map[string]*PostfixLogParser) { var ok int @@ -211,291 +235,327 @@ func periodicallyCleanMQueue(mqueue map[string]*PostfixLogParser) { } } -func NewCmdRoot() *cobra.Command { - var flatten bool - var outputFile string - var pidFilePath string - var promListenAddress string - var promMetricPath string +func initConfig() {} + +func init() { + + rootCmd.Flags().BoolVarP(&gFlatten, "gFlatten", "f", false, "Flatten output for using with syslog") + rootCmd.Flags().StringVarP(&gOutputFile, "out", "o", "", "Output to file, append if exists") + rootCmd.Flags().StringVarP(&gPidFilePath, "pidfile", "p", "", "pid file path") + rootCmd.Flags().StringVarP(&gSyslogListenAddress, "syslog.listen-address", "s", "do-not-listen", "Address to listen on for syslog incoming messages. Default is to parse stdin") + rootCmd.Flags().StringVarP(&gPromListenAddress, "prom.listen-address", "l", "do-not-listen", "Address to listen on for prometheus metrics") + rootCmd.Flags().StringVarP(&gPromMetricPath, "prom.telemetry-path", "m", "/metrics", "Path under which to expose metrics.") + + cobra.OnInitialize(initConfig) +} + +func processLogs(cmd *cobra.Command, args []string) { + var scanner *bufio.Scanner + var listener net.Listener var mtx sync.Mutex + var useStdin bool + + fmt.Printf("postfix-log-parser v%s\n", Version) + BuildInfo.WithLabelValues(Version, runtime.Version()).Set(1) + StartTime.Set(float64(time.Now().Unix())) + + // Prometheus exporter + if gPromListenAddress != "do-not-listen" { + go func() { + http.Handle(gPromMetricPath, promhttp.Handler()) + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte(` + + Postfix-log-parser Exporter + +

Postfix-log-parser Exporter

+

Metrics

+ + `)) + }) + log.Fatal(http.ListenAndServe(gPromListenAddress, nil)) + }() + } - cmd := &cobra.Command{ - Use: "postfix-log-parser", - Short: "Parse postfix log, and output json format", - //Long: ``, - Run: func(cmd *cobra.Command, args []string) { + // Create PID file + if len(gPidFilePath) > 0 { + if pid, err := pidfile.Create(gPidFilePath); err != nil { + log.Fatal(err) + } else { + defer pid.Clear() + } + } - BuildInfo.WithLabelValues(Version, runtime.Version()).Set(1) - StartTime.Set(float64(time.Now().Unix())) - - // Prometheus exporter - if promListenAddress != "do-not-listen" { - go func() { - http.Handle(promMetricPath, promhttp.Handler()) - http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - w.Write([]byte(` - - Postfix-log-parser Exporter - -

Postfix-log-parser Exporter

-

Metrics

- - `)) - }) - log.Fatal(http.ListenAndServe(promListenAddress, nil)) - }() - } + // create queue + m := make(map[string]*PostfixLogParser) - // Create PID file - if len(pidFilePath) > 0 { - if pid, err := pidfile.Create(pidFilePath); err != nil { - log.Fatal(err) - } else { - defer pid.Clear() + // initialize + p := postfixlog.NewPostfixLog() + + // Get a writer, file or stdout + _, File, err := NewWriter(gOutputFile) + if err != nil { + cmd.SetOutput(os.Stderr) + cmd.Println(err) + os.Exit(1) + } + + // Manage output file rotation when receiving SIGUSR1 + if len(gOutputFile) > 0 { + sig := make(chan os.Signal) + signal.Notify(sig, syscall.SIGUSR1) + go func() { + for { + <-sig + mtx.Lock() + fmt.Println("SIGUSR1 received, recreating output file") + File.Close() + _, File, err = NewWriter(gOutputFile) + if err != nil { + mtx.Unlock() + cmd.SetOutput(os.Stderr) + cmd.Println(err) + os.Exit(1) } + mtx.Unlock() } + }() + } - // create queue - m := make(map[string]*PostfixLogParser) + // Cleaner thread + go periodicallyCleanMQueue(m) - // initialize - p := postfixlog.NewPostfixLog() + // Initialize Stdin input + if true == strings.EqualFold(gSyslogListenAddress, "do-not-listen") { + useStdin = true + scanner = bufio.NewScanner(os.Stdin) + } else { + listener, err = net.Listen("tcp", gSyslogListenAddress) + if err != nil { + log.Fatal(fmt.Sprintf("Error listening on %s: %v\n", gSyslogListenAddress, err)) + } + } - // Get a writer, file or stdout - _, File, err := NewWriter(outputFile) - if err != nil { - cmd.SetOutput(os.Stderr) - cmd.Println(err) - os.Exit(1) + for { + // If input is made via TCP Conn, we need to read from a connected net.Conn + if useStdin == false { + if scanner == nil { + // We support _only one_ concurent connection to the service + connClt, err := listener.Accept() + if err != nil { + log.Printf("Error accepting: %v", err) + // Loop + continue + } + // Read will fail if no data after "duration" + connClt.SetReadDeadline(time.Now().Add(time.Duration(3600) * time.Second)) + scanner = bufio.NewScanner(connClt) } + } - // Manage output file rotation when receiving SIGUSR1 - if len(outputFile) > 0 { - sig := make(chan os.Signal) - signal.Notify(sig, syscall.SIGUSR1) - go func() { - for { - <-sig - mtx.Lock() - fmt.Println("SIGUSR1 received, recreating output file") - File.Close() - _, File, err = NewWriter(outputFile) - if err != nil { - mtx.Unlock() - cmd.SetOutput(os.Stderr) - cmd.Println(err) - os.Exit(1) - } - mtx.Unlock() - } - }() + if false == scanner.Scan() { + // After Scan returns false, the Err method will return any error that occurred during scanning, except that if it was io.EOF, Err will return nil + if err := scanner.Err(); err != nil { + log.Printf("Error reading data: %v\n", err.Error()) + continue + } + if useStdin == false { + // close connection so we can Accept() again, then loop + scanner = nil + continue + } else { + // stdin is dead, abort mission! + return } + } + LineReadCnt.Inc() - // Cleaner thread - go periodicallyCleanMQueue(m) + // parse log + logFormat, err := p.Parse(scanner.Bytes()) + if err != nil { + // Incorrect line, just skip it + if err.Error() == "Error: Line do not match regex" { + LineIncorrectCnt.Inc() + continue + } + cmd.SetOutput(os.Stderr) + cmd.Println(err) + os.Exit(1) + } - // input stdin - scanner := bufio.NewScanner(os.Stdin) - for scanner.Scan() { - LineReadCnt.Inc() + /* + Oct 10 04:02:02 mail.example.com postfix/smtpd[22941]: DFBEFDBF00C5: client=example.net[127.0.0.1], sasl_method=PLAIN, sasl_username=user@example.com + */ + if logFormat.ClientHostname != "" && !strings.HasPrefix(logFormat.Messages, "milter-reject:") { + m[logFormat.QueueId] = &PostfixLogParser{ + Time: logFormat.Time, + Hostname: logFormat.Hostname, + Process: logFormat.Process, + QueueId: logFormat.QueueId, + ClientHostname: logFormat.ClientHostname, + ClinetIp: logFormat.ClinetIp, + SaslMethod: logFormat.SaslMethod, + SaslUsername: logFormat.SaslUsername, + } + } - // parse log - logFormat, err := p.Parse(scanner.Bytes()) - if err != nil { - // Incorrect line, just skip it - if err.Error() == "Error: Line do not match regex" { - LineIncorrectCnt.Inc() - continue - } - cmd.SetOutput(os.Stderr) - cmd.Println(err) - os.Exit(1) - } + /* + Oct 10 04:02:02 mail.example.com postfix/cleanup[22923]: DFBEFDBF00C5: message-id=<20181009190202.81363306015D@example.com> + */ + if logFormat.MessageId != "" { + if plp, ok := m[logFormat.QueueId]; ok { + plp.MessageId = logFormat.MessageId + } + } - /* - Oct 10 04:02:02 mail.example.com postfix/smtpd[22941]: DFBEFDBF00C5: client=example.net[127.0.0.1], sasl_method=PLAIN, sasl_username=user@example.com - */ - if logFormat.ClientHostname != "" && !strings.HasPrefix(logFormat.Messages, "milter-reject:") { - m[logFormat.QueueId] = &PostfixLogParser{ - Time: logFormat.Time, - Hostname: logFormat.Hostname, - Process: logFormat.Process, - QueueId: logFormat.QueueId, - ClientHostname: logFormat.ClientHostname, - ClinetIp: logFormat.ClinetIp, - SaslMethod: logFormat.SaslMethod, - SaslUsername: logFormat.SaslUsername, - } + /* + Oct 10 04:02:03 mail.example.com postfix/qmgr[18719]: DFBEFDBF00C5: from=, size=3578, nrcpt=1 (queue active) + */ + if logFormat.From != "" { + if plp, ok := m[logFormat.QueueId]; ok { + plp.From = logFormat.From + plp.Size = logFormat.Size + plp.NRcpt = logFormat.NRcpt + } + nrcpt, _ := strconv.ParseFloat(logFormat.NRcpt, 64) + MsgInCnt.WithLabelValues(logFormat.Hostname).Add(nrcpt) + } + + /* + Oct 10 04:02:08 mail.example.com postfix/smtp[22928]: DFBEFDBF00C5: to=, relay=mail.example-to.com[192.168.0.10]:25, delay=5.3, delays=0.26/0/0.31/4.7, dsn=2.0.0, status=sent (250 2.0.0 Ok: queued as C598F1B0002D) + */ + if logFormat.To != "" { + if plp, ok := m[logFormat.QueueId]; ok { + message := Message{ + Time: logFormat.Time, + To: logFormat.To, + Status: logFormat.Status, + Message: logFormat.Messages, + BounceId: "", } - /* - Oct 10 04:02:02 mail.example.com postfix/cleanup[22923]: DFBEFDBF00C5: message-id=<20181009190202.81363306015D@example.com> + /* When a message is deferred, it won't be written out until it is either sent, expired, or generates a non delivery notification. + We want to know instantly when a message is deferred, so we handle this case by emiting output for this message, and not appending this occurence + to the list of Messages */ - if logFormat.MessageId != "" { - if plp, ok := m[logFormat.QueueId]; ok { - plp.MessageId = logFormat.MessageId + if logFormat.Status == "deferred" { + MsgDeferredCnt.WithLabelValues(plp.Hostname).Inc() + tmpplp := PostfixLogParser{ + Time: plp.Time, + Hostname: plp.Hostname, + Process: plp.Process, + QueueId: plp.QueueId, + ClientHostname: plp.ClientHostname, + ClinetIp: plp.ClinetIp, + SaslMethod: plp.SaslMethod, + SaslUsername: plp.SaslUsername, + MessageId: plp.MessageId, + From: plp.From, + Size: plp.Size, + NRcpt: plp.NRcpt, } - } + tmpplp.Messages = append(tmpplp.Messages, message) - /* - Oct 10 04:02:03 mail.example.com postfix/qmgr[18719]: DFBEFDBF00C5: from=, size=3578, nrcpt=1 (queue active) - */ - if logFormat.From != "" { - if plp, ok := m[logFormat.QueueId]; ok { - plp.From = logFormat.From - plp.Size = logFormat.Size - plp.NRcpt = logFormat.NRcpt + var jsonBytes []byte + if gFlatten { + jsonBytes, err = json.Marshal(PlpToFlat(&tmpplp)[0]) + } else { + jsonBytes, err = json.Marshal(tmpplp) + } + if err != nil { + log.Fatal(err) + } + mtx.Lock() + err = writeOut(string(jsonBytes), gOutputFile) + mtx.Unlock() + if err != nil { + log.Fatal(err) } - nrcpt,_ := strconv.ParseFloat(logFormat.NRcpt, 64) - MsgInCnt.WithLabelValues(logFormat.Hostname).Add(nrcpt) + tmpplp.Messages = nil + // cannot use nil as type PostfixLogParser in assignment + //tmpplp = nil + } else { + plp.Messages = append(plp.Messages, message) } + } + } - /* - Oct 10 04:02:08 mail.example.com postfix/smtp[22928]: DFBEFDBF00C5: to=, relay=mail.example-to.com[192.168.0.10]:25, delay=5.3, delays=0.26/0/0.31/4.7, dsn=2.0.0, status=sent (250 2.0.0 Ok: queued as C598F1B0002D) - */ - if logFormat.To != "" { - if plp, ok := m[logFormat.QueueId]; ok { + /* + 2021-02-05T17:25:03+01:00 mail.example.com postfix/bounce[39258]: 006B056E6: sender non-delivery notification: 642E456E9 + */ + if logFormat.BounceId != "" { + if plp, ok := m[logFormat.QueueId]; ok { + // Get the matching Message by Status=bounced + for i, msg := range plp.Messages { + // Need to manage more than one bounce for the same queue_id. This is flawy as we just rely on order to match + if msg.Status == "bounced" && len(msg.BounceId) == 0 { message := Message{ - Time: logFormat.Time, - To: logFormat.To, - Status: logFormat.Status, - Message: logFormat.Messages, - BounceId: "", - } - - /* When a message is deferred, it won't be written out until it is either sent, expired, or generates a non delivery notification. - We want to know instantly when a message is deferred, so we handle this case by emiting output for this message, and not appending this occurence - to the list of Messages - */ - if logFormat.Status == "deferred" { - MsgDeferredCnt.WithLabelValues(plp.Hostname).Inc() - tmpplp := PostfixLogParser{ - Time: plp.Time, - Hostname: plp.Hostname, - Process: plp.Process, - QueueId: plp.QueueId, - ClientHostname: plp.ClientHostname, - ClinetIp: plp.ClinetIp, - SaslMethod: plp.SaslMethod, - SaslUsername: plp.SaslUsername, - MessageId: plp.MessageId, - From: plp.From, - Size: plp.Size, - NRcpt: plp.NRcpt, - } - tmpplp.Messages = append(tmpplp.Messages, message) - - var jsonBytes []byte - if flatten { - jsonBytes, err = json.Marshal(PlpToFlat(&tmpplp)[0]) - } else { - jsonBytes, err = json.Marshal(tmpplp) - } - if err != nil { - log.Fatal(err) - } - mtx.Lock() - err = writeOut(string(jsonBytes), outputFile) - mtx.Unlock() - if err != nil { - log.Fatal(err) - } - tmpplp.Messages = nil - // cannot use nil as type PostfixLogParser in assignment - //tmpplp = nil - } else { - plp.Messages = append(plp.Messages, message) + Time: msg.Time, + To: msg.To, + Status: msg.Status, + Message: msg.Message, + BounceId: logFormat.BounceId, } + // Delete old message, put new at the end + copy(plp.Messages[i:], plp.Messages[i+1:]) + plp.Messages[len(plp.Messages)-1] = message + break } } + } + } + /* + Oct 10 04:02:08 mail.example.com postfix/qmgr[18719]: DFBEFDBF00C5: removed + or + 2021-02-05T14:17:51+01:00 smtp.server.com postfix/cleanup[38982]: D8C136A3A: milter-reject: END-OF-MESSAGE from unknown[1.2.3.4]: 4.7.1 Greylisting in action, try again later; from= to= proto=ESMTP helo= + */ + // "removed" message is end of logs. then flush. + if logFormat.Messages == "removed" || strings.HasPrefix(logFormat.Status, "milter-") { + if plp, ok := m[logFormat.QueueId]; ok { + for _, plpf := range PlpToFlat(plp) { + switch plpf.Status { + case "sent": + MsgSentCnt.WithLabelValues(plpf.Hostname).Inc() + case "milter-reject": + MsgRejectedCnt.WithLabelValues(plpf.Hostname).Inc() + case "milter-hold": + MsgHoldCnt.WithLabelValues(plpf.Hostname).Inc() + case "bounced": + MsgBouncedCnt.WithLabelValues(plpf.Hostname).Inc() + } - /* - 2021-02-05T17:25:03+01:00 mail.example.com postfix/bounce[39258]: 006B056E6: sender non-delivery notification: 642E456E9 - */ - if logFormat.BounceId != "" { - if plp, ok := m[logFormat.QueueId]; ok { - // Get the matching Message by Status=bounced - for i, msg := range plp.Messages { - // Need to manage more than one bounce for the same queue_id. This is flawy as we just rely on order to match - if msg.Status == "bounced" && len(msg.BounceId) == 0 { - message := Message{ - Time: msg.Time, - To: msg.To, - Status: msg.Status, - Message: msg.Message, - BounceId: logFormat.BounceId, - } - // Delete old message, put new at the end - copy(plp.Messages[i:], plp.Messages[i+1:]) - plp.Messages[len(plp.Messages)-1] = message - break - } + if gFlatten { + jsonBytes, err := json.Marshal(plpf) + if err != nil { + log.Fatal(err) + } + mtx.Lock() + err = writeOut(string(jsonBytes), gOutputFile) + mtx.Unlock() + if err != nil { + log.Fatal(err) } } } - /* - Oct 10 04:02:08 mail.example.com postfix/qmgr[18719]: DFBEFDBF00C5: removed - or - 2021-02-05T14:17:51+01:00 smtp.server.com postfix/cleanup[38982]: D8C136A3A: milter-reject: END-OF-MESSAGE from unknown[1.2.3.4]: 4.7.1 Greylisting in action, try again later; from= to= proto=ESMTP helo= - */ - // "removed" message is end of logs. then flush. - if logFormat.Messages == "removed" || strings.HasPrefix(logFormat.Status, "milter-") { - if plp, ok := m[logFormat.QueueId]; ok { - for _, plpf := range PlpToFlat(plp) { - switch plpf.Status { - case "sent": - MsgSentCnt.WithLabelValues(plpf.Hostname).Inc() - case "milter-reject": - MsgRejectedCnt.WithLabelValues(plpf.Hostname).Inc() - case "milter-hold": - MsgHoldCnt.WithLabelValues(plpf.Hostname).Inc() - case "bounced": - MsgBouncedCnt.WithLabelValues(plpf.Hostname).Inc() - } - - if flatten { - jsonBytes, err := json.Marshal(plpf) - if err != nil { - log.Fatal(err) - } - mtx.Lock() - err = writeOut(string(jsonBytes), outputFile) - mtx.Unlock() - if err != nil { - log.Fatal(err) - } - } - } - if !flatten { - jsonBytes, err := json.Marshal(plp) - if err != nil { - log.Fatal(err) - } - mtx.Lock() - err = writeOut(string(jsonBytes), outputFile) - mtx.Unlock() - if err != nil { - log.Fatal(err) - } - } + if !gFlatten { + jsonBytes, err := json.Marshal(plp) + if err != nil { + log.Fatal(err) + } + mtx.Lock() + err = writeOut(string(jsonBytes), gOutputFile) + mtx.Unlock() + if err != nil { + log.Fatal(err) } } } - if File != nil { - mtx.Lock() - File.Close() - mtx.Unlock() - } - }, + } + } + if File != nil { + mtx.Lock() + File.Close() + mtx.Unlock() } - - cmd.Flags().BoolVarP(&flatten, "flatten", "f", false, "Flatten output for using with syslog") - cmd.Flags().StringVarP(&outputFile, "out", "o", "", "Output to file, append if exists") - cmd.Flags().StringVarP(&pidFilePath, "pidfile", "p", "", "pid file path") - cmd.Flags().StringVarP(&promListenAddress, "web.listen-address", "l", "do-not-listen", "Address to listen on for web interface and telemetry") - cmd.Flags().StringVarP(&promMetricPath, "web.telemetry-path", "m", "/metrics", "Path under which to expose metrics.") - - cobra.OnInitialize(initConfig) - return cmd } From 24689937ce08e401f6d7301003f5bf82d0dd2f09 Mon Sep 17 00:00:00 2001 From: yo Date: Tue, 7 Jun 2022 13:42:55 +0200 Subject: [PATCH 043/100] Add version flag, remove printing it to stdout at execution time --- postfix-log-parser/cmd/root.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index 4191ae9..8259eb6 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -239,6 +239,8 @@ func initConfig() {} func init() { + rootCmd.Version = Version + rootCmd.Flags().BoolVarP(&gFlatten, "gFlatten", "f", false, "Flatten output for using with syslog") rootCmd.Flags().StringVarP(&gOutputFile, "out", "o", "", "Output to file, append if exists") rootCmd.Flags().StringVarP(&gPidFilePath, "pidfile", "p", "", "pid file path") @@ -255,7 +257,8 @@ func processLogs(cmd *cobra.Command, args []string) { var mtx sync.Mutex var useStdin bool - fmt.Printf("postfix-log-parser v%s\n", Version) + // Nope, breaks stdout output interpretation by jq + //fmt.Printf("postfix-log-parser v%s\n", Version) BuildInfo.WithLabelValues(Version, runtime.Version()).Set(1) StartTime.Set(float64(time.Now().Unix())) From eb17374dbeb1d3a4c4e1e8eb4b8e78be64f0e804 Mon Sep 17 00:00:00 2001 From: yo Date: Tue, 7 Jun 2022 13:44:41 +0200 Subject: [PATCH 044/100] Version bump --- postfix-log-parser/cmd/root.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index 8259eb6..3544791 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -76,7 +76,7 @@ var ( File os.File Writer *bufio.Writer - Version = "1.2.7c" + Version = "1.2.7" BuildInfo = promauto.NewGaugeVec(prometheus.GaugeOpts{ Name: "postfixlogparser_build_info", From 6f517d2196b47dd1b026bf2d32c89eb823aa14b6 Mon Sep 17 00:00:00 2001 From: yo Date: Fri, 10 Jun 2022 22:01:27 +0200 Subject: [PATCH 045/100] Fixed miscomprehension of setDeadline, it now is an idle timeout of 10mn --- postfix-log-parser/cmd/root.go | 39 ++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index 3544791..df7bfcb 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -1,27 +1,28 @@ package cmd import ( - "bufio" - "encoding/json" + "os" "fmt" - "log" "net" + "log" + "sync" + "time" + "bufio" + "errors" "net/http" - "os" - "os/signal" "runtime" "strconv" "strings" - "sync" "syscall" - "time" + "os/signal" + "encoding/json" - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promauto" - "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/spf13/cobra" "github.com/tabalt/pidfile" postfixlog "github.com/yo000/postfix-log-parser" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" + "github.com/prometheus/client_golang/prometheus/promhttp" ) func init() {} @@ -76,7 +77,7 @@ var ( File os.File Writer *bufio.Writer - Version = "1.2.7" + Version = "1.2.9" BuildInfo = promauto.NewGaugeVec(prometheus.GaugeOpts{ Name: "postfixlogparser_build_info", @@ -339,19 +340,20 @@ func processLogs(cmd *cobra.Command, args []string) { } } + var connClt net.Conn for { // If input is made via TCP Conn, we need to read from a connected net.Conn if useStdin == false { if scanner == nil { // We support _only one_ concurent connection to the service - connClt, err := listener.Accept() + connClt, err = listener.Accept() if err != nil { log.Printf("Error accepting: %v", err) // Loop continue } - // Read will fail if no data after "duration" - connClt.SetReadDeadline(time.Now().Add(time.Duration(3600) * time.Second)) + // Read will fail after "duration" + connClt.SetReadDeadline(time.Now().Add(time.Duration(600) * time.Second)) scanner = bufio.NewScanner(connClt) } } @@ -360,6 +362,12 @@ func processLogs(cmd *cobra.Command, args []string) { // After Scan returns false, the Err method will return any error that occurred during scanning, except that if it was io.EOF, Err will return nil if err := scanner.Err(); err != nil { log.Printf("Error reading data: %v\n", err.Error()) + if useStdin == false && errors.Is(err, os.ErrDeadlineExceeded) { + log.Printf("I/O timeout on socket, closing connection.\n") + // Should we? Actually we can't as connClt is not known here + // connClt.Close() + scanner = nil + } continue } if useStdin == false { @@ -371,6 +379,9 @@ func processLogs(cmd *cobra.Command, args []string) { return } } + // Extend timeout after successful read (so we got an idle timeout) + connClt.SetReadDeadline(time.Now().Add(time.Duration(600) * time.Second)) + LineReadCnt.Inc() // parse log From 25da6702ece0fee3ea99437de01e926738a261fc Mon Sep 17 00:00:00 2001 From: yo Date: Wed, 15 Jun 2022 11:25:56 +0200 Subject: [PATCH 046/100] BUGFIX nil pointer dereference when not using -s --- postfix-log-parser/cmd/root.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index df7bfcb..8ac741f 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -77,7 +77,7 @@ var ( File os.File Writer *bufio.Writer - Version = "1.2.9" + Version = "1.2.10" BuildInfo = promauto.NewGaugeVec(prometheus.GaugeOpts{ Name: "postfixlogparser_build_info", @@ -380,7 +380,9 @@ func processLogs(cmd *cobra.Command, args []string) { } } // Extend timeout after successful read (so we got an idle timeout) - connClt.SetReadDeadline(time.Now().Add(time.Duration(600) * time.Second)) + if useStdin == false { + connClt.SetReadDeadline(time.Now().Add(time.Duration(600) * time.Second)) + } LineReadCnt.Inc() From bafcc426e254aedc1fc21db823e1221a4e95177c Mon Sep 17 00:00:00 2001 From: yo Date: Wed, 23 Jun 2021 17:00:39 +0200 Subject: [PATCH 047/100] Added counter for msg accepted by smtpd --- postfix-log-parser/cmd/root.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index ff2ecd8..cd574c4 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -74,7 +74,7 @@ var ( File os.File Writer *bufio.Writer - Version = "1.2.5.1" + Version = "1.2.5.2" BuildInfo = promauto.NewGaugeVec(prometheus.GaugeOpts{ Name: "postfixlogparser_build_info", @@ -96,6 +96,10 @@ var ( Name: "postfixlogparser_line_out_count", Help: "Number of lines written to ouput", }, []string{"host"}) + MsgInCnt = promauto.NewCounterVec(prometheus.CounterOpts{ + Name: "postfixlogparser_msg_in_count", + Help: "Number of mails accepted by smtpd", + }, []string{"host"}) MsgSentCnt = promauto.NewCounterVec(prometheus.CounterOpts{ Name: "postfixlogparser_msg_sent_count", Help: "Number of mails sent", @@ -293,6 +297,7 @@ func NewCmdRoot() *cobra.Command { SaslMethod: logFormat.SaslMethod, SaslUsername: logFormat.SaslUsername, } + MsgInCnt.WithLabelValues(logFormat.Hostname).Inc() } /* From c7200f8ec05ce43b9083e615bf0691b33f91a5a2 Mon Sep 17 00:00:00 2001 From: yo Date: Wed, 23 Jun 2021 17:00:54 +0200 Subject: [PATCH 048/100] dependencies update --- go.mod | 18 +- go.sum | 569 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 585 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 22dcf41..e066c23 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,22 @@ module github.com/yo000/postfix-log-parser require ( - github.com/spf13/cobra v0.0.3 - github.com/spf13/pflag v1.0.3 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.1.1 // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/google/go-cmp v0.5.6 // indirect + github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect + github.com/prometheus/client_golang v1.11.0 + github.com/prometheus/client_model v0.2.0 // indirect + github.com/prometheus/common v0.29.0 // indirect + github.com/prometheus/procfs v0.6.0 // indirect + github.com/spf13/cobra v1.1.3 + github.com/spf13/pflag v1.0.5 // indirect + github.com/tabalt/pidfile v1.1.0 + golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 // indirect + golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect + google.golang.org/protobuf v1.26.0 // indirect ) go 1.13 diff --git a/go.sum b/go.sum index 385007d..fd948a3 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,573 @@ +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 v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +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/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +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/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +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/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/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 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +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/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +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/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.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +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/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/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/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +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/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +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-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +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-20191227052852-215e87163ea7/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/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +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.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +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/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +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.4.1/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.1/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-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +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/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +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/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +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/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +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-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/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/sdk v0.1.1/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-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-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.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/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +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.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +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/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +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.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/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +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/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 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/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/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/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +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/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/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ= +github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/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.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= +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.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/common v0.29.0 h1:3jqPBvKT4OHAbje2Ql7KeaaSicDBCxMYwEJU1zRJceE= +github.com/prometheus/common v0.29.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/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.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +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/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +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.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +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/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 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v1.1.3 h1:xghbfqPkxzxP3C/f3n5DdpAbdKLj4ZE4BWQI362l53M= +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.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +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/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/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/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/tabalt/pidfile v1.1.0 h1:Q7qQGZ4MoAXE+rvM5tB4/eAIrawewYewByhMiPoDE50= +github.com/tabalt/pidfile v1.1.0/go.mod h1:7F1QwNrjfAApsuX4Nyah3RsbHVAdY/D9qZWp0nnJ/Uw= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +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.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +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-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/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/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/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +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/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +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.1.1-0.20191107180719-034126e5016b/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/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-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-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-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-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/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-20200222125558-5a598a2470a0/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-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/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-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20210525063256-abc453219eb5/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= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +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-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-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/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-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-20190215142949-d0b11bdaac8a/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-20190412213103-97732733099d/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-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/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-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/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-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 h1:RqytpXGR1iVNX7psjB3ff8y7sNFinVFvkx1c8SjBkio= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +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.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +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-20180917221912-90fa682c2a6e/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-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-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-20190606124116-d0a3d012864b/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-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-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/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-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +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 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +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/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +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/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +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-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-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/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.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +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/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +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/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +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.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +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= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= From d1e8df3db4afc05fb51eea127a45407135cd1529 Mon Sep 17 00:00:00 2001 From: yo Date: Wed, 23 Jun 2021 17:57:19 +0200 Subject: [PATCH 049/100] Count msg in more precisely, with nrcpt --- postfix-log-parser/cmd/root.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index cd574c4..367b460 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -10,6 +10,7 @@ import ( "os/signal" "runtime" "strings" + "strconv" "sync" "syscall" "time" @@ -74,7 +75,7 @@ var ( File os.File Writer *bufio.Writer - Version = "1.2.5.2" + Version = "1.2.5.3" BuildInfo = promauto.NewGaugeVec(prometheus.GaugeOpts{ Name: "postfixlogparser_build_info", @@ -297,7 +298,6 @@ func NewCmdRoot() *cobra.Command { SaslMethod: logFormat.SaslMethod, SaslUsername: logFormat.SaslUsername, } - MsgInCnt.WithLabelValues(logFormat.Hostname).Inc() } /* @@ -318,6 +318,8 @@ func NewCmdRoot() *cobra.Command { plp.Size = logFormat.Size plp.NRcpt = logFormat.NRcpt } + nrcpt,_ := strconv.ParseFloat(logFormat.NRcpt, 64) + MsgInCnt.WithLabelValues(logFormat.Hostname).Add(nrcpt) } /* From 390f4563d40b31df7b3feb7c2b017f47db063d2b Mon Sep 17 00:00:00 2001 From: yo Date: Tue, 12 Apr 2022 20:44:53 +0200 Subject: [PATCH 050/100] Test milter-reject --- test/test-milter.log | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/test/test-milter.log b/test/test-milter.log index e21d33d..f397ef9 100644 --- a/test/test-milter.log +++ b/test/test-milter.log @@ -1,6 +1,16 @@ -2021-02-06T10:57:57+01:00 mail.server.com postfix/smtpd[59633]: D6DDA23B0F: client=unknown[99.88.77.66] +021-02-06T10:57:57+01:00 mail.server.com postfix/smtpd[59633]: D6DDA23B0F: client=unknown[99.88.77.66] 2021-02-06T10:57:57+01:00 mail.server.com postfix/cleanup[59586]: D6DDA23B0F: message-id=<20210206095814.2B34E19A19F6@host.sender.domain> 2021-02-06T10:57:58+01:00 mail.server.com postfix/cleanup[59586]: D6DDA23B0F: milter-reject: END-OF-MESSAGE from unknown[99.88.77.66]: 4.7.1 Greylisting in action, try again later; from= to= proto=ESMTP helo= 2021-02-19T14:32:14.936091+01:00 smtp01.example.org postfix/smtpd[58176] E89FA2DEDA: client=srv05.source.com[10.11.12.13] 2021-02-19T14:32:14.938357+01:00 smtp01.example.org postfix/cleanup[52106] E89FA2DEDA: message-id=<20210219133213.5D10C499CA@srv.domain.org> 2021-02-19T14:32:14.976132+01:00 smtp01.example.org postfix/cleanup[52106] E89FA2DEDA: milter-hold: END-OF-MESSAGE from srv05.source.com[10.11.12.13]: milter triggers HOLD action; from=<> to= proto=ESMTP helo= + + +2021-12-03T06:27:32.797930+01:00 smtp01.example.org postfix/smtpd[50419] connect from 42-64-53-170.subs.proxad.net[42.64.53.170] +2021-12-03T06:27:33.887389+01:00 smtp01.example.org postfix/smtpd[50419] B42E77AB32: client=42-64-53-170.subs.proxad.net[42.64.53.170], sasl_method=PLAIN, sasl_username=user42@smtp01.example.com +2021-12-03T06:27:33.923496+01:00 smtp01.example.org postfix/smtpd[50419] B42E77AB32: filter: RCPT from 42-64-53-170.subs.proxad.net[42.64.53.170]: : Recipient address triggers FILTER relay:[smtp01.example.org]; from= to= proto=ESMTP helo=<[172.18.0.2]> +2021-12-03T06:27:33.965888+01:00 smtp01.example.org postfix/cleanup[19657] B42E77AB32: replace: header Received: from [172.18.0.2] (42-64-53-170.subs.proxad.net [42.64.53.170])??(Authenticated sender: user42@smtp01.example.com)??by smtp01.example.org (Postfix) with ESMTPA id D89B from 42-64-53-170.subs.proxad.net[42.64.53.170]; from= to= proto=ESMTP helo=<[172.18.0.2]>: Received: from [172.18.0.2] (42-64-53-170.subs.proxad.net [42.64.53.170])??(Authenticated sender: youssef)??by smtp01.example.org (Postfix) with ESMTPA id B42E77AB32;??Wed, 22 Dec 2021 13:59:32 +0100 (CET) +2021-12-03T06:27:33.965947+01:00 smtp01.example.org postfix/cleanup[19657] B42E77AB32: message-id=<164017759823.25920.10144256874843932242@12f2bb757669> +2021-12-03T06:27:34.105536+01:00 smtp01.example.org postfix/cleanup[19657] B42E77AB32: milter-reject: END-OF-MESSAGE from 42-64-53-170.subs.proxad.net[42.64.53.170]: 4.7.1 Ratelimit "05" exceeded; from= to= proto=ESMTP helo=<[172.18.0.2]> +2021-12-03T06:27:34.135099+01:00 smtp01.example.org postfix/smtpd[50419] disconnect from 42-64-53-170.subs.proxad.net[42.64.53.170] ehlo=1 auth=1 mail=1 rcpt=2 data=0/1 rset=1 quit=1 commands=7/8 + From ba55026c3c90b461a95ca31fba0c7a441a2808c0 Mon Sep 17 00:00:00 2001 From: yo Date: Tue, 12 Apr 2022 20:45:19 +0200 Subject: [PATCH 051/100] Periodically clean mqueue --- postfix-log-parser/cmd/root.go | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index 367b460..89f54b4 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -75,7 +75,7 @@ var ( File os.File Writer *bufio.Writer - Version = "1.2.5.3" + Version = "1.2.6" BuildInfo = promauto.NewGaugeVec(prometheus.GaugeOpts{ Name: "postfixlogparser_build_info", @@ -185,6 +185,32 @@ func writeOut(msg string, filename string) error { return nil } + +// Every 24H, remove sent, milter-rejected and deferred that entered queue more than 5 days ago +func periodicallyCleanMQueue(mqueue map[string]*PostfixLogParser) { + var ok int + + for range time.Tick(time.Hour * 24) { + for _, inmail := range mqueue { + ok = 0 + // Check all mails were sent (multiple destinations mails) + // or rejected + for _, outmail := range inmail.Messages { + if outmail.Status == "sent" || outmail.Status == "milter-reject" { + ok += 1 + } else if outmail.Status == "deferred" { + if inmail.Time.Add(time.Hour * 5 * 24).Before(time.Now()) { + ok += 1 + } + } + } + if ok == len(inmail.Messages) { + delete(mqueue, inmail.MessageId) + } + } + } +} + func NewCmdRoot() *cobra.Command { var flatten bool var outputFile string @@ -266,6 +292,9 @@ func NewCmdRoot() *cobra.Command { }() } + // Cleaner thread + go periodicallyCleanMQueue(m) + // input stdin scanner := bufio.NewScanner(os.Stdin) for scanner.Scan() { @@ -287,7 +316,7 @@ func NewCmdRoot() *cobra.Command { /* Oct 10 04:02:02 mail.example.com postfix/smtpd[22941]: DFBEFDBF00C5: client=example.net[127.0.0.1], sasl_method=PLAIN, sasl_username=user@example.com */ - if logFormat.ClientHostname != "" { + if logFormat.ClientHostname != "" && !strings.HasPrefix(logFormat.Messages, "milter-reject:") { m[logFormat.QueueId] = &PostfixLogParser{ Time: logFormat.Time, Hostname: logFormat.Hostname, From 0f636a17300afa6bd8135c043cc84793fee70d84 Mon Sep 17 00:00:00 2001 From: yo Date: Mon, 25 Apr 2022 10:35:57 +0200 Subject: [PATCH 052/100] Cleaning --- postfix-log-parser/cmd/root.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index 89f54b4..1cc6eaa 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -262,7 +262,7 @@ func NewCmdRoot() *cobra.Command { p := postfixlog.NewPostfixLog() // Get a writer, file or stdout - Writer, File, err := NewWriter(outputFile) + _, File, err := NewWriter(outputFile) if err != nil { cmd.SetOutput(os.Stderr) cmd.Println(err) @@ -278,9 +278,8 @@ func NewCmdRoot() *cobra.Command { <-sig mtx.Lock() fmt.Println("SIGUSR1 received, recreating output file") - //Writer.Flush() // Done by File.CLose() File.Close() - Writer, File, err = NewWriter(outputFile) + _, File, err = NewWriter(outputFile) if err != nil { mtx.Unlock() cmd.SetOutput(os.Stderr) From b7d584334a5a6ba8284db591c146ed444c9967d9 Mon Sep 17 00:00:00 2001 From: yo Date: Tue, 7 Jun 2022 13:13:04 +0200 Subject: [PATCH 053/100] RootCmd reorg --- postfix-log-parser/cmd/execute.go | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 postfix-log-parser/cmd/execute.go diff --git a/postfix-log-parser/cmd/execute.go b/postfix-log-parser/cmd/execute.go deleted file mode 100644 index 530f8a7..0000000 --- a/postfix-log-parser/cmd/execute.go +++ /dev/null @@ -1,15 +0,0 @@ -package cmd - -import "os" - -func initConfig() {} - -func Execute() { - cmd := NewCmdRoot() - cmd.SetOutput(os.Stdout) - if err := cmd.Execute(); err != nil { - cmd.SetOutput(os.Stderr) - cmd.Println(err) - os.Exit(1) - } -} From 1069a13fbe8c858df9d95b5c55b46f6569077485 Mon Sep 17 00:00:00 2001 From: yo Date: Tue, 7 Jun 2022 13:13:46 +0200 Subject: [PATCH 054/100] Accept input from TCP port with -s switch. Only 1 client supported. --- postfix-log-parser/cmd/root.go | 566 ++++++++++++++++++--------------- 1 file changed, 313 insertions(+), 253 deletions(-) diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index 1cc6eaa..4191ae9 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -5,12 +5,13 @@ import ( "encoding/json" "fmt" "log" + "net" "net/http" "os" "os/signal" "runtime" - "strings" "strconv" + "strings" "sync" "syscall" "time" @@ -75,7 +76,7 @@ var ( File os.File Writer *bufio.Writer - Version = "1.2.6" + Version = "1.2.7c" BuildInfo = promauto.NewGaugeVec(prometheus.GaugeOpts{ Name: "postfixlogparser_build_info", @@ -121,8 +122,32 @@ var ( Name: "postfixlogparser_msg_hold_count", Help: "Number of mails hold", }, []string{"host"}) + + rootCmd = &cobra.Command{ + Use: "postfix-log-parser", + Short: "Postfix Log Parser v" + Version + ". Parse postfix log, and output json format", + //Long: ``, + Run: func(cmd *cobra.Command, args []string) { + processLogs(cmd, args) + }, + } + + gFlatten bool + gOutputFile string + gPidFilePath string + gSyslogListenAddress string + gPromListenAddress string + gPromMetricPath string ) +func Execute() { + if err := rootCmd.Execute(); err != nil { + rootCmd.SetOutput(os.Stderr) + rootCmd.Println(err) + os.Exit(1) + } +} + func PlpToFlat(plp *PostfixLogParser) []PostfixLogParserFlat { var plpf = make([]PostfixLogParserFlat, len(plp.Messages)) @@ -185,7 +210,6 @@ func writeOut(msg string, filename string) error { return nil } - // Every 24H, remove sent, milter-rejected and deferred that entered queue more than 5 days ago func periodicallyCleanMQueue(mqueue map[string]*PostfixLogParser) { var ok int @@ -211,291 +235,327 @@ func periodicallyCleanMQueue(mqueue map[string]*PostfixLogParser) { } } -func NewCmdRoot() *cobra.Command { - var flatten bool - var outputFile string - var pidFilePath string - var promListenAddress string - var promMetricPath string +func initConfig() {} + +func init() { + + rootCmd.Flags().BoolVarP(&gFlatten, "gFlatten", "f", false, "Flatten output for using with syslog") + rootCmd.Flags().StringVarP(&gOutputFile, "out", "o", "", "Output to file, append if exists") + rootCmd.Flags().StringVarP(&gPidFilePath, "pidfile", "p", "", "pid file path") + rootCmd.Flags().StringVarP(&gSyslogListenAddress, "syslog.listen-address", "s", "do-not-listen", "Address to listen on for syslog incoming messages. Default is to parse stdin") + rootCmd.Flags().StringVarP(&gPromListenAddress, "prom.listen-address", "l", "do-not-listen", "Address to listen on for prometheus metrics") + rootCmd.Flags().StringVarP(&gPromMetricPath, "prom.telemetry-path", "m", "/metrics", "Path under which to expose metrics.") + + cobra.OnInitialize(initConfig) +} + +func processLogs(cmd *cobra.Command, args []string) { + var scanner *bufio.Scanner + var listener net.Listener var mtx sync.Mutex + var useStdin bool + + fmt.Printf("postfix-log-parser v%s\n", Version) + BuildInfo.WithLabelValues(Version, runtime.Version()).Set(1) + StartTime.Set(float64(time.Now().Unix())) + + // Prometheus exporter + if gPromListenAddress != "do-not-listen" { + go func() { + http.Handle(gPromMetricPath, promhttp.Handler()) + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte(` + + Postfix-log-parser Exporter + +

Postfix-log-parser Exporter

+

Metrics

+ + `)) + }) + log.Fatal(http.ListenAndServe(gPromListenAddress, nil)) + }() + } - cmd := &cobra.Command{ - Use: "postfix-log-parser", - Short: "Parse postfix log, and output json format", - //Long: ``, - Run: func(cmd *cobra.Command, args []string) { + // Create PID file + if len(gPidFilePath) > 0 { + if pid, err := pidfile.Create(gPidFilePath); err != nil { + log.Fatal(err) + } else { + defer pid.Clear() + } + } - BuildInfo.WithLabelValues(Version, runtime.Version()).Set(1) - StartTime.Set(float64(time.Now().Unix())) - - // Prometheus exporter - if promListenAddress != "do-not-listen" { - go func() { - http.Handle(promMetricPath, promhttp.Handler()) - http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - w.Write([]byte(` - - Postfix-log-parser Exporter - -

Postfix-log-parser Exporter

-

Metrics

- - `)) - }) - log.Fatal(http.ListenAndServe(promListenAddress, nil)) - }() - } + // create queue + m := make(map[string]*PostfixLogParser) - // Create PID file - if len(pidFilePath) > 0 { - if pid, err := pidfile.Create(pidFilePath); err != nil { - log.Fatal(err) - } else { - defer pid.Clear() + // initialize + p := postfixlog.NewPostfixLog() + + // Get a writer, file or stdout + _, File, err := NewWriter(gOutputFile) + if err != nil { + cmd.SetOutput(os.Stderr) + cmd.Println(err) + os.Exit(1) + } + + // Manage output file rotation when receiving SIGUSR1 + if len(gOutputFile) > 0 { + sig := make(chan os.Signal) + signal.Notify(sig, syscall.SIGUSR1) + go func() { + for { + <-sig + mtx.Lock() + fmt.Println("SIGUSR1 received, recreating output file") + File.Close() + _, File, err = NewWriter(gOutputFile) + if err != nil { + mtx.Unlock() + cmd.SetOutput(os.Stderr) + cmd.Println(err) + os.Exit(1) } + mtx.Unlock() } + }() + } - // create queue - m := make(map[string]*PostfixLogParser) + // Cleaner thread + go periodicallyCleanMQueue(m) - // initialize - p := postfixlog.NewPostfixLog() + // Initialize Stdin input + if true == strings.EqualFold(gSyslogListenAddress, "do-not-listen") { + useStdin = true + scanner = bufio.NewScanner(os.Stdin) + } else { + listener, err = net.Listen("tcp", gSyslogListenAddress) + if err != nil { + log.Fatal(fmt.Sprintf("Error listening on %s: %v\n", gSyslogListenAddress, err)) + } + } - // Get a writer, file or stdout - _, File, err := NewWriter(outputFile) - if err != nil { - cmd.SetOutput(os.Stderr) - cmd.Println(err) - os.Exit(1) + for { + // If input is made via TCP Conn, we need to read from a connected net.Conn + if useStdin == false { + if scanner == nil { + // We support _only one_ concurent connection to the service + connClt, err := listener.Accept() + if err != nil { + log.Printf("Error accepting: %v", err) + // Loop + continue + } + // Read will fail if no data after "duration" + connClt.SetReadDeadline(time.Now().Add(time.Duration(3600) * time.Second)) + scanner = bufio.NewScanner(connClt) } + } - // Manage output file rotation when receiving SIGUSR1 - if len(outputFile) > 0 { - sig := make(chan os.Signal) - signal.Notify(sig, syscall.SIGUSR1) - go func() { - for { - <-sig - mtx.Lock() - fmt.Println("SIGUSR1 received, recreating output file") - File.Close() - _, File, err = NewWriter(outputFile) - if err != nil { - mtx.Unlock() - cmd.SetOutput(os.Stderr) - cmd.Println(err) - os.Exit(1) - } - mtx.Unlock() - } - }() + if false == scanner.Scan() { + // After Scan returns false, the Err method will return any error that occurred during scanning, except that if it was io.EOF, Err will return nil + if err := scanner.Err(); err != nil { + log.Printf("Error reading data: %v\n", err.Error()) + continue + } + if useStdin == false { + // close connection so we can Accept() again, then loop + scanner = nil + continue + } else { + // stdin is dead, abort mission! + return } + } + LineReadCnt.Inc() - // Cleaner thread - go periodicallyCleanMQueue(m) + // parse log + logFormat, err := p.Parse(scanner.Bytes()) + if err != nil { + // Incorrect line, just skip it + if err.Error() == "Error: Line do not match regex" { + LineIncorrectCnt.Inc() + continue + } + cmd.SetOutput(os.Stderr) + cmd.Println(err) + os.Exit(1) + } - // input stdin - scanner := bufio.NewScanner(os.Stdin) - for scanner.Scan() { - LineReadCnt.Inc() + /* + Oct 10 04:02:02 mail.example.com postfix/smtpd[22941]: DFBEFDBF00C5: client=example.net[127.0.0.1], sasl_method=PLAIN, sasl_username=user@example.com + */ + if logFormat.ClientHostname != "" && !strings.HasPrefix(logFormat.Messages, "milter-reject:") { + m[logFormat.QueueId] = &PostfixLogParser{ + Time: logFormat.Time, + Hostname: logFormat.Hostname, + Process: logFormat.Process, + QueueId: logFormat.QueueId, + ClientHostname: logFormat.ClientHostname, + ClinetIp: logFormat.ClinetIp, + SaslMethod: logFormat.SaslMethod, + SaslUsername: logFormat.SaslUsername, + } + } - // parse log - logFormat, err := p.Parse(scanner.Bytes()) - if err != nil { - // Incorrect line, just skip it - if err.Error() == "Error: Line do not match regex" { - LineIncorrectCnt.Inc() - continue - } - cmd.SetOutput(os.Stderr) - cmd.Println(err) - os.Exit(1) - } + /* + Oct 10 04:02:02 mail.example.com postfix/cleanup[22923]: DFBEFDBF00C5: message-id=<20181009190202.81363306015D@example.com> + */ + if logFormat.MessageId != "" { + if plp, ok := m[logFormat.QueueId]; ok { + plp.MessageId = logFormat.MessageId + } + } - /* - Oct 10 04:02:02 mail.example.com postfix/smtpd[22941]: DFBEFDBF00C5: client=example.net[127.0.0.1], sasl_method=PLAIN, sasl_username=user@example.com - */ - if logFormat.ClientHostname != "" && !strings.HasPrefix(logFormat.Messages, "milter-reject:") { - m[logFormat.QueueId] = &PostfixLogParser{ - Time: logFormat.Time, - Hostname: logFormat.Hostname, - Process: logFormat.Process, - QueueId: logFormat.QueueId, - ClientHostname: logFormat.ClientHostname, - ClinetIp: logFormat.ClinetIp, - SaslMethod: logFormat.SaslMethod, - SaslUsername: logFormat.SaslUsername, - } + /* + Oct 10 04:02:03 mail.example.com postfix/qmgr[18719]: DFBEFDBF00C5: from=, size=3578, nrcpt=1 (queue active) + */ + if logFormat.From != "" { + if plp, ok := m[logFormat.QueueId]; ok { + plp.From = logFormat.From + plp.Size = logFormat.Size + plp.NRcpt = logFormat.NRcpt + } + nrcpt, _ := strconv.ParseFloat(logFormat.NRcpt, 64) + MsgInCnt.WithLabelValues(logFormat.Hostname).Add(nrcpt) + } + + /* + Oct 10 04:02:08 mail.example.com postfix/smtp[22928]: DFBEFDBF00C5: to=, relay=mail.example-to.com[192.168.0.10]:25, delay=5.3, delays=0.26/0/0.31/4.7, dsn=2.0.0, status=sent (250 2.0.0 Ok: queued as C598F1B0002D) + */ + if logFormat.To != "" { + if plp, ok := m[logFormat.QueueId]; ok { + message := Message{ + Time: logFormat.Time, + To: logFormat.To, + Status: logFormat.Status, + Message: logFormat.Messages, + BounceId: "", } - /* - Oct 10 04:02:02 mail.example.com postfix/cleanup[22923]: DFBEFDBF00C5: message-id=<20181009190202.81363306015D@example.com> + /* When a message is deferred, it won't be written out until it is either sent, expired, or generates a non delivery notification. + We want to know instantly when a message is deferred, so we handle this case by emiting output for this message, and not appending this occurence + to the list of Messages */ - if logFormat.MessageId != "" { - if plp, ok := m[logFormat.QueueId]; ok { - plp.MessageId = logFormat.MessageId + if logFormat.Status == "deferred" { + MsgDeferredCnt.WithLabelValues(plp.Hostname).Inc() + tmpplp := PostfixLogParser{ + Time: plp.Time, + Hostname: plp.Hostname, + Process: plp.Process, + QueueId: plp.QueueId, + ClientHostname: plp.ClientHostname, + ClinetIp: plp.ClinetIp, + SaslMethod: plp.SaslMethod, + SaslUsername: plp.SaslUsername, + MessageId: plp.MessageId, + From: plp.From, + Size: plp.Size, + NRcpt: plp.NRcpt, } - } + tmpplp.Messages = append(tmpplp.Messages, message) - /* - Oct 10 04:02:03 mail.example.com postfix/qmgr[18719]: DFBEFDBF00C5: from=, size=3578, nrcpt=1 (queue active) - */ - if logFormat.From != "" { - if plp, ok := m[logFormat.QueueId]; ok { - plp.From = logFormat.From - plp.Size = logFormat.Size - plp.NRcpt = logFormat.NRcpt + var jsonBytes []byte + if gFlatten { + jsonBytes, err = json.Marshal(PlpToFlat(&tmpplp)[0]) + } else { + jsonBytes, err = json.Marshal(tmpplp) + } + if err != nil { + log.Fatal(err) + } + mtx.Lock() + err = writeOut(string(jsonBytes), gOutputFile) + mtx.Unlock() + if err != nil { + log.Fatal(err) } - nrcpt,_ := strconv.ParseFloat(logFormat.NRcpt, 64) - MsgInCnt.WithLabelValues(logFormat.Hostname).Add(nrcpt) + tmpplp.Messages = nil + // cannot use nil as type PostfixLogParser in assignment + //tmpplp = nil + } else { + plp.Messages = append(plp.Messages, message) } + } + } - /* - Oct 10 04:02:08 mail.example.com postfix/smtp[22928]: DFBEFDBF00C5: to=, relay=mail.example-to.com[192.168.0.10]:25, delay=5.3, delays=0.26/0/0.31/4.7, dsn=2.0.0, status=sent (250 2.0.0 Ok: queued as C598F1B0002D) - */ - if logFormat.To != "" { - if plp, ok := m[logFormat.QueueId]; ok { + /* + 2021-02-05T17:25:03+01:00 mail.example.com postfix/bounce[39258]: 006B056E6: sender non-delivery notification: 642E456E9 + */ + if logFormat.BounceId != "" { + if plp, ok := m[logFormat.QueueId]; ok { + // Get the matching Message by Status=bounced + for i, msg := range plp.Messages { + // Need to manage more than one bounce for the same queue_id. This is flawy as we just rely on order to match + if msg.Status == "bounced" && len(msg.BounceId) == 0 { message := Message{ - Time: logFormat.Time, - To: logFormat.To, - Status: logFormat.Status, - Message: logFormat.Messages, - BounceId: "", - } - - /* When a message is deferred, it won't be written out until it is either sent, expired, or generates a non delivery notification. - We want to know instantly when a message is deferred, so we handle this case by emiting output for this message, and not appending this occurence - to the list of Messages - */ - if logFormat.Status == "deferred" { - MsgDeferredCnt.WithLabelValues(plp.Hostname).Inc() - tmpplp := PostfixLogParser{ - Time: plp.Time, - Hostname: plp.Hostname, - Process: plp.Process, - QueueId: plp.QueueId, - ClientHostname: plp.ClientHostname, - ClinetIp: plp.ClinetIp, - SaslMethod: plp.SaslMethod, - SaslUsername: plp.SaslUsername, - MessageId: plp.MessageId, - From: plp.From, - Size: plp.Size, - NRcpt: plp.NRcpt, - } - tmpplp.Messages = append(tmpplp.Messages, message) - - var jsonBytes []byte - if flatten { - jsonBytes, err = json.Marshal(PlpToFlat(&tmpplp)[0]) - } else { - jsonBytes, err = json.Marshal(tmpplp) - } - if err != nil { - log.Fatal(err) - } - mtx.Lock() - err = writeOut(string(jsonBytes), outputFile) - mtx.Unlock() - if err != nil { - log.Fatal(err) - } - tmpplp.Messages = nil - // cannot use nil as type PostfixLogParser in assignment - //tmpplp = nil - } else { - plp.Messages = append(plp.Messages, message) + Time: msg.Time, + To: msg.To, + Status: msg.Status, + Message: msg.Message, + BounceId: logFormat.BounceId, } + // Delete old message, put new at the end + copy(plp.Messages[i:], plp.Messages[i+1:]) + plp.Messages[len(plp.Messages)-1] = message + break } } + } + } + /* + Oct 10 04:02:08 mail.example.com postfix/qmgr[18719]: DFBEFDBF00C5: removed + or + 2021-02-05T14:17:51+01:00 smtp.server.com postfix/cleanup[38982]: D8C136A3A: milter-reject: END-OF-MESSAGE from unknown[1.2.3.4]: 4.7.1 Greylisting in action, try again later; from= to= proto=ESMTP helo= + */ + // "removed" message is end of logs. then flush. + if logFormat.Messages == "removed" || strings.HasPrefix(logFormat.Status, "milter-") { + if plp, ok := m[logFormat.QueueId]; ok { + for _, plpf := range PlpToFlat(plp) { + switch plpf.Status { + case "sent": + MsgSentCnt.WithLabelValues(plpf.Hostname).Inc() + case "milter-reject": + MsgRejectedCnt.WithLabelValues(plpf.Hostname).Inc() + case "milter-hold": + MsgHoldCnt.WithLabelValues(plpf.Hostname).Inc() + case "bounced": + MsgBouncedCnt.WithLabelValues(plpf.Hostname).Inc() + } - /* - 2021-02-05T17:25:03+01:00 mail.example.com postfix/bounce[39258]: 006B056E6: sender non-delivery notification: 642E456E9 - */ - if logFormat.BounceId != "" { - if plp, ok := m[logFormat.QueueId]; ok { - // Get the matching Message by Status=bounced - for i, msg := range plp.Messages { - // Need to manage more than one bounce for the same queue_id. This is flawy as we just rely on order to match - if msg.Status == "bounced" && len(msg.BounceId) == 0 { - message := Message{ - Time: msg.Time, - To: msg.To, - Status: msg.Status, - Message: msg.Message, - BounceId: logFormat.BounceId, - } - // Delete old message, put new at the end - copy(plp.Messages[i:], plp.Messages[i+1:]) - plp.Messages[len(plp.Messages)-1] = message - break - } + if gFlatten { + jsonBytes, err := json.Marshal(plpf) + if err != nil { + log.Fatal(err) + } + mtx.Lock() + err = writeOut(string(jsonBytes), gOutputFile) + mtx.Unlock() + if err != nil { + log.Fatal(err) } } } - /* - Oct 10 04:02:08 mail.example.com postfix/qmgr[18719]: DFBEFDBF00C5: removed - or - 2021-02-05T14:17:51+01:00 smtp.server.com postfix/cleanup[38982]: D8C136A3A: milter-reject: END-OF-MESSAGE from unknown[1.2.3.4]: 4.7.1 Greylisting in action, try again later; from= to= proto=ESMTP helo= - */ - // "removed" message is end of logs. then flush. - if logFormat.Messages == "removed" || strings.HasPrefix(logFormat.Status, "milter-") { - if plp, ok := m[logFormat.QueueId]; ok { - for _, plpf := range PlpToFlat(plp) { - switch plpf.Status { - case "sent": - MsgSentCnt.WithLabelValues(plpf.Hostname).Inc() - case "milter-reject": - MsgRejectedCnt.WithLabelValues(plpf.Hostname).Inc() - case "milter-hold": - MsgHoldCnt.WithLabelValues(plpf.Hostname).Inc() - case "bounced": - MsgBouncedCnt.WithLabelValues(plpf.Hostname).Inc() - } - - if flatten { - jsonBytes, err := json.Marshal(plpf) - if err != nil { - log.Fatal(err) - } - mtx.Lock() - err = writeOut(string(jsonBytes), outputFile) - mtx.Unlock() - if err != nil { - log.Fatal(err) - } - } - } - if !flatten { - jsonBytes, err := json.Marshal(plp) - if err != nil { - log.Fatal(err) - } - mtx.Lock() - err = writeOut(string(jsonBytes), outputFile) - mtx.Unlock() - if err != nil { - log.Fatal(err) - } - } + if !gFlatten { + jsonBytes, err := json.Marshal(plp) + if err != nil { + log.Fatal(err) + } + mtx.Lock() + err = writeOut(string(jsonBytes), gOutputFile) + mtx.Unlock() + if err != nil { + log.Fatal(err) } } } - if File != nil { - mtx.Lock() - File.Close() - mtx.Unlock() - } - }, + } + } + if File != nil { + mtx.Lock() + File.Close() + mtx.Unlock() } - - cmd.Flags().BoolVarP(&flatten, "flatten", "f", false, "Flatten output for using with syslog") - cmd.Flags().StringVarP(&outputFile, "out", "o", "", "Output to file, append if exists") - cmd.Flags().StringVarP(&pidFilePath, "pidfile", "p", "", "pid file path") - cmd.Flags().StringVarP(&promListenAddress, "web.listen-address", "l", "do-not-listen", "Address to listen on for web interface and telemetry") - cmd.Flags().StringVarP(&promMetricPath, "web.telemetry-path", "m", "/metrics", "Path under which to expose metrics.") - - cobra.OnInitialize(initConfig) - return cmd } From b05764b13b72c52fad27e6fc07dd6d166594aa61 Mon Sep 17 00:00:00 2001 From: yo Date: Tue, 7 Jun 2022 13:42:55 +0200 Subject: [PATCH 055/100] Add version flag, remove printing it to stdout at execution time --- postfix-log-parser/cmd/root.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index 4191ae9..8259eb6 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -239,6 +239,8 @@ func initConfig() {} func init() { + rootCmd.Version = Version + rootCmd.Flags().BoolVarP(&gFlatten, "gFlatten", "f", false, "Flatten output for using with syslog") rootCmd.Flags().StringVarP(&gOutputFile, "out", "o", "", "Output to file, append if exists") rootCmd.Flags().StringVarP(&gPidFilePath, "pidfile", "p", "", "pid file path") @@ -255,7 +257,8 @@ func processLogs(cmd *cobra.Command, args []string) { var mtx sync.Mutex var useStdin bool - fmt.Printf("postfix-log-parser v%s\n", Version) + // Nope, breaks stdout output interpretation by jq + //fmt.Printf("postfix-log-parser v%s\n", Version) BuildInfo.WithLabelValues(Version, runtime.Version()).Set(1) StartTime.Set(float64(time.Now().Unix())) From 3e2e7d8c8de5b0f9bf8416c947e678e7950e0ca2 Mon Sep 17 00:00:00 2001 From: yo Date: Tue, 7 Jun 2022 13:44:41 +0200 Subject: [PATCH 056/100] Version bump --- postfix-log-parser/cmd/root.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index 8259eb6..3544791 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -76,7 +76,7 @@ var ( File os.File Writer *bufio.Writer - Version = "1.2.7c" + Version = "1.2.7" BuildInfo = promauto.NewGaugeVec(prometheus.GaugeOpts{ Name: "postfixlogparser_build_info", From afdcb626f75e21294fb3ee84f743725bdbead3bb Mon Sep 17 00:00:00 2001 From: yo Date: Fri, 10 Jun 2022 22:01:27 +0200 Subject: [PATCH 057/100] Fixed miscomprehension of setDeadline, it now is an idle timeout of 10mn --- postfix-log-parser/cmd/root.go | 39 ++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index 3544791..df7bfcb 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -1,27 +1,28 @@ package cmd import ( - "bufio" - "encoding/json" + "os" "fmt" - "log" "net" + "log" + "sync" + "time" + "bufio" + "errors" "net/http" - "os" - "os/signal" "runtime" "strconv" "strings" - "sync" "syscall" - "time" + "os/signal" + "encoding/json" - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promauto" - "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/spf13/cobra" "github.com/tabalt/pidfile" postfixlog "github.com/yo000/postfix-log-parser" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" + "github.com/prometheus/client_golang/prometheus/promhttp" ) func init() {} @@ -76,7 +77,7 @@ var ( File os.File Writer *bufio.Writer - Version = "1.2.7" + Version = "1.2.9" BuildInfo = promauto.NewGaugeVec(prometheus.GaugeOpts{ Name: "postfixlogparser_build_info", @@ -339,19 +340,20 @@ func processLogs(cmd *cobra.Command, args []string) { } } + var connClt net.Conn for { // If input is made via TCP Conn, we need to read from a connected net.Conn if useStdin == false { if scanner == nil { // We support _only one_ concurent connection to the service - connClt, err := listener.Accept() + connClt, err = listener.Accept() if err != nil { log.Printf("Error accepting: %v", err) // Loop continue } - // Read will fail if no data after "duration" - connClt.SetReadDeadline(time.Now().Add(time.Duration(3600) * time.Second)) + // Read will fail after "duration" + connClt.SetReadDeadline(time.Now().Add(time.Duration(600) * time.Second)) scanner = bufio.NewScanner(connClt) } } @@ -360,6 +362,12 @@ func processLogs(cmd *cobra.Command, args []string) { // After Scan returns false, the Err method will return any error that occurred during scanning, except that if it was io.EOF, Err will return nil if err := scanner.Err(); err != nil { log.Printf("Error reading data: %v\n", err.Error()) + if useStdin == false && errors.Is(err, os.ErrDeadlineExceeded) { + log.Printf("I/O timeout on socket, closing connection.\n") + // Should we? Actually we can't as connClt is not known here + // connClt.Close() + scanner = nil + } continue } if useStdin == false { @@ -371,6 +379,9 @@ func processLogs(cmd *cobra.Command, args []string) { return } } + // Extend timeout after successful read (so we got an idle timeout) + connClt.SetReadDeadline(time.Now().Add(time.Duration(600) * time.Second)) + LineReadCnt.Inc() // parse log From d3fde1615eda0da1ef97329d7d24852b24a593a6 Mon Sep 17 00:00:00 2001 From: yo Date: Wed, 15 Jun 2022 11:25:56 +0200 Subject: [PATCH 058/100] BUGFIX nil pointer dereference when not using -s --- postfix-log-parser/cmd/root.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index df7bfcb..8ac741f 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -77,7 +77,7 @@ var ( File os.File Writer *bufio.Writer - Version = "1.2.9" + Version = "1.2.10" BuildInfo = promauto.NewGaugeVec(prometheus.GaugeOpts{ Name: "postfixlogparser_build_info", @@ -380,7 +380,9 @@ func processLogs(cmd *cobra.Command, args []string) { } } // Extend timeout after successful read (so we got an idle timeout) - connClt.SetReadDeadline(time.Now().Add(time.Duration(600) * time.Second)) + if useStdin == false { + connClt.SetReadDeadline(time.Now().Add(time.Duration(600) * time.Second)) + } LineReadCnt.Inc() From 8c20ccceac1a6b59952043e9bd51f8e562dd7d92 Mon Sep 17 00:00:00 2001 From: yo Date: Wed, 23 Jun 2021 17:00:39 +0200 Subject: [PATCH 059/100] Added counter for msg accepted by smtpd --- postfix-log-parser/cmd/root.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index ff2ecd8..cd574c4 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -74,7 +74,7 @@ var ( File os.File Writer *bufio.Writer - Version = "1.2.5.1" + Version = "1.2.5.2" BuildInfo = promauto.NewGaugeVec(prometheus.GaugeOpts{ Name: "postfixlogparser_build_info", @@ -96,6 +96,10 @@ var ( Name: "postfixlogparser_line_out_count", Help: "Number of lines written to ouput", }, []string{"host"}) + MsgInCnt = promauto.NewCounterVec(prometheus.CounterOpts{ + Name: "postfixlogparser_msg_in_count", + Help: "Number of mails accepted by smtpd", + }, []string{"host"}) MsgSentCnt = promauto.NewCounterVec(prometheus.CounterOpts{ Name: "postfixlogparser_msg_sent_count", Help: "Number of mails sent", @@ -293,6 +297,7 @@ func NewCmdRoot() *cobra.Command { SaslMethod: logFormat.SaslMethod, SaslUsername: logFormat.SaslUsername, } + MsgInCnt.WithLabelValues(logFormat.Hostname).Inc() } /* From 95b88edca7e46d9bfcfb593c27a62f76ee0c6e85 Mon Sep 17 00:00:00 2001 From: yo Date: Wed, 23 Jun 2021 17:00:54 +0200 Subject: [PATCH 060/100] dependencies update --- go.mod | 18 +- go.sum | 569 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 585 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 22dcf41..e066c23 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,22 @@ module github.com/yo000/postfix-log-parser require ( - github.com/spf13/cobra v0.0.3 - github.com/spf13/pflag v1.0.3 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.1.1 // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/google/go-cmp v0.5.6 // indirect + github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect + github.com/prometheus/client_golang v1.11.0 + github.com/prometheus/client_model v0.2.0 // indirect + github.com/prometheus/common v0.29.0 // indirect + github.com/prometheus/procfs v0.6.0 // indirect + github.com/spf13/cobra v1.1.3 + github.com/spf13/pflag v1.0.5 // indirect + github.com/tabalt/pidfile v1.1.0 + golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 // indirect + golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect + google.golang.org/protobuf v1.26.0 // indirect ) go 1.13 diff --git a/go.sum b/go.sum index 385007d..fd948a3 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,573 @@ +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 v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +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/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +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/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +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/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/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 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +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/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +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/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.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +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/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/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/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +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/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +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-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +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-20191227052852-215e87163ea7/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/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +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.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +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/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +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.4.1/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.1/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-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +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/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +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/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +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/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +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-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/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/sdk v0.1.1/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-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-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.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/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +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.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +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/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +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.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/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +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/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 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/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/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/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +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/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/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ= +github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/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.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= +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.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/common v0.29.0 h1:3jqPBvKT4OHAbje2Ql7KeaaSicDBCxMYwEJU1zRJceE= +github.com/prometheus/common v0.29.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/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.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +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/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +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.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +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/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 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v1.1.3 h1:xghbfqPkxzxP3C/f3n5DdpAbdKLj4ZE4BWQI362l53M= +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.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +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/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/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/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/tabalt/pidfile v1.1.0 h1:Q7qQGZ4MoAXE+rvM5tB4/eAIrawewYewByhMiPoDE50= +github.com/tabalt/pidfile v1.1.0/go.mod h1:7F1QwNrjfAApsuX4Nyah3RsbHVAdY/D9qZWp0nnJ/Uw= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +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.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +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-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/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/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/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +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/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +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.1.1-0.20191107180719-034126e5016b/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/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-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-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-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-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/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-20200222125558-5a598a2470a0/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-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/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-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20210525063256-abc453219eb5/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= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +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-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-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/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-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-20190215142949-d0b11bdaac8a/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-20190412213103-97732733099d/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-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/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-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/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-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 h1:RqytpXGR1iVNX7psjB3ff8y7sNFinVFvkx1c8SjBkio= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +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.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +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-20180917221912-90fa682c2a6e/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-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-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-20190606124116-d0a3d012864b/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-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-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/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-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +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 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +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/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +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/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +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-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-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/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.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +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/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +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/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +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.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +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= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= From 04e4ef2f7af99a0b4f89a6d1b79af7487ec59fde Mon Sep 17 00:00:00 2001 From: yo Date: Wed, 23 Jun 2021 17:57:19 +0200 Subject: [PATCH 061/100] Count msg in more precisely, with nrcpt --- postfix-log-parser/cmd/root.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index cd574c4..367b460 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -10,6 +10,7 @@ import ( "os/signal" "runtime" "strings" + "strconv" "sync" "syscall" "time" @@ -74,7 +75,7 @@ var ( File os.File Writer *bufio.Writer - Version = "1.2.5.2" + Version = "1.2.5.3" BuildInfo = promauto.NewGaugeVec(prometheus.GaugeOpts{ Name: "postfixlogparser_build_info", @@ -297,7 +298,6 @@ func NewCmdRoot() *cobra.Command { SaslMethod: logFormat.SaslMethod, SaslUsername: logFormat.SaslUsername, } - MsgInCnt.WithLabelValues(logFormat.Hostname).Inc() } /* @@ -318,6 +318,8 @@ func NewCmdRoot() *cobra.Command { plp.Size = logFormat.Size plp.NRcpt = logFormat.NRcpt } + nrcpt,_ := strconv.ParseFloat(logFormat.NRcpt, 64) + MsgInCnt.WithLabelValues(logFormat.Hostname).Add(nrcpt) } /* From e90a565c2f4cd2ee4f1c9aa286e6689c9a464c30 Mon Sep 17 00:00:00 2001 From: yo Date: Tue, 12 Apr 2022 20:44:53 +0200 Subject: [PATCH 062/100] Test milter-reject --- test/test-milter.log | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/test/test-milter.log b/test/test-milter.log index e21d33d..f397ef9 100644 --- a/test/test-milter.log +++ b/test/test-milter.log @@ -1,6 +1,16 @@ -2021-02-06T10:57:57+01:00 mail.server.com postfix/smtpd[59633]: D6DDA23B0F: client=unknown[99.88.77.66] +021-02-06T10:57:57+01:00 mail.server.com postfix/smtpd[59633]: D6DDA23B0F: client=unknown[99.88.77.66] 2021-02-06T10:57:57+01:00 mail.server.com postfix/cleanup[59586]: D6DDA23B0F: message-id=<20210206095814.2B34E19A19F6@host.sender.domain> 2021-02-06T10:57:58+01:00 mail.server.com postfix/cleanup[59586]: D6DDA23B0F: milter-reject: END-OF-MESSAGE from unknown[99.88.77.66]: 4.7.1 Greylisting in action, try again later; from= to= proto=ESMTP helo= 2021-02-19T14:32:14.936091+01:00 smtp01.example.org postfix/smtpd[58176] E89FA2DEDA: client=srv05.source.com[10.11.12.13] 2021-02-19T14:32:14.938357+01:00 smtp01.example.org postfix/cleanup[52106] E89FA2DEDA: message-id=<20210219133213.5D10C499CA@srv.domain.org> 2021-02-19T14:32:14.976132+01:00 smtp01.example.org postfix/cleanup[52106] E89FA2DEDA: milter-hold: END-OF-MESSAGE from srv05.source.com[10.11.12.13]: milter triggers HOLD action; from=<> to= proto=ESMTP helo= + + +2021-12-03T06:27:32.797930+01:00 smtp01.example.org postfix/smtpd[50419] connect from 42-64-53-170.subs.proxad.net[42.64.53.170] +2021-12-03T06:27:33.887389+01:00 smtp01.example.org postfix/smtpd[50419] B42E77AB32: client=42-64-53-170.subs.proxad.net[42.64.53.170], sasl_method=PLAIN, sasl_username=user42@smtp01.example.com +2021-12-03T06:27:33.923496+01:00 smtp01.example.org postfix/smtpd[50419] B42E77AB32: filter: RCPT from 42-64-53-170.subs.proxad.net[42.64.53.170]: : Recipient address triggers FILTER relay:[smtp01.example.org]; from= to= proto=ESMTP helo=<[172.18.0.2]> +2021-12-03T06:27:33.965888+01:00 smtp01.example.org postfix/cleanup[19657] B42E77AB32: replace: header Received: from [172.18.0.2] (42-64-53-170.subs.proxad.net [42.64.53.170])??(Authenticated sender: user42@smtp01.example.com)??by smtp01.example.org (Postfix) with ESMTPA id D89B from 42-64-53-170.subs.proxad.net[42.64.53.170]; from= to= proto=ESMTP helo=<[172.18.0.2]>: Received: from [172.18.0.2] (42-64-53-170.subs.proxad.net [42.64.53.170])??(Authenticated sender: youssef)??by smtp01.example.org (Postfix) with ESMTPA id B42E77AB32;??Wed, 22 Dec 2021 13:59:32 +0100 (CET) +2021-12-03T06:27:33.965947+01:00 smtp01.example.org postfix/cleanup[19657] B42E77AB32: message-id=<164017759823.25920.10144256874843932242@12f2bb757669> +2021-12-03T06:27:34.105536+01:00 smtp01.example.org postfix/cleanup[19657] B42E77AB32: milter-reject: END-OF-MESSAGE from 42-64-53-170.subs.proxad.net[42.64.53.170]: 4.7.1 Ratelimit "05" exceeded; from= to= proto=ESMTP helo=<[172.18.0.2]> +2021-12-03T06:27:34.135099+01:00 smtp01.example.org postfix/smtpd[50419] disconnect from 42-64-53-170.subs.proxad.net[42.64.53.170] ehlo=1 auth=1 mail=1 rcpt=2 data=0/1 rset=1 quit=1 commands=7/8 + From 13f4500b84c8d7263b281ca9911795c7f54dd7ad Mon Sep 17 00:00:00 2001 From: yo Date: Tue, 12 Apr 2022 20:45:19 +0200 Subject: [PATCH 063/100] Periodically clean mqueue --- postfix-log-parser/cmd/root.go | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index 367b460..89f54b4 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -75,7 +75,7 @@ var ( File os.File Writer *bufio.Writer - Version = "1.2.5.3" + Version = "1.2.6" BuildInfo = promauto.NewGaugeVec(prometheus.GaugeOpts{ Name: "postfixlogparser_build_info", @@ -185,6 +185,32 @@ func writeOut(msg string, filename string) error { return nil } + +// Every 24H, remove sent, milter-rejected and deferred that entered queue more than 5 days ago +func periodicallyCleanMQueue(mqueue map[string]*PostfixLogParser) { + var ok int + + for range time.Tick(time.Hour * 24) { + for _, inmail := range mqueue { + ok = 0 + // Check all mails were sent (multiple destinations mails) + // or rejected + for _, outmail := range inmail.Messages { + if outmail.Status == "sent" || outmail.Status == "milter-reject" { + ok += 1 + } else if outmail.Status == "deferred" { + if inmail.Time.Add(time.Hour * 5 * 24).Before(time.Now()) { + ok += 1 + } + } + } + if ok == len(inmail.Messages) { + delete(mqueue, inmail.MessageId) + } + } + } +} + func NewCmdRoot() *cobra.Command { var flatten bool var outputFile string @@ -266,6 +292,9 @@ func NewCmdRoot() *cobra.Command { }() } + // Cleaner thread + go periodicallyCleanMQueue(m) + // input stdin scanner := bufio.NewScanner(os.Stdin) for scanner.Scan() { @@ -287,7 +316,7 @@ func NewCmdRoot() *cobra.Command { /* Oct 10 04:02:02 mail.example.com postfix/smtpd[22941]: DFBEFDBF00C5: client=example.net[127.0.0.1], sasl_method=PLAIN, sasl_username=user@example.com */ - if logFormat.ClientHostname != "" { + if logFormat.ClientHostname != "" && !strings.HasPrefix(logFormat.Messages, "milter-reject:") { m[logFormat.QueueId] = &PostfixLogParser{ Time: logFormat.Time, Hostname: logFormat.Hostname, From b868a5d60792467062dfa54b575729afec8d27b9 Mon Sep 17 00:00:00 2001 From: yo Date: Mon, 25 Apr 2022 10:35:57 +0200 Subject: [PATCH 064/100] Cleaning --- postfix-log-parser/cmd/root.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index 89f54b4..1cc6eaa 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -262,7 +262,7 @@ func NewCmdRoot() *cobra.Command { p := postfixlog.NewPostfixLog() // Get a writer, file or stdout - Writer, File, err := NewWriter(outputFile) + _, File, err := NewWriter(outputFile) if err != nil { cmd.SetOutput(os.Stderr) cmd.Println(err) @@ -278,9 +278,8 @@ func NewCmdRoot() *cobra.Command { <-sig mtx.Lock() fmt.Println("SIGUSR1 received, recreating output file") - //Writer.Flush() // Done by File.CLose() File.Close() - Writer, File, err = NewWriter(outputFile) + _, File, err = NewWriter(outputFile) if err != nil { mtx.Unlock() cmd.SetOutput(os.Stderr) From 17e0e0aa35be0c07af9a10d377575b82a562a4ce Mon Sep 17 00:00:00 2001 From: yo Date: Tue, 7 Jun 2022 13:13:04 +0200 Subject: [PATCH 065/100] RootCmd reorg --- postfix-log-parser/cmd/execute.go | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 postfix-log-parser/cmd/execute.go diff --git a/postfix-log-parser/cmd/execute.go b/postfix-log-parser/cmd/execute.go deleted file mode 100644 index 530f8a7..0000000 --- a/postfix-log-parser/cmd/execute.go +++ /dev/null @@ -1,15 +0,0 @@ -package cmd - -import "os" - -func initConfig() {} - -func Execute() { - cmd := NewCmdRoot() - cmd.SetOutput(os.Stdout) - if err := cmd.Execute(); err != nil { - cmd.SetOutput(os.Stderr) - cmd.Println(err) - os.Exit(1) - } -} From cb5a2339c273829a3fbe3a52f4d1a5da1807a977 Mon Sep 17 00:00:00 2001 From: yo Date: Tue, 7 Jun 2022 13:13:46 +0200 Subject: [PATCH 066/100] Accept input from TCP port with -s switch. Only 1 client supported. --- postfix-log-parser/cmd/root.go | 566 ++++++++++++++++++--------------- 1 file changed, 313 insertions(+), 253 deletions(-) diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index 1cc6eaa..4191ae9 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -5,12 +5,13 @@ import ( "encoding/json" "fmt" "log" + "net" "net/http" "os" "os/signal" "runtime" - "strings" "strconv" + "strings" "sync" "syscall" "time" @@ -75,7 +76,7 @@ var ( File os.File Writer *bufio.Writer - Version = "1.2.6" + Version = "1.2.7c" BuildInfo = promauto.NewGaugeVec(prometheus.GaugeOpts{ Name: "postfixlogparser_build_info", @@ -121,8 +122,32 @@ var ( Name: "postfixlogparser_msg_hold_count", Help: "Number of mails hold", }, []string{"host"}) + + rootCmd = &cobra.Command{ + Use: "postfix-log-parser", + Short: "Postfix Log Parser v" + Version + ". Parse postfix log, and output json format", + //Long: ``, + Run: func(cmd *cobra.Command, args []string) { + processLogs(cmd, args) + }, + } + + gFlatten bool + gOutputFile string + gPidFilePath string + gSyslogListenAddress string + gPromListenAddress string + gPromMetricPath string ) +func Execute() { + if err := rootCmd.Execute(); err != nil { + rootCmd.SetOutput(os.Stderr) + rootCmd.Println(err) + os.Exit(1) + } +} + func PlpToFlat(plp *PostfixLogParser) []PostfixLogParserFlat { var plpf = make([]PostfixLogParserFlat, len(plp.Messages)) @@ -185,7 +210,6 @@ func writeOut(msg string, filename string) error { return nil } - // Every 24H, remove sent, milter-rejected and deferred that entered queue more than 5 days ago func periodicallyCleanMQueue(mqueue map[string]*PostfixLogParser) { var ok int @@ -211,291 +235,327 @@ func periodicallyCleanMQueue(mqueue map[string]*PostfixLogParser) { } } -func NewCmdRoot() *cobra.Command { - var flatten bool - var outputFile string - var pidFilePath string - var promListenAddress string - var promMetricPath string +func initConfig() {} + +func init() { + + rootCmd.Flags().BoolVarP(&gFlatten, "gFlatten", "f", false, "Flatten output for using with syslog") + rootCmd.Flags().StringVarP(&gOutputFile, "out", "o", "", "Output to file, append if exists") + rootCmd.Flags().StringVarP(&gPidFilePath, "pidfile", "p", "", "pid file path") + rootCmd.Flags().StringVarP(&gSyslogListenAddress, "syslog.listen-address", "s", "do-not-listen", "Address to listen on for syslog incoming messages. Default is to parse stdin") + rootCmd.Flags().StringVarP(&gPromListenAddress, "prom.listen-address", "l", "do-not-listen", "Address to listen on for prometheus metrics") + rootCmd.Flags().StringVarP(&gPromMetricPath, "prom.telemetry-path", "m", "/metrics", "Path under which to expose metrics.") + + cobra.OnInitialize(initConfig) +} + +func processLogs(cmd *cobra.Command, args []string) { + var scanner *bufio.Scanner + var listener net.Listener var mtx sync.Mutex + var useStdin bool + + fmt.Printf("postfix-log-parser v%s\n", Version) + BuildInfo.WithLabelValues(Version, runtime.Version()).Set(1) + StartTime.Set(float64(time.Now().Unix())) + + // Prometheus exporter + if gPromListenAddress != "do-not-listen" { + go func() { + http.Handle(gPromMetricPath, promhttp.Handler()) + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte(` + + Postfix-log-parser Exporter + +

Postfix-log-parser Exporter

+

Metrics

+ + `)) + }) + log.Fatal(http.ListenAndServe(gPromListenAddress, nil)) + }() + } - cmd := &cobra.Command{ - Use: "postfix-log-parser", - Short: "Parse postfix log, and output json format", - //Long: ``, - Run: func(cmd *cobra.Command, args []string) { + // Create PID file + if len(gPidFilePath) > 0 { + if pid, err := pidfile.Create(gPidFilePath); err != nil { + log.Fatal(err) + } else { + defer pid.Clear() + } + } - BuildInfo.WithLabelValues(Version, runtime.Version()).Set(1) - StartTime.Set(float64(time.Now().Unix())) - - // Prometheus exporter - if promListenAddress != "do-not-listen" { - go func() { - http.Handle(promMetricPath, promhttp.Handler()) - http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - w.Write([]byte(` - - Postfix-log-parser Exporter - -

Postfix-log-parser Exporter

-

Metrics

- - `)) - }) - log.Fatal(http.ListenAndServe(promListenAddress, nil)) - }() - } + // create queue + m := make(map[string]*PostfixLogParser) - // Create PID file - if len(pidFilePath) > 0 { - if pid, err := pidfile.Create(pidFilePath); err != nil { - log.Fatal(err) - } else { - defer pid.Clear() + // initialize + p := postfixlog.NewPostfixLog() + + // Get a writer, file or stdout + _, File, err := NewWriter(gOutputFile) + if err != nil { + cmd.SetOutput(os.Stderr) + cmd.Println(err) + os.Exit(1) + } + + // Manage output file rotation when receiving SIGUSR1 + if len(gOutputFile) > 0 { + sig := make(chan os.Signal) + signal.Notify(sig, syscall.SIGUSR1) + go func() { + for { + <-sig + mtx.Lock() + fmt.Println("SIGUSR1 received, recreating output file") + File.Close() + _, File, err = NewWriter(gOutputFile) + if err != nil { + mtx.Unlock() + cmd.SetOutput(os.Stderr) + cmd.Println(err) + os.Exit(1) } + mtx.Unlock() } + }() + } - // create queue - m := make(map[string]*PostfixLogParser) + // Cleaner thread + go periodicallyCleanMQueue(m) - // initialize - p := postfixlog.NewPostfixLog() + // Initialize Stdin input + if true == strings.EqualFold(gSyslogListenAddress, "do-not-listen") { + useStdin = true + scanner = bufio.NewScanner(os.Stdin) + } else { + listener, err = net.Listen("tcp", gSyslogListenAddress) + if err != nil { + log.Fatal(fmt.Sprintf("Error listening on %s: %v\n", gSyslogListenAddress, err)) + } + } - // Get a writer, file or stdout - _, File, err := NewWriter(outputFile) - if err != nil { - cmd.SetOutput(os.Stderr) - cmd.Println(err) - os.Exit(1) + for { + // If input is made via TCP Conn, we need to read from a connected net.Conn + if useStdin == false { + if scanner == nil { + // We support _only one_ concurent connection to the service + connClt, err := listener.Accept() + if err != nil { + log.Printf("Error accepting: %v", err) + // Loop + continue + } + // Read will fail if no data after "duration" + connClt.SetReadDeadline(time.Now().Add(time.Duration(3600) * time.Second)) + scanner = bufio.NewScanner(connClt) } + } - // Manage output file rotation when receiving SIGUSR1 - if len(outputFile) > 0 { - sig := make(chan os.Signal) - signal.Notify(sig, syscall.SIGUSR1) - go func() { - for { - <-sig - mtx.Lock() - fmt.Println("SIGUSR1 received, recreating output file") - File.Close() - _, File, err = NewWriter(outputFile) - if err != nil { - mtx.Unlock() - cmd.SetOutput(os.Stderr) - cmd.Println(err) - os.Exit(1) - } - mtx.Unlock() - } - }() + if false == scanner.Scan() { + // After Scan returns false, the Err method will return any error that occurred during scanning, except that if it was io.EOF, Err will return nil + if err := scanner.Err(); err != nil { + log.Printf("Error reading data: %v\n", err.Error()) + continue + } + if useStdin == false { + // close connection so we can Accept() again, then loop + scanner = nil + continue + } else { + // stdin is dead, abort mission! + return } + } + LineReadCnt.Inc() - // Cleaner thread - go periodicallyCleanMQueue(m) + // parse log + logFormat, err := p.Parse(scanner.Bytes()) + if err != nil { + // Incorrect line, just skip it + if err.Error() == "Error: Line do not match regex" { + LineIncorrectCnt.Inc() + continue + } + cmd.SetOutput(os.Stderr) + cmd.Println(err) + os.Exit(1) + } - // input stdin - scanner := bufio.NewScanner(os.Stdin) - for scanner.Scan() { - LineReadCnt.Inc() + /* + Oct 10 04:02:02 mail.example.com postfix/smtpd[22941]: DFBEFDBF00C5: client=example.net[127.0.0.1], sasl_method=PLAIN, sasl_username=user@example.com + */ + if logFormat.ClientHostname != "" && !strings.HasPrefix(logFormat.Messages, "milter-reject:") { + m[logFormat.QueueId] = &PostfixLogParser{ + Time: logFormat.Time, + Hostname: logFormat.Hostname, + Process: logFormat.Process, + QueueId: logFormat.QueueId, + ClientHostname: logFormat.ClientHostname, + ClinetIp: logFormat.ClinetIp, + SaslMethod: logFormat.SaslMethod, + SaslUsername: logFormat.SaslUsername, + } + } - // parse log - logFormat, err := p.Parse(scanner.Bytes()) - if err != nil { - // Incorrect line, just skip it - if err.Error() == "Error: Line do not match regex" { - LineIncorrectCnt.Inc() - continue - } - cmd.SetOutput(os.Stderr) - cmd.Println(err) - os.Exit(1) - } + /* + Oct 10 04:02:02 mail.example.com postfix/cleanup[22923]: DFBEFDBF00C5: message-id=<20181009190202.81363306015D@example.com> + */ + if logFormat.MessageId != "" { + if plp, ok := m[logFormat.QueueId]; ok { + plp.MessageId = logFormat.MessageId + } + } - /* - Oct 10 04:02:02 mail.example.com postfix/smtpd[22941]: DFBEFDBF00C5: client=example.net[127.0.0.1], sasl_method=PLAIN, sasl_username=user@example.com - */ - if logFormat.ClientHostname != "" && !strings.HasPrefix(logFormat.Messages, "milter-reject:") { - m[logFormat.QueueId] = &PostfixLogParser{ - Time: logFormat.Time, - Hostname: logFormat.Hostname, - Process: logFormat.Process, - QueueId: logFormat.QueueId, - ClientHostname: logFormat.ClientHostname, - ClinetIp: logFormat.ClinetIp, - SaslMethod: logFormat.SaslMethod, - SaslUsername: logFormat.SaslUsername, - } + /* + Oct 10 04:02:03 mail.example.com postfix/qmgr[18719]: DFBEFDBF00C5: from=, size=3578, nrcpt=1 (queue active) + */ + if logFormat.From != "" { + if plp, ok := m[logFormat.QueueId]; ok { + plp.From = logFormat.From + plp.Size = logFormat.Size + plp.NRcpt = logFormat.NRcpt + } + nrcpt, _ := strconv.ParseFloat(logFormat.NRcpt, 64) + MsgInCnt.WithLabelValues(logFormat.Hostname).Add(nrcpt) + } + + /* + Oct 10 04:02:08 mail.example.com postfix/smtp[22928]: DFBEFDBF00C5: to=, relay=mail.example-to.com[192.168.0.10]:25, delay=5.3, delays=0.26/0/0.31/4.7, dsn=2.0.0, status=sent (250 2.0.0 Ok: queued as C598F1B0002D) + */ + if logFormat.To != "" { + if plp, ok := m[logFormat.QueueId]; ok { + message := Message{ + Time: logFormat.Time, + To: logFormat.To, + Status: logFormat.Status, + Message: logFormat.Messages, + BounceId: "", } - /* - Oct 10 04:02:02 mail.example.com postfix/cleanup[22923]: DFBEFDBF00C5: message-id=<20181009190202.81363306015D@example.com> + /* When a message is deferred, it won't be written out until it is either sent, expired, or generates a non delivery notification. + We want to know instantly when a message is deferred, so we handle this case by emiting output for this message, and not appending this occurence + to the list of Messages */ - if logFormat.MessageId != "" { - if plp, ok := m[logFormat.QueueId]; ok { - plp.MessageId = logFormat.MessageId + if logFormat.Status == "deferred" { + MsgDeferredCnt.WithLabelValues(plp.Hostname).Inc() + tmpplp := PostfixLogParser{ + Time: plp.Time, + Hostname: plp.Hostname, + Process: plp.Process, + QueueId: plp.QueueId, + ClientHostname: plp.ClientHostname, + ClinetIp: plp.ClinetIp, + SaslMethod: plp.SaslMethod, + SaslUsername: plp.SaslUsername, + MessageId: plp.MessageId, + From: plp.From, + Size: plp.Size, + NRcpt: plp.NRcpt, } - } + tmpplp.Messages = append(tmpplp.Messages, message) - /* - Oct 10 04:02:03 mail.example.com postfix/qmgr[18719]: DFBEFDBF00C5: from=, size=3578, nrcpt=1 (queue active) - */ - if logFormat.From != "" { - if plp, ok := m[logFormat.QueueId]; ok { - plp.From = logFormat.From - plp.Size = logFormat.Size - plp.NRcpt = logFormat.NRcpt + var jsonBytes []byte + if gFlatten { + jsonBytes, err = json.Marshal(PlpToFlat(&tmpplp)[0]) + } else { + jsonBytes, err = json.Marshal(tmpplp) + } + if err != nil { + log.Fatal(err) + } + mtx.Lock() + err = writeOut(string(jsonBytes), gOutputFile) + mtx.Unlock() + if err != nil { + log.Fatal(err) } - nrcpt,_ := strconv.ParseFloat(logFormat.NRcpt, 64) - MsgInCnt.WithLabelValues(logFormat.Hostname).Add(nrcpt) + tmpplp.Messages = nil + // cannot use nil as type PostfixLogParser in assignment + //tmpplp = nil + } else { + plp.Messages = append(plp.Messages, message) } + } + } - /* - Oct 10 04:02:08 mail.example.com postfix/smtp[22928]: DFBEFDBF00C5: to=, relay=mail.example-to.com[192.168.0.10]:25, delay=5.3, delays=0.26/0/0.31/4.7, dsn=2.0.0, status=sent (250 2.0.0 Ok: queued as C598F1B0002D) - */ - if logFormat.To != "" { - if plp, ok := m[logFormat.QueueId]; ok { + /* + 2021-02-05T17:25:03+01:00 mail.example.com postfix/bounce[39258]: 006B056E6: sender non-delivery notification: 642E456E9 + */ + if logFormat.BounceId != "" { + if plp, ok := m[logFormat.QueueId]; ok { + // Get the matching Message by Status=bounced + for i, msg := range plp.Messages { + // Need to manage more than one bounce for the same queue_id. This is flawy as we just rely on order to match + if msg.Status == "bounced" && len(msg.BounceId) == 0 { message := Message{ - Time: logFormat.Time, - To: logFormat.To, - Status: logFormat.Status, - Message: logFormat.Messages, - BounceId: "", - } - - /* When a message is deferred, it won't be written out until it is either sent, expired, or generates a non delivery notification. - We want to know instantly when a message is deferred, so we handle this case by emiting output for this message, and not appending this occurence - to the list of Messages - */ - if logFormat.Status == "deferred" { - MsgDeferredCnt.WithLabelValues(plp.Hostname).Inc() - tmpplp := PostfixLogParser{ - Time: plp.Time, - Hostname: plp.Hostname, - Process: plp.Process, - QueueId: plp.QueueId, - ClientHostname: plp.ClientHostname, - ClinetIp: plp.ClinetIp, - SaslMethod: plp.SaslMethod, - SaslUsername: plp.SaslUsername, - MessageId: plp.MessageId, - From: plp.From, - Size: plp.Size, - NRcpt: plp.NRcpt, - } - tmpplp.Messages = append(tmpplp.Messages, message) - - var jsonBytes []byte - if flatten { - jsonBytes, err = json.Marshal(PlpToFlat(&tmpplp)[0]) - } else { - jsonBytes, err = json.Marshal(tmpplp) - } - if err != nil { - log.Fatal(err) - } - mtx.Lock() - err = writeOut(string(jsonBytes), outputFile) - mtx.Unlock() - if err != nil { - log.Fatal(err) - } - tmpplp.Messages = nil - // cannot use nil as type PostfixLogParser in assignment - //tmpplp = nil - } else { - plp.Messages = append(plp.Messages, message) + Time: msg.Time, + To: msg.To, + Status: msg.Status, + Message: msg.Message, + BounceId: logFormat.BounceId, } + // Delete old message, put new at the end + copy(plp.Messages[i:], plp.Messages[i+1:]) + plp.Messages[len(plp.Messages)-1] = message + break } } + } + } + /* + Oct 10 04:02:08 mail.example.com postfix/qmgr[18719]: DFBEFDBF00C5: removed + or + 2021-02-05T14:17:51+01:00 smtp.server.com postfix/cleanup[38982]: D8C136A3A: milter-reject: END-OF-MESSAGE from unknown[1.2.3.4]: 4.7.1 Greylisting in action, try again later; from= to= proto=ESMTP helo= + */ + // "removed" message is end of logs. then flush. + if logFormat.Messages == "removed" || strings.HasPrefix(logFormat.Status, "milter-") { + if plp, ok := m[logFormat.QueueId]; ok { + for _, plpf := range PlpToFlat(plp) { + switch plpf.Status { + case "sent": + MsgSentCnt.WithLabelValues(plpf.Hostname).Inc() + case "milter-reject": + MsgRejectedCnt.WithLabelValues(plpf.Hostname).Inc() + case "milter-hold": + MsgHoldCnt.WithLabelValues(plpf.Hostname).Inc() + case "bounced": + MsgBouncedCnt.WithLabelValues(plpf.Hostname).Inc() + } - /* - 2021-02-05T17:25:03+01:00 mail.example.com postfix/bounce[39258]: 006B056E6: sender non-delivery notification: 642E456E9 - */ - if logFormat.BounceId != "" { - if plp, ok := m[logFormat.QueueId]; ok { - // Get the matching Message by Status=bounced - for i, msg := range plp.Messages { - // Need to manage more than one bounce for the same queue_id. This is flawy as we just rely on order to match - if msg.Status == "bounced" && len(msg.BounceId) == 0 { - message := Message{ - Time: msg.Time, - To: msg.To, - Status: msg.Status, - Message: msg.Message, - BounceId: logFormat.BounceId, - } - // Delete old message, put new at the end - copy(plp.Messages[i:], plp.Messages[i+1:]) - plp.Messages[len(plp.Messages)-1] = message - break - } + if gFlatten { + jsonBytes, err := json.Marshal(plpf) + if err != nil { + log.Fatal(err) + } + mtx.Lock() + err = writeOut(string(jsonBytes), gOutputFile) + mtx.Unlock() + if err != nil { + log.Fatal(err) } } } - /* - Oct 10 04:02:08 mail.example.com postfix/qmgr[18719]: DFBEFDBF00C5: removed - or - 2021-02-05T14:17:51+01:00 smtp.server.com postfix/cleanup[38982]: D8C136A3A: milter-reject: END-OF-MESSAGE from unknown[1.2.3.4]: 4.7.1 Greylisting in action, try again later; from= to= proto=ESMTP helo= - */ - // "removed" message is end of logs. then flush. - if logFormat.Messages == "removed" || strings.HasPrefix(logFormat.Status, "milter-") { - if plp, ok := m[logFormat.QueueId]; ok { - for _, plpf := range PlpToFlat(plp) { - switch plpf.Status { - case "sent": - MsgSentCnt.WithLabelValues(plpf.Hostname).Inc() - case "milter-reject": - MsgRejectedCnt.WithLabelValues(plpf.Hostname).Inc() - case "milter-hold": - MsgHoldCnt.WithLabelValues(plpf.Hostname).Inc() - case "bounced": - MsgBouncedCnt.WithLabelValues(plpf.Hostname).Inc() - } - - if flatten { - jsonBytes, err := json.Marshal(plpf) - if err != nil { - log.Fatal(err) - } - mtx.Lock() - err = writeOut(string(jsonBytes), outputFile) - mtx.Unlock() - if err != nil { - log.Fatal(err) - } - } - } - if !flatten { - jsonBytes, err := json.Marshal(plp) - if err != nil { - log.Fatal(err) - } - mtx.Lock() - err = writeOut(string(jsonBytes), outputFile) - mtx.Unlock() - if err != nil { - log.Fatal(err) - } - } + if !gFlatten { + jsonBytes, err := json.Marshal(plp) + if err != nil { + log.Fatal(err) + } + mtx.Lock() + err = writeOut(string(jsonBytes), gOutputFile) + mtx.Unlock() + if err != nil { + log.Fatal(err) } } } - if File != nil { - mtx.Lock() - File.Close() - mtx.Unlock() - } - }, + } + } + if File != nil { + mtx.Lock() + File.Close() + mtx.Unlock() } - - cmd.Flags().BoolVarP(&flatten, "flatten", "f", false, "Flatten output for using with syslog") - cmd.Flags().StringVarP(&outputFile, "out", "o", "", "Output to file, append if exists") - cmd.Flags().StringVarP(&pidFilePath, "pidfile", "p", "", "pid file path") - cmd.Flags().StringVarP(&promListenAddress, "web.listen-address", "l", "do-not-listen", "Address to listen on for web interface and telemetry") - cmd.Flags().StringVarP(&promMetricPath, "web.telemetry-path", "m", "/metrics", "Path under which to expose metrics.") - - cobra.OnInitialize(initConfig) - return cmd } From 0978ff09df6f35935df423f03bab0fd9eecfbcd3 Mon Sep 17 00:00:00 2001 From: yo Date: Tue, 7 Jun 2022 13:42:55 +0200 Subject: [PATCH 067/100] Add version flag, remove printing it to stdout at execution time --- postfix-log-parser/cmd/root.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index 4191ae9..8259eb6 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -239,6 +239,8 @@ func initConfig() {} func init() { + rootCmd.Version = Version + rootCmd.Flags().BoolVarP(&gFlatten, "gFlatten", "f", false, "Flatten output for using with syslog") rootCmd.Flags().StringVarP(&gOutputFile, "out", "o", "", "Output to file, append if exists") rootCmd.Flags().StringVarP(&gPidFilePath, "pidfile", "p", "", "pid file path") @@ -255,7 +257,8 @@ func processLogs(cmd *cobra.Command, args []string) { var mtx sync.Mutex var useStdin bool - fmt.Printf("postfix-log-parser v%s\n", Version) + // Nope, breaks stdout output interpretation by jq + //fmt.Printf("postfix-log-parser v%s\n", Version) BuildInfo.WithLabelValues(Version, runtime.Version()).Set(1) StartTime.Set(float64(time.Now().Unix())) From d8c72c173544c96b62ed3b73e218c0876de5df43 Mon Sep 17 00:00:00 2001 From: yo Date: Tue, 7 Jun 2022 13:44:41 +0200 Subject: [PATCH 068/100] Version bump --- postfix-log-parser/cmd/root.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index 8259eb6..3544791 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -76,7 +76,7 @@ var ( File os.File Writer *bufio.Writer - Version = "1.2.7c" + Version = "1.2.7" BuildInfo = promauto.NewGaugeVec(prometheus.GaugeOpts{ Name: "postfixlogparser_build_info", From a55c378e5fc1c740b9a33f5297d58a9fda2eb69e Mon Sep 17 00:00:00 2001 From: yo Date: Fri, 10 Jun 2022 22:01:27 +0200 Subject: [PATCH 069/100] Fixed miscomprehension of setDeadline, it now is an idle timeout of 10mn --- postfix-log-parser/cmd/root.go | 39 ++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index 3544791..df7bfcb 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -1,27 +1,28 @@ package cmd import ( - "bufio" - "encoding/json" + "os" "fmt" - "log" "net" + "log" + "sync" + "time" + "bufio" + "errors" "net/http" - "os" - "os/signal" "runtime" "strconv" "strings" - "sync" "syscall" - "time" + "os/signal" + "encoding/json" - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promauto" - "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/spf13/cobra" "github.com/tabalt/pidfile" postfixlog "github.com/yo000/postfix-log-parser" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" + "github.com/prometheus/client_golang/prometheus/promhttp" ) func init() {} @@ -76,7 +77,7 @@ var ( File os.File Writer *bufio.Writer - Version = "1.2.7" + Version = "1.2.9" BuildInfo = promauto.NewGaugeVec(prometheus.GaugeOpts{ Name: "postfixlogparser_build_info", @@ -339,19 +340,20 @@ func processLogs(cmd *cobra.Command, args []string) { } } + var connClt net.Conn for { // If input is made via TCP Conn, we need to read from a connected net.Conn if useStdin == false { if scanner == nil { // We support _only one_ concurent connection to the service - connClt, err := listener.Accept() + connClt, err = listener.Accept() if err != nil { log.Printf("Error accepting: %v", err) // Loop continue } - // Read will fail if no data after "duration" - connClt.SetReadDeadline(time.Now().Add(time.Duration(3600) * time.Second)) + // Read will fail after "duration" + connClt.SetReadDeadline(time.Now().Add(time.Duration(600) * time.Second)) scanner = bufio.NewScanner(connClt) } } @@ -360,6 +362,12 @@ func processLogs(cmd *cobra.Command, args []string) { // After Scan returns false, the Err method will return any error that occurred during scanning, except that if it was io.EOF, Err will return nil if err := scanner.Err(); err != nil { log.Printf("Error reading data: %v\n", err.Error()) + if useStdin == false && errors.Is(err, os.ErrDeadlineExceeded) { + log.Printf("I/O timeout on socket, closing connection.\n") + // Should we? Actually we can't as connClt is not known here + // connClt.Close() + scanner = nil + } continue } if useStdin == false { @@ -371,6 +379,9 @@ func processLogs(cmd *cobra.Command, args []string) { return } } + // Extend timeout after successful read (so we got an idle timeout) + connClt.SetReadDeadline(time.Now().Add(time.Duration(600) * time.Second)) + LineReadCnt.Inc() // parse log From 7b14269cba812f9e2e9449b618f777ef2c48fe47 Mon Sep 17 00:00:00 2001 From: yo Date: Wed, 15 Jun 2022 11:25:56 +0200 Subject: [PATCH 070/100] BUGFIX nil pointer dereference when not using -s --- postfix-log-parser/cmd/root.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index df7bfcb..8ac741f 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -77,7 +77,7 @@ var ( File os.File Writer *bufio.Writer - Version = "1.2.9" + Version = "1.2.10" BuildInfo = promauto.NewGaugeVec(prometheus.GaugeOpts{ Name: "postfixlogparser_build_info", @@ -380,7 +380,9 @@ func processLogs(cmd *cobra.Command, args []string) { } } // Extend timeout after successful read (so we got an idle timeout) - connClt.SetReadDeadline(time.Now().Add(time.Duration(600) * time.Second)) + if useStdin == false { + connClt.SetReadDeadline(time.Now().Add(time.Duration(600) * time.Second)) + } LineReadCnt.Inc() From e10d28b9c663a02948ec387dcd7642a97a998e11 Mon Sep 17 00:00:00 2001 From: yo Date: Wed, 15 Jun 2022 22:11:56 +0200 Subject: [PATCH 071/100] Rename m to mQueue --- postfix-log-parser/cmd/root.go | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index 8ac741f..717a32a 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -257,9 +257,11 @@ func processLogs(cmd *cobra.Command, args []string) { var listener net.Listener var mtx sync.Mutex var useStdin bool - - // Nope, breaks stdout output interpretation by jq - //fmt.Printf("postfix-log-parser v%s\n", Version) + + // create queue + mQueue := make(map[string]*PostfixLogParser) + + BuildInfo.WithLabelValues(Version, runtime.Version()).Set(1) StartTime.Set(float64(time.Now().Unix())) @@ -289,10 +291,7 @@ func processLogs(cmd *cobra.Command, args []string) { defer pid.Clear() } } - - // create queue - m := make(map[string]*PostfixLogParser) - + // initialize p := postfixlog.NewPostfixLog() @@ -327,7 +326,7 @@ func processLogs(cmd *cobra.Command, args []string) { } // Cleaner thread - go periodicallyCleanMQueue(m) + go periodicallyCleanMQueue(mQueue) // Initialize Stdin input if true == strings.EqualFold(gSyslogListenAddress, "do-not-listen") { @@ -403,7 +402,7 @@ func processLogs(cmd *cobra.Command, args []string) { Oct 10 04:02:02 mail.example.com postfix/smtpd[22941]: DFBEFDBF00C5: client=example.net[127.0.0.1], sasl_method=PLAIN, sasl_username=user@example.com */ if logFormat.ClientHostname != "" && !strings.HasPrefix(logFormat.Messages, "milter-reject:") { - m[logFormat.QueueId] = &PostfixLogParser{ + mQueue[logFormat.QueueId] = &PostfixLogParser{ Time: logFormat.Time, Hostname: logFormat.Hostname, Process: logFormat.Process, @@ -419,7 +418,7 @@ func processLogs(cmd *cobra.Command, args []string) { Oct 10 04:02:02 mail.example.com postfix/cleanup[22923]: DFBEFDBF00C5: message-id=<20181009190202.81363306015D@example.com> */ if logFormat.MessageId != "" { - if plp, ok := m[logFormat.QueueId]; ok { + if plp, ok := mQueue[logFormat.QueueId]; ok { plp.MessageId = logFormat.MessageId } } @@ -428,7 +427,7 @@ func processLogs(cmd *cobra.Command, args []string) { Oct 10 04:02:03 mail.example.com postfix/qmgr[18719]: DFBEFDBF00C5: from=, size=3578, nrcpt=1 (queue active) */ if logFormat.From != "" { - if plp, ok := m[logFormat.QueueId]; ok { + if plp, ok := mQueue[logFormat.QueueId]; ok { plp.From = logFormat.From plp.Size = logFormat.Size plp.NRcpt = logFormat.NRcpt @@ -441,7 +440,7 @@ func processLogs(cmd *cobra.Command, args []string) { Oct 10 04:02:08 mail.example.com postfix/smtp[22928]: DFBEFDBF00C5: to=, relay=mail.example-to.com[192.168.0.10]:25, delay=5.3, delays=0.26/0/0.31/4.7, dsn=2.0.0, status=sent (250 2.0.0 Ok: queued as C598F1B0002D) */ if logFormat.To != "" { - if plp, ok := m[logFormat.QueueId]; ok { + if plp, ok := mQueue[logFormat.QueueId]; ok { message := Message{ Time: logFormat.Time, To: logFormat.To, @@ -500,7 +499,7 @@ func processLogs(cmd *cobra.Command, args []string) { 2021-02-05T17:25:03+01:00 mail.example.com postfix/bounce[39258]: 006B056E6: sender non-delivery notification: 642E456E9 */ if logFormat.BounceId != "" { - if plp, ok := m[logFormat.QueueId]; ok { + if plp, ok := mQueue[logFormat.QueueId]; ok { // Get the matching Message by Status=bounced for i, msg := range plp.Messages { // Need to manage more than one bounce for the same queue_id. This is flawy as we just rely on order to match @@ -527,7 +526,7 @@ func processLogs(cmd *cobra.Command, args []string) { */ // "removed" message is end of logs. then flush. if logFormat.Messages == "removed" || strings.HasPrefix(logFormat.Status, "milter-") { - if plp, ok := m[logFormat.QueueId]; ok { + if plp, ok := mQueue[logFormat.QueueId]; ok { for _, plpf := range PlpToFlat(plp) { switch plpf.Status { case "sent": From 3635365b08cb78e8ddfde62356b19e90ca28d3eb Mon Sep 17 00:00:00 2001 From: yo Date: Wed, 15 Jun 2022 23:01:00 +0200 Subject: [PATCH 072/100] Main parsing code is now in a function, ready to be put in a goroutine for each TCP Conn --- postfix-log-parser/cmd/root.go | 370 +++++++++++++++++---------------- 1 file changed, 192 insertions(+), 178 deletions(-) diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index 717a32a..e7b492f 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -252,6 +252,194 @@ func init() { cobra.OnInitialize(initConfig) } +/* + * This is the function doing the work. + * Each input is stored in a map, + * then written to output when we recognize it as the last line + */ +func parseStoreAndWrite(input []byte, mq map[string]*PostfixLogParser, mtx sync.Mutex, p *postfixlog.PostfixLog) error { + logFormat, err := p.Parse(input) + if err != nil { + // Incorrect line, just skip it + if err.Error() == "Error: Line do not match regex" { + LineIncorrectCnt.Inc() + return err + } + return err + } + + /* + Oct 10 04:02:02 mail.example.com postfix/smtpd[22941]: DFBEFDBF00C5: client=example.net[127.0.0.1], sasl_method=PLAIN, sasl_username=user@example.com + */ + if logFormat.ClientHostname != "" && !strings.HasPrefix(logFormat.Messages, "milter-reject:") { + mq[logFormat.QueueId] = &PostfixLogParser{ + Time: logFormat.Time, + Hostname: logFormat.Hostname, + Process: logFormat.Process, + QueueId: logFormat.QueueId, + ClientHostname: logFormat.ClientHostname, + ClinetIp: logFormat.ClinetIp, + SaslMethod: logFormat.SaslMethod, + SaslUsername: logFormat.SaslUsername, + } + } + + /* + Oct 10 04:02:02 mail.example.com postfix/cleanup[22923]: DFBEFDBF00C5: message-id=<20181009190202.81363306015D@example.com> + */ + if logFormat.MessageId != "" { + if plp, ok := mq[logFormat.QueueId]; ok { + plp.MessageId = logFormat.MessageId + } + } + + /* + Oct 10 04:02:03 mail.example.com postfix/qmgr[18719]: DFBEFDBF00C5: from=, size=3578, nrcpt=1 (queue active) + */ + if logFormat.From != "" { + if plp, ok := mq[logFormat.QueueId]; ok { + plp.From = logFormat.From + plp.Size = logFormat.Size + plp.NRcpt = logFormat.NRcpt + } + nrcpt, _ := strconv.ParseFloat(logFormat.NRcpt, 64) + MsgInCnt.WithLabelValues(logFormat.Hostname).Add(nrcpt) + } + + /* + Oct 10 04:02:08 mail.example.com postfix/smtp[22928]: DFBEFDBF00C5: to=, relay=mail.example-to.com[192.168.0.10]:25, delay=5.3, delays=0.26/0/0.31/4.7, dsn=2.0.0, status=sent (250 2.0.0 Ok: queued as C598F1B0002D) + */ + if logFormat.To != "" { + if plp, ok := mq[logFormat.QueueId]; ok { + message := Message{ + Time: logFormat.Time, + To: logFormat.To, + Status: logFormat.Status, + Message: logFormat.Messages, + BounceId: "", + } + + /* When a message is deferred, it won't be written out until it is either sent, expired, or generates a non delivery notification. + We want to know instantly when a message is deferred, so we handle this case by emiting output for this message, and not appending this occurence + to the list of Messages + */ + if logFormat.Status == "deferred" { + MsgDeferredCnt.WithLabelValues(plp.Hostname).Inc() + tmpplp := PostfixLogParser{ + Time: plp.Time, + Hostname: plp.Hostname, + Process: plp.Process, + QueueId: plp.QueueId, + ClientHostname: plp.ClientHostname, + ClinetIp: plp.ClinetIp, + SaslMethod: plp.SaslMethod, + SaslUsername: plp.SaslUsername, + MessageId: plp.MessageId, + From: plp.From, + Size: plp.Size, + NRcpt: plp.NRcpt, + } + tmpplp.Messages = append(tmpplp.Messages, message) + + var jsonBytes []byte + if gFlatten { + jsonBytes, err = json.Marshal(PlpToFlat(&tmpplp)[0]) + } else { + jsonBytes, err = json.Marshal(tmpplp) + } + if err != nil { + log.Fatal(err) + } + mtx.Lock() + err = writeOut(string(jsonBytes), gOutputFile) + mtx.Unlock() + if err != nil { + log.Fatal(err) + } + tmpplp.Messages = nil + // cannot use nil as type PostfixLogParser in assignment + //tmpplp = nil + } else { + plp.Messages = append(plp.Messages, message) + } + } + } + + /* + 2021-02-05T17:25:03+01:00 mail.example.com postfix/bounce[39258]: 006B056E6: sender non-delivery notification: 642E456E9 + */ + if logFormat.BounceId != "" { + if plp, ok := mq[logFormat.QueueId]; ok { + // Get the matching Message by Status=bounced + for i, msg := range plp.Messages { + // Need to manage more than one bounce for the same queue_id. This is flawy as we just rely on order to match + if msg.Status == "bounced" && len(msg.BounceId) == 0 { + message := Message{ + Time: msg.Time, + To: msg.To, + Status: msg.Status, + Message: msg.Message, + BounceId: logFormat.BounceId, + } + // Delete old message, put new at the end + copy(plp.Messages[i:], plp.Messages[i+1:]) + plp.Messages[len(plp.Messages)-1] = message + break + } + } + } + } + /* + Oct 10 04:02:08 mail.example.com postfix/qmgr[18719]: DFBEFDBF00C5: removed + or + 2021-02-05T14:17:51+01:00 smtp.server.com postfix/cleanup[38982]: D8C136A3A: milter-reject: END-OF-MESSAGE from unknown[1.2.3.4]: 4.7.1 Greylisting in action, try again later; from= to= proto=ESMTP helo= + */ + // "removed" message is end of logs. then flush. + if logFormat.Messages == "removed" || strings.HasPrefix(logFormat.Status, "milter-") { + if plp, ok := mq[logFormat.QueueId]; ok { + for _, plpf := range PlpToFlat(plp) { + switch plpf.Status { + case "sent": + MsgSentCnt.WithLabelValues(plpf.Hostname).Inc() + case "milter-reject": + MsgRejectedCnt.WithLabelValues(plpf.Hostname).Inc() + case "milter-hold": + MsgHoldCnt.WithLabelValues(plpf.Hostname).Inc() + case "bounced": + MsgBouncedCnt.WithLabelValues(plpf.Hostname).Inc() + } + + if gFlatten { + jsonBytes, err := json.Marshal(plpf) + if err != nil { + log.Fatal(err) + } + mtx.Lock() + err = writeOut(string(jsonBytes), gOutputFile) + mtx.Unlock() + if err != nil { + log.Fatal(err) + } + } + } + + if !gFlatten { + jsonBytes, err := json.Marshal(plp) + if err != nil { + log.Fatal(err) + } + mtx.Lock() + err = writeOut(string(jsonBytes), gOutputFile) + mtx.Unlock() + if err != nil { + log.Fatal(err) + } + } + } + } + return nil +} + func processLogs(cmd *cobra.Command, args []string) { var scanner *bufio.Scanner var listener net.Listener @@ -384,187 +572,13 @@ func processLogs(cmd *cobra.Command, args []string) { } LineReadCnt.Inc() - - // parse log - logFormat, err := p.Parse(scanner.Bytes()) + + err = parseStoreAndWrite(scanner.Bytes(), mQueue, mtx, p) if err != nil { - // Incorrect line, just skip it - if err.Error() == "Error: Line do not match regex" { - LineIncorrectCnt.Inc() - continue - } cmd.SetOutput(os.Stderr) cmd.Println(err) - os.Exit(1) - } - - /* - Oct 10 04:02:02 mail.example.com postfix/smtpd[22941]: DFBEFDBF00C5: client=example.net[127.0.0.1], sasl_method=PLAIN, sasl_username=user@example.com - */ - if logFormat.ClientHostname != "" && !strings.HasPrefix(logFormat.Messages, "milter-reject:") { - mQueue[logFormat.QueueId] = &PostfixLogParser{ - Time: logFormat.Time, - Hostname: logFormat.Hostname, - Process: logFormat.Process, - QueueId: logFormat.QueueId, - ClientHostname: logFormat.ClientHostname, - ClinetIp: logFormat.ClinetIp, - SaslMethod: logFormat.SaslMethod, - SaslUsername: logFormat.SaslUsername, - } - } - - /* - Oct 10 04:02:02 mail.example.com postfix/cleanup[22923]: DFBEFDBF00C5: message-id=<20181009190202.81363306015D@example.com> - */ - if logFormat.MessageId != "" { - if plp, ok := mQueue[logFormat.QueueId]; ok { - plp.MessageId = logFormat.MessageId - } - } - - /* - Oct 10 04:02:03 mail.example.com postfix/qmgr[18719]: DFBEFDBF00C5: from=, size=3578, nrcpt=1 (queue active) - */ - if logFormat.From != "" { - if plp, ok := mQueue[logFormat.QueueId]; ok { - plp.From = logFormat.From - plp.Size = logFormat.Size - plp.NRcpt = logFormat.NRcpt - } - nrcpt, _ := strconv.ParseFloat(logFormat.NRcpt, 64) - MsgInCnt.WithLabelValues(logFormat.Hostname).Add(nrcpt) - } - - /* - Oct 10 04:02:08 mail.example.com postfix/smtp[22928]: DFBEFDBF00C5: to=, relay=mail.example-to.com[192.168.0.10]:25, delay=5.3, delays=0.26/0/0.31/4.7, dsn=2.0.0, status=sent (250 2.0.0 Ok: queued as C598F1B0002D) - */ - if logFormat.To != "" { - if plp, ok := mQueue[logFormat.QueueId]; ok { - message := Message{ - Time: logFormat.Time, - To: logFormat.To, - Status: logFormat.Status, - Message: logFormat.Messages, - BounceId: "", - } - - /* When a message is deferred, it won't be written out until it is either sent, expired, or generates a non delivery notification. - We want to know instantly when a message is deferred, so we handle this case by emiting output for this message, and not appending this occurence - to the list of Messages - */ - if logFormat.Status == "deferred" { - MsgDeferredCnt.WithLabelValues(plp.Hostname).Inc() - tmpplp := PostfixLogParser{ - Time: plp.Time, - Hostname: plp.Hostname, - Process: plp.Process, - QueueId: plp.QueueId, - ClientHostname: plp.ClientHostname, - ClinetIp: plp.ClinetIp, - SaslMethod: plp.SaslMethod, - SaslUsername: plp.SaslUsername, - MessageId: plp.MessageId, - From: plp.From, - Size: plp.Size, - NRcpt: plp.NRcpt, - } - tmpplp.Messages = append(tmpplp.Messages, message) - - var jsonBytes []byte - if gFlatten { - jsonBytes, err = json.Marshal(PlpToFlat(&tmpplp)[0]) - } else { - jsonBytes, err = json.Marshal(tmpplp) - } - if err != nil { - log.Fatal(err) - } - mtx.Lock() - err = writeOut(string(jsonBytes), gOutputFile) - mtx.Unlock() - if err != nil { - log.Fatal(err) - } - tmpplp.Messages = nil - // cannot use nil as type PostfixLogParser in assignment - //tmpplp = nil - } else { - plp.Messages = append(plp.Messages, message) - } - } - } - - /* - 2021-02-05T17:25:03+01:00 mail.example.com postfix/bounce[39258]: 006B056E6: sender non-delivery notification: 642E456E9 - */ - if logFormat.BounceId != "" { - if plp, ok := mQueue[logFormat.QueueId]; ok { - // Get the matching Message by Status=bounced - for i, msg := range plp.Messages { - // Need to manage more than one bounce for the same queue_id. This is flawy as we just rely on order to match - if msg.Status == "bounced" && len(msg.BounceId) == 0 { - message := Message{ - Time: msg.Time, - To: msg.To, - Status: msg.Status, - Message: msg.Message, - BounceId: logFormat.BounceId, - } - // Delete old message, put new at the end - copy(plp.Messages[i:], plp.Messages[i+1:]) - plp.Messages[len(plp.Messages)-1] = message - break - } - } - } - } - /* - Oct 10 04:02:08 mail.example.com postfix/qmgr[18719]: DFBEFDBF00C5: removed - or - 2021-02-05T14:17:51+01:00 smtp.server.com postfix/cleanup[38982]: D8C136A3A: milter-reject: END-OF-MESSAGE from unknown[1.2.3.4]: 4.7.1 Greylisting in action, try again later; from= to= proto=ESMTP helo= - */ - // "removed" message is end of logs. then flush. - if logFormat.Messages == "removed" || strings.HasPrefix(logFormat.Status, "milter-") { - if plp, ok := mQueue[logFormat.QueueId]; ok { - for _, plpf := range PlpToFlat(plp) { - switch plpf.Status { - case "sent": - MsgSentCnt.WithLabelValues(plpf.Hostname).Inc() - case "milter-reject": - MsgRejectedCnt.WithLabelValues(plpf.Hostname).Inc() - case "milter-hold": - MsgHoldCnt.WithLabelValues(plpf.Hostname).Inc() - case "bounced": - MsgBouncedCnt.WithLabelValues(plpf.Hostname).Inc() - } - - if gFlatten { - jsonBytes, err := json.Marshal(plpf) - if err != nil { - log.Fatal(err) - } - mtx.Lock() - err = writeOut(string(jsonBytes), gOutputFile) - mtx.Unlock() - if err != nil { - log.Fatal(err) - } - } - } - - if !gFlatten { - jsonBytes, err := json.Marshal(plp) - if err != nil { - log.Fatal(err) - } - mtx.Lock() - err = writeOut(string(jsonBytes), gOutputFile) - mtx.Unlock() - if err != nil { - log.Fatal(err) - } - } + if err.Error() != "Error: Line do not match regex" { + os.Exit(1) } } } From 8f7cd08e1a83e0b32fc8bd8eabd1abcf9a4bb142 Mon Sep 17 00:00:00 2001 From: yo Date: Thu, 16 Jun 2022 17:33:05 +0200 Subject: [PATCH 073/100] Add mQueue mutex, add postfixlogparser_client_count metric --- postfix-log-parser/cmd/root.go | 82 +++++++++++++++++++++------------- 1 file changed, 52 insertions(+), 30 deletions(-) diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index e7b492f..55933ce 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -3,24 +3,24 @@ package cmd import ( "os" "fmt" - "net" "log" + "net" "sync" "time" "bufio" "errors" - "net/http" "runtime" "strconv" "strings" "syscall" + "net/http" "os/signal" "encoding/json" "github.com/spf13/cobra" "github.com/tabalt/pidfile" - postfixlog "github.com/yo000/postfix-log-parser" "github.com/prometheus/client_golang/prometheus" + postfixlog "github.com/yo000/postfix-log-parser" "github.com/prometheus/client_golang/prometheus/promauto" "github.com/prometheus/client_golang/prometheus/promhttp" ) @@ -123,6 +123,10 @@ var ( Name: "postfixlogparser_msg_hold_count", Help: "Number of mails hold", }, []string{"host"}) + ConnectedClientCnt = promauto.NewCounter(prometheus.CounterOpts{ + Name: "postfixlogparser_client_count", + Help: "Number of connected clients", + }) rootCmd = &cobra.Command{ Use: "postfix-log-parser", @@ -212,10 +216,11 @@ func writeOut(msg string, filename string) error { } // Every 24H, remove sent, milter-rejected and deferred that entered queue more than 5 days ago -func periodicallyCleanMQueue(mqueue map[string]*PostfixLogParser) { +func periodicallyCleanMQueue(mqueue map[string]*PostfixLogParser, mqMtx sync.Mutex) { var ok int for range time.Tick(time.Hour * 24) { + // Do we need read lock? for _, inmail := range mqueue { ok = 0 // Check all mails were sent (multiple destinations mails) @@ -230,7 +235,9 @@ func periodicallyCleanMQueue(mqueue map[string]*PostfixLogParser) { } } if ok == len(inmail.Messages) { + mqMtx.Lock() delete(mqueue, inmail.MessageId) + mqMtx.Unlock() } } } @@ -252,12 +259,12 @@ func init() { cobra.OnInitialize(initConfig) } -/* - * This is the function doing the work. - * Each input is stored in a map, +/* + * This is the function doing the work. + * Each input is stored in a map, * then written to output when we recognize it as the last line */ -func parseStoreAndWrite(input []byte, mq map[string]*PostfixLogParser, mtx sync.Mutex, p *postfixlog.PostfixLog) error { +func parseStoreAndWrite(input []byte, mq map[string]*PostfixLogParser, mqMtx sync.Mutex, outfMtx sync.Mutex, p *postfixlog.PostfixLog) error { logFormat, err := p.Parse(input) if err != nil { // Incorrect line, just skip it @@ -272,6 +279,7 @@ func parseStoreAndWrite(input []byte, mq map[string]*PostfixLogParser, mtx sync. Oct 10 04:02:02 mail.example.com postfix/smtpd[22941]: DFBEFDBF00C5: client=example.net[127.0.0.1], sasl_method=PLAIN, sasl_username=user@example.com */ if logFormat.ClientHostname != "" && !strings.HasPrefix(logFormat.Messages, "milter-reject:") { + mqMtx.Lock() mq[logFormat.QueueId] = &PostfixLogParser{ Time: logFormat.Time, Hostname: logFormat.Hostname, @@ -282,6 +290,7 @@ func parseStoreAndWrite(input []byte, mq map[string]*PostfixLogParser, mtx sync. SaslMethod: logFormat.SaslMethod, SaslUsername: logFormat.SaslUsername, } + mqMtx.Unlock() } /* @@ -289,7 +298,9 @@ func parseStoreAndWrite(input []byte, mq map[string]*PostfixLogParser, mtx sync. */ if logFormat.MessageId != "" { if plp, ok := mq[logFormat.QueueId]; ok { + mqMtx.Lock() plp.MessageId = logFormat.MessageId + mqMtx.Unlock() } } @@ -298,9 +309,11 @@ func parseStoreAndWrite(input []byte, mq map[string]*PostfixLogParser, mtx sync. */ if logFormat.From != "" { if plp, ok := mq[logFormat.QueueId]; ok { + mqMtx.Lock() plp.From = logFormat.From plp.Size = logFormat.Size plp.NRcpt = logFormat.NRcpt + mqMtx.Unlock() } nrcpt, _ := strconv.ParseFloat(logFormat.NRcpt, 64) MsgInCnt.WithLabelValues(logFormat.Hostname).Add(nrcpt) @@ -311,6 +324,7 @@ func parseStoreAndWrite(input []byte, mq map[string]*PostfixLogParser, mtx sync. */ if logFormat.To != "" { if plp, ok := mq[logFormat.QueueId]; ok { + mqMtx.Lock() message := Message{ Time: logFormat.Time, To: logFormat.To, @@ -318,10 +332,11 @@ func parseStoreAndWrite(input []byte, mq map[string]*PostfixLogParser, mtx sync. Message: logFormat.Messages, BounceId: "", } + mqMtx.Unlock() /* When a message is deferred, it won't be written out until it is either sent, expired, or generates a non delivery notification. - We want to know instantly when a message is deferred, so we handle this case by emiting output for this message, and not appending this occurence - to the list of Messages + We want to know instantly when a message is deferred, so we handle this case by emiting output for this message, and not appending this occurence + to the list of Messages */ if logFormat.Status == "deferred" { MsgDeferredCnt.WithLabelValues(plp.Hostname).Inc() @@ -350,9 +365,9 @@ func parseStoreAndWrite(input []byte, mq map[string]*PostfixLogParser, mtx sync. if err != nil { log.Fatal(err) } - mtx.Lock() + outfMtx.Lock() err = writeOut(string(jsonBytes), gOutputFile) - mtx.Unlock() + outfMtx.Unlock() if err != nil { log.Fatal(err) } @@ -371,6 +386,7 @@ func parseStoreAndWrite(input []byte, mq map[string]*PostfixLogParser, mtx sync. if logFormat.BounceId != "" { if plp, ok := mq[logFormat.QueueId]; ok { // Get the matching Message by Status=bounced + mqMtx.Lock() for i, msg := range plp.Messages { // Need to manage more than one bounce for the same queue_id. This is flawy as we just rely on order to match if msg.Status == "bounced" && len(msg.BounceId) == 0 { @@ -387,6 +403,7 @@ func parseStoreAndWrite(input []byte, mq map[string]*PostfixLogParser, mtx sync. break } } + mqMtx.Unlock() } } /* @@ -414,9 +431,9 @@ func parseStoreAndWrite(input []byte, mq map[string]*PostfixLogParser, mtx sync. if err != nil { log.Fatal(err) } - mtx.Lock() + outfMtx.Lock() err = writeOut(string(jsonBytes), gOutputFile) - mtx.Unlock() + outfMtx.Unlock() if err != nil { log.Fatal(err) } @@ -428,9 +445,9 @@ func parseStoreAndWrite(input []byte, mq map[string]*PostfixLogParser, mtx sync. if err != nil { log.Fatal(err) } - mtx.Lock() + outfMtx.Lock() err = writeOut(string(jsonBytes), gOutputFile) - mtx.Unlock() + outfMtx.Unlock() if err != nil { log.Fatal(err) } @@ -443,13 +460,15 @@ func parseStoreAndWrite(input []byte, mq map[string]*PostfixLogParser, mtx sync. func processLogs(cmd *cobra.Command, args []string) { var scanner *bufio.Scanner var listener net.Listener - var mtx sync.Mutex + // Output file mutex + var outfMtx sync.Mutex + // mQueue mutex + var mqMtx sync.Mutex var useStdin bool - - // create queue + + // create map of messages mQueue := make(map[string]*PostfixLogParser) - - + BuildInfo.WithLabelValues(Version, runtime.Version()).Set(1) StartTime.Set(float64(time.Now().Unix())) @@ -479,7 +498,7 @@ func processLogs(cmd *cobra.Command, args []string) { defer pid.Clear() } } - + // initialize p := postfixlog.NewPostfixLog() @@ -498,23 +517,23 @@ func processLogs(cmd *cobra.Command, args []string) { go func() { for { <-sig - mtx.Lock() + outfMtx.Lock() fmt.Println("SIGUSR1 received, recreating output file") File.Close() _, File, err = NewWriter(gOutputFile) if err != nil { - mtx.Unlock() + outfMtx.Unlock() cmd.SetOutput(os.Stderr) cmd.Println(err) os.Exit(1) } - mtx.Unlock() + outfMtx.Unlock() } }() } // Cleaner thread - go periodicallyCleanMQueue(mQueue) + go periodicallyCleanMQueue(mQueue, mqMtx) // Initialize Stdin input if true == strings.EqualFold(gSyslogListenAddress, "do-not-listen") { @@ -542,6 +561,7 @@ func processLogs(cmd *cobra.Command, args []string) { // Read will fail after "duration" connClt.SetReadDeadline(time.Now().Add(time.Duration(600) * time.Second)) scanner = bufio.NewScanner(connClt) + ConnectedClientCnt.Inc() } } @@ -554,12 +574,14 @@ func processLogs(cmd *cobra.Command, args []string) { // Should we? Actually we can't as connClt is not known here // connClt.Close() scanner = nil + ConnectedClientCnt.Dec() } continue } if useStdin == false { // close connection so we can Accept() again, then loop scanner = nil + ConnectedClientCnt.Dec() continue } else { // stdin is dead, abort mission! @@ -572,8 +594,8 @@ func processLogs(cmd *cobra.Command, args []string) { } LineReadCnt.Inc() - - err = parseStoreAndWrite(scanner.Bytes(), mQueue, mtx, p) + + err = parseStoreAndWrite(scanner.Bytes(), mQueue, mqMtx, outfMtx, p) if err != nil { cmd.SetOutput(os.Stderr) cmd.Println(err) @@ -583,8 +605,8 @@ func processLogs(cmd *cobra.Command, args []string) { } } if File != nil { - mtx.Lock() + outfMtx.Lock() File.Close() - mtx.Unlock() + outfMtx.Unlock() } } From f5dcffe0de83aefa6413f268465c8a1142df95c0 Mon Sep 17 00:00:00 2001 From: yo Date: Thu, 16 Jun 2022 17:36:56 +0200 Subject: [PATCH 074/100] postfixlogparser_client_count is a gauge, as counter can not decrement --- postfix-log-parser/cmd/root.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index 55933ce..c0b7037 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -123,7 +123,7 @@ var ( Name: "postfixlogparser_msg_hold_count", Help: "Number of mails hold", }, []string{"host"}) - ConnectedClientCnt = promauto.NewCounter(prometheus.CounterOpts{ + ConnectedClientCnt = promauto.NewGauge(prometheus.GaugeOpts{ Name: "postfixlogparser_client_count", Help: "Number of connected clients", }) From 095e7c0dac732d6699ca8644e5f742a18bc1f1d2 Mon Sep 17 00:00:00 2001 From: yo Date: Fri, 17 Jun 2022 23:03:25 +0200 Subject: [PATCH 075/100] go routines for each TCP connection, moved locks to protect mQueue reads --- postfix-log-parser/cmd/root.go | 144 +++++++++++++++++---------------- 1 file changed, 74 insertions(+), 70 deletions(-) diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index c0b7037..8a76497 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -264,7 +264,7 @@ func init() { * Each input is stored in a map, * then written to output when we recognize it as the last line */ -func parseStoreAndWrite(input []byte, mq map[string]*PostfixLogParser, mqMtx sync.Mutex, outfMtx sync.Mutex, p *postfixlog.PostfixLog) error { +func parseStoreAndWrite(input []byte, mq map[string]*PostfixLogParser, mqMtx *sync.Mutex, outfMtx *sync.Mutex, p *postfixlog.PostfixLog) error { logFormat, err := p.Parse(input) if err != nil { // Incorrect line, just skip it @@ -297,24 +297,24 @@ func parseStoreAndWrite(input []byte, mq map[string]*PostfixLogParser, mqMtx syn Oct 10 04:02:02 mail.example.com postfix/cleanup[22923]: DFBEFDBF00C5: message-id=<20181009190202.81363306015D@example.com> */ if logFormat.MessageId != "" { + mqMtx.Lock() if plp, ok := mq[logFormat.QueueId]; ok { - mqMtx.Lock() plp.MessageId = logFormat.MessageId - mqMtx.Unlock() } + mqMtx.Unlock() } /* Oct 10 04:02:03 mail.example.com postfix/qmgr[18719]: DFBEFDBF00C5: from=, size=3578, nrcpt=1 (queue active) */ if logFormat.From != "" { + mqMtx.Lock() if plp, ok := mq[logFormat.QueueId]; ok { - mqMtx.Lock() plp.From = logFormat.From plp.Size = logFormat.Size plp.NRcpt = logFormat.NRcpt - mqMtx.Unlock() } + mqMtx.Unlock() nrcpt, _ := strconv.ParseFloat(logFormat.NRcpt, 64) MsgInCnt.WithLabelValues(logFormat.Hostname).Add(nrcpt) } @@ -323,8 +323,8 @@ func parseStoreAndWrite(input []byte, mq map[string]*PostfixLogParser, mqMtx syn Oct 10 04:02:08 mail.example.com postfix/smtp[22928]: DFBEFDBF00C5: to=, relay=mail.example-to.com[192.168.0.10]:25, delay=5.3, delays=0.26/0/0.31/4.7, dsn=2.0.0, status=sent (250 2.0.0 Ok: queued as C598F1B0002D) */ if logFormat.To != "" { + mqMtx.Lock() if plp, ok := mq[logFormat.QueueId]; ok { - mqMtx.Lock() message := Message{ Time: logFormat.Time, To: logFormat.To, @@ -332,8 +332,6 @@ func parseStoreAndWrite(input []byte, mq map[string]*PostfixLogParser, mqMtx syn Message: logFormat.Messages, BounceId: "", } - mqMtx.Unlock() - /* When a message is deferred, it won't be written out until it is either sent, expired, or generates a non delivery notification. We want to know instantly when a message is deferred, so we handle this case by emiting output for this message, and not appending this occurence to the list of Messages @@ -378,15 +376,16 @@ func parseStoreAndWrite(input []byte, mq map[string]*PostfixLogParser, mqMtx syn plp.Messages = append(plp.Messages, message) } } + mqMtx.Unlock() } /* 2021-02-05T17:25:03+01:00 mail.example.com postfix/bounce[39258]: 006B056E6: sender non-delivery notification: 642E456E9 */ if logFormat.BounceId != "" { + mqMtx.Lock() if plp, ok := mq[logFormat.QueueId]; ok { // Get the matching Message by Status=bounced - mqMtx.Lock() for i, msg := range plp.Messages { // Need to manage more than one bounce for the same queue_id. This is flawy as we just rely on order to match if msg.Status == "bounced" && len(msg.BounceId) == 0 { @@ -403,8 +402,8 @@ func parseStoreAndWrite(input []byte, mq map[string]*PostfixLogParser, mqMtx syn break } } - mqMtx.Unlock() } + mqMtx.Unlock() } /* Oct 10 04:02:08 mail.example.com postfix/qmgr[18719]: DFBEFDBF00C5: removed @@ -413,6 +412,7 @@ func parseStoreAndWrite(input []byte, mq map[string]*PostfixLogParser, mqMtx syn */ // "removed" message is end of logs. then flush. if logFormat.Messages == "removed" || strings.HasPrefix(logFormat.Status, "milter-") { + mqMtx.Lock() if plp, ok := mq[logFormat.QueueId]; ok { for _, plpf := range PlpToFlat(plp) { switch plpf.Status { @@ -453,12 +453,58 @@ func parseStoreAndWrite(input []byte, mq map[string]*PostfixLogParser, mqMtx syn } } } + mqMtx.Unlock() + } + return nil +} + +func scanAndProcess(scanner *bufio.Scanner, isStdin bool, conn net.Conn, mQueue map[string]*PostfixLogParser, + mqMtx *sync.Mutex, outfMtx *sync.Mutex, p *postfixlog.PostfixLog) error { + + ConnectedClientCnt.Inc() + for { + // If input is made via TCP Conn, we need to read from a connected net.Conn + if scanner == nil || (isStdin == false && conn == nil) { + return errors.New("Invalid input") + } + + if false == scanner.Scan() { + // After Scan returns false, the Err method will return any error that occurred during scanning, except that if it was io.EOF, Err will return nil + if err := scanner.Err(); err != nil { + log.Printf("Error reading data: %v\n", err.Error()) + } + if isStdin == false { + log.Printf("No more data, closing connection.\n") + // Should we? + conn.Close() + ConnectedClientCnt.Dec() + } + // input is dead, abort mission! + return errors.New("Read error") + } + // Extend timeout after successful read (so we got an idle timeout) + if isStdin == false && conn != nil{ + conn.SetReadDeadline(time.Now().Add(time.Duration(600) * time.Second)) + } + + LineReadCnt.Inc() + + read := scanner.Bytes() + err := parseStoreAndWrite(read, mQueue, mqMtx, outfMtx, p) + if err != nil { + if err.Error() != "Error: Line do not match regex" { + return err + } else { + log.Printf("input do not match regex: %s\n", string(read)) + } + } } return nil } + func processLogs(cmd *cobra.Command, args []string) { - var scanner *bufio.Scanner + //var scanner *bufio.Scanner var listener net.Listener // Output file mutex var outfMtx sync.Mutex @@ -534,76 +580,34 @@ func processLogs(cmd *cobra.Command, args []string) { // Cleaner thread go periodicallyCleanMQueue(mQueue, mqMtx) - - // Initialize Stdin input + + // Initialize Stdin input... if true == strings.EqualFold(gSyslogListenAddress, "do-not-listen") { + + // DEUBG + fmt.Printf("Reading stdin\n") + useStdin = true - scanner = bufio.NewScanner(os.Stdin) + scanner := bufio.NewScanner(os.Stdin) + scanAndProcess(scanner, useStdin, nil, mQueue, &mqMtx, &outfMtx, p) + // ...or manages incoming connections } else { listener, err = net.Listen("tcp", gSyslogListenAddress) if err != nil { log.Fatal(fmt.Sprintf("Error listening on %s: %v\n", gSyslogListenAddress, err)) } - } - - var connClt net.Conn - for { - // If input is made via TCP Conn, we need to read from a connected net.Conn - if useStdin == false { - if scanner == nil { - // We support _only one_ concurent connection to the service - connClt, err = listener.Accept() - if err != nil { - log.Printf("Error accepting: %v", err) - // Loop - continue - } - // Read will fail after "duration" - connClt.SetReadDeadline(time.Now().Add(time.Duration(600) * time.Second)) - scanner = bufio.NewScanner(connClt) - ConnectedClientCnt.Inc() - } - } - - if false == scanner.Scan() { - // After Scan returns false, the Err method will return any error that occurred during scanning, except that if it was io.EOF, Err will return nil - if err := scanner.Err(); err != nil { - log.Printf("Error reading data: %v\n", err.Error()) - if useStdin == false && errors.Is(err, os.ErrDeadlineExceeded) { - log.Printf("I/O timeout on socket, closing connection.\n") - // Should we? Actually we can't as connClt is not known here - // connClt.Close() - scanner = nil - ConnectedClientCnt.Dec() - } - continue - } - if useStdin == false { - // close connection so we can Accept() again, then loop - scanner = nil - ConnectedClientCnt.Dec() + for { + connClt, err := listener.Accept() + if err != nil { + log.Printf("Error accepting: %v", err) + // Loop continue - } else { - // stdin is dead, abort mission! - return - } - } - // Extend timeout after successful read (so we got an idle timeout) - if useStdin == false { - connClt.SetReadDeadline(time.Now().Add(time.Duration(600) * time.Second)) - } - - LineReadCnt.Inc() - - err = parseStoreAndWrite(scanner.Bytes(), mQueue, mqMtx, outfMtx, p) - if err != nil { - cmd.SetOutput(os.Stderr) - cmd.Println(err) - if err.Error() != "Error: Line do not match regex" { - os.Exit(1) } + scanner := bufio.NewScanner(connClt) + go scanAndProcess(scanner, useStdin, connClt, mQueue, &mqMtx, &outfMtx, p) } } + if File != nil { outfMtx.Lock() File.Close() From be194e1f2c5a3bb8eef5cac54b09a4723317637a Mon Sep 17 00:00:00 2001 From: yo Date: Fri, 17 Jun 2022 23:05:00 +0200 Subject: [PATCH 076/100] Version bump to 1.3a --- postfix-log-parser/cmd/root.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index 8a76497..7e50c38 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -77,7 +77,7 @@ var ( File os.File Writer *bufio.Writer - Version = "1.2.10" + Version = "1.3a" BuildInfo = promauto.NewGaugeVec(prometheus.GaugeOpts{ Name: "postfixlogparser_build_info", From 2f110bd4b7c0117fff0b14dde199d553245eada0 Mon Sep 17 00:00:00 2001 From: yo Date: Fri, 17 Jun 2022 23:28:05 +0200 Subject: [PATCH 077/100] Go fmt --- postfix-log-parser/cmd/root.go | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index 7e50c38..fa17677 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -77,7 +77,7 @@ var ( File os.File Writer *bufio.Writer - Version = "1.3a" + Version = "1.2.10" BuildInfo = promauto.NewGaugeVec(prometheus.GaugeOpts{ Name: "postfixlogparser_build_info", @@ -264,7 +264,8 @@ func init() { * Each input is stored in a map, * then written to output when we recognize it as the last line */ -func parseStoreAndWrite(input []byte, mq map[string]*PostfixLogParser, mqMtx *sync.Mutex, outfMtx *sync.Mutex, p *postfixlog.PostfixLog) error { +func parseStoreAndWrite(input []byte, mq map[string]*PostfixLogParser, mqMtx *sync.Mutex, + outfMtx *sync.Mutex, p *postfixlog.PostfixLog) error { logFormat, err := p.Parse(input) if err != nil { // Incorrect line, just skip it @@ -459,15 +460,13 @@ func parseStoreAndWrite(input []byte, mq map[string]*PostfixLogParser, mqMtx *sy } func scanAndProcess(scanner *bufio.Scanner, isStdin bool, conn net.Conn, mQueue map[string]*PostfixLogParser, - mqMtx *sync.Mutex, outfMtx *sync.Mutex, p *postfixlog.PostfixLog) error { - - ConnectedClientCnt.Inc() + mqMtx *sync.Mutex, outfMtx *sync.Mutex, p *postfixlog.PostfixLog) error { for { // If input is made via TCP Conn, we need to read from a connected net.Conn if scanner == nil || (isStdin == false && conn == nil) { - return errors.New("Invalid input") + return errors.New("Invalid input") } - + if false == scanner.Scan() { // After Scan returns false, the Err method will return any error that occurred during scanning, except that if it was io.EOF, Err will return nil if err := scanner.Err(); err != nil { @@ -477,18 +476,17 @@ func scanAndProcess(scanner *bufio.Scanner, isStdin bool, conn net.Conn, mQueue log.Printf("No more data, closing connection.\n") // Should we? conn.Close() - ConnectedClientCnt.Dec() } // input is dead, abort mission! return errors.New("Read error") } // Extend timeout after successful read (so we got an idle timeout) - if isStdin == false && conn != nil{ + if isStdin == false && conn != nil { conn.SetReadDeadline(time.Now().Add(time.Duration(600) * time.Second)) } LineReadCnt.Inc() - + read := scanner.Bytes() err := parseStoreAndWrite(read, mQueue, mqMtx, outfMtx, p) if err != nil { @@ -502,7 +500,6 @@ func scanAndProcess(scanner *bufio.Scanner, isStdin bool, conn net.Conn, mQueue return nil } - func processLogs(cmd *cobra.Command, args []string) { //var scanner *bufio.Scanner var listener net.Listener @@ -580,13 +577,9 @@ func processLogs(cmd *cobra.Command, args []string) { // Cleaner thread go periodicallyCleanMQueue(mQueue, mqMtx) - + // Initialize Stdin input... if true == strings.EqualFold(gSyslogListenAddress, "do-not-listen") { - - // DEUBG - fmt.Printf("Reading stdin\n") - useStdin = true scanner := bufio.NewScanner(os.Stdin) scanAndProcess(scanner, useStdin, nil, mQueue, &mqMtx, &outfMtx, p) @@ -604,10 +597,12 @@ func processLogs(cmd *cobra.Command, args []string) { continue } scanner := bufio.NewScanner(connClt) + ConnectedClientCnt.Inc() go scanAndProcess(scanner, useStdin, connClt, mQueue, &mqMtx, &outfMtx, p) + ConnectedClientCnt.Dec() } } - + if File != nil { outfMtx.Lock() File.Close() From 39a99cd5206cbf2485624ba7d638a3442aacc922 Mon Sep 17 00:00:00 2001 From: yo Date: Fri, 17 Jun 2022 23:30:23 +0200 Subject: [PATCH 078/100] Version bump to 1.3a --- postfix-log-parser/cmd/root.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index fa17677..c2c7ad5 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -77,7 +77,7 @@ var ( File os.File Writer *bufio.Writer - Version = "1.2.10" + Version = "1.3a" BuildInfo = promauto.NewGaugeVec(prometheus.GaugeOpts{ Name: "postfixlogparser_build_info", From 9a7cf35f7606ff57b357e2420319c8561f85bf06 Mon Sep 17 00:00:00 2001 From: yo Date: Thu, 30 Jun 2022 11:37:44 +0200 Subject: [PATCH 079/100] Handle authentication failures + version bump --- postfix-log-parser.go | 9 ++++- postfix-log-parser/cmd/root.go | 74 +++++++++++++++++++++++++++------- 2 files changed, 67 insertions(+), 16 deletions(-) diff --git a/postfix-log-parser.go b/postfix-log-parser.go index 51d5e2c..66ccee3 100644 --- a/postfix-log-parser.go +++ b/postfix-log-parser.go @@ -20,7 +20,8 @@ const ( ToRegexpFormat = `(?:to=<(.+@.+)>.*status=([a-z]+))?` SenderNDNRegexpFormat = `(?:sender non-delivery notification: ([0-9A-Z]*))?` MilterRegexpFormat = `(?:(milter-.*): .* from (.+)\[(.+)\]: .*from=<(.+@.+)?> to=<(.+@.+)> .*)?` - MessageDetailsRegexpFormat = `(` + ClientRegexpFormat + MessageIdRegexpFormat + FromRegexpFormat + ToRegexpFormat + SenderNDNRegexpFormat + MilterRegexpFormat + `.*)` + AuthentFailedRegexpFormat = `(?:warning: (.+)\[(.+)\]: SASL (.*) authentication failed: (.*))?` + MessageDetailsRegexpFormat = `(` + ClientRegexpFormat + MessageIdRegexpFormat + FromRegexpFormat + ToRegexpFormat + SenderNDNRegexpFormat + MilterRegexpFormat + AuthentFailedRegexpFormat + `.*)` RegexpFormat = SyslogPri + TimeRegexpFormat + ` ` + HostRegexpFormat + ` ` + ProcessRegexpFormat + `:? ` + QueueIdRegexpFormat + `(?:\: )?` + MessageDetailsRegexpFormat ) @@ -93,6 +94,12 @@ func (p *PostfixLog) Parse(text []byte) (LogFormat, error) { logFormat.ClinetIp = string(group[19]) logFormat.From = string(group[20]) logFormat.To = string(group[21]) + // Authentication failure + } else if len(group[22]) > 0 { + logFormat.ClientHostname = string(group[22]) + logFormat.ClinetIp = string(group[23]) + logFormat.SaslMethod = string(group[24]) + logFormat.Status = "auth-failed" } else { logFormat.Status = string(group[15]) logFormat.ClientHostname = string(group[6]) diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index c2c7ad5..22f9fe5 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -1,28 +1,28 @@ package cmd import ( - "os" + "bufio" + "encoding/json" + "errors" "fmt" "log" "net" - "sync" - "time" - "bufio" - "errors" + "net/http" + "os" + "os/signal" "runtime" "strconv" "strings" + "sync" "syscall" - "net/http" - "os/signal" - "encoding/json" + "time" - "github.com/spf13/cobra" - "github.com/tabalt/pidfile" "github.com/prometheus/client_golang/prometheus" - postfixlog "github.com/yo000/postfix-log-parser" "github.com/prometheus/client_golang/prometheus/promauto" "github.com/prometheus/client_golang/prometheus/promhttp" + "github.com/spf13/cobra" + "github.com/tabalt/pidfile" + postfixlog "github.com/yo000/postfix-log-parser" ) func init() {} @@ -77,7 +77,7 @@ var ( File os.File Writer *bufio.Writer - Version = "1.3a" + Version = "1.4" BuildInfo = promauto.NewGaugeVec(prometheus.GaugeOpts{ Name: "postfixlogparser_build_info", @@ -123,6 +123,10 @@ var ( Name: "postfixlogparser_msg_hold_count", Help: "Number of mails hold", }, []string{"host"}) + MsgAuthFailed = promauto.NewCounterVec(prometheus.CounterOpts{ + Name: "postfixlogparser_auth_failed_count", + Help: "Number of failed authentications", + }, []string{"host"}) ConnectedClientCnt = promauto.NewGauge(prometheus.GaugeOpts{ Name: "postfixlogparser_client_count", Help: "Number of connected clients", @@ -264,7 +268,7 @@ func init() { * Each input is stored in a map, * then written to output when we recognize it as the last line */ -func parseStoreAndWrite(input []byte, mq map[string]*PostfixLogParser, mqMtx *sync.Mutex, +func parseStoreAndWrite(input []byte, mq map[string]*PostfixLogParser, mqMtx *sync.Mutex, outfMtx *sync.Mutex, p *postfixlog.PostfixLog) error { logFormat, err := p.Parse(input) if err != nil { @@ -456,10 +460,50 @@ func parseStoreAndWrite(input []byte, mq map[string]*PostfixLogParser, mqMtx *sy } mqMtx.Unlock() } + + /* + 2022-06-29T10:55:18.498553+02:00 srv-smtp-01.domain.com postfix/smtpd[75994] warning: unknown[10.11.12.13]: SASL LOGIN authentication failed: authentication failure + */ + // An auth failed message is not queued, we just write and forget + if strings.EqualFold(logFormat.Status, "auth-failed") { + MsgAuthFailed.WithLabelValues(logFormat.Hostname).Inc() + message := Message{ + Time: logFormat.Time, + Status: logFormat.Status, + Message: logFormat.Messages, + } + + tmpplp := PostfixLogParser{ + Time: logFormat.Time, + Hostname: logFormat.Hostname, + Process: logFormat.Process, + ClientHostname: logFormat.ClientHostname, + ClinetIp: logFormat.ClinetIp, + SaslMethod: logFormat.SaslMethod, + } + tmpplp.Messages = append(tmpplp.Messages, message) + + var jsonBytes []byte + if gFlatten { + jsonBytes, err = json.Marshal(PlpToFlat(&tmpplp)[0]) + } else { + jsonBytes, err = json.Marshal(tmpplp) + } + if err != nil { + log.Fatal(err) + } + outfMtx.Lock() + err = writeOut(string(jsonBytes), gOutputFile) + outfMtx.Unlock() + if err != nil { + log.Fatal(err) + } + } + return nil } -func scanAndProcess(scanner *bufio.Scanner, isStdin bool, conn net.Conn, mQueue map[string]*PostfixLogParser, +func scanAndProcess(scanner *bufio.Scanner, isStdin bool, conn net.Conn, mQueue map[string]*PostfixLogParser, mqMtx *sync.Mutex, outfMtx *sync.Mutex, p *postfixlog.PostfixLog) error { for { // If input is made via TCP Conn, we need to read from a connected net.Conn @@ -583,7 +627,7 @@ func processLogs(cmd *cobra.Command, args []string) { useStdin = true scanner := bufio.NewScanner(os.Stdin) scanAndProcess(scanner, useStdin, nil, mQueue, &mqMtx, &outfMtx, p) - // ...or manages incoming connections + // ...or manages incoming connections } else { listener, err = net.Listen("tcp", gSyslogListenAddress) if err != nil { From f26c9c490d70dba04093c01fb470978d5cf4dafb Mon Sep 17 00:00:00 2001 From: yo Date: Sat, 23 Jul 2022 12:59:31 +0200 Subject: [PATCH 080/100] bugfix: mutex lock on periodicallyCleanMQueue, version bump to 1.4 --- postfix-log-parser/cmd/root.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index c2c7ad5..a59a6d6 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -77,7 +77,7 @@ var ( File os.File Writer *bufio.Writer - Version = "1.3a" + Version = "1.4" BuildInfo = promauto.NewGaugeVec(prometheus.GaugeOpts{ Name: "postfixlogparser_build_info", @@ -216,7 +216,7 @@ func writeOut(msg string, filename string) error { } // Every 24H, remove sent, milter-rejected and deferred that entered queue more than 5 days ago -func periodicallyCleanMQueue(mqueue map[string]*PostfixLogParser, mqMtx sync.Mutex) { +func periodicallyCleanMQueue(mqueue map[string]*PostfixLogParser, mqMtx *sync.Mutex) { var ok int for range time.Tick(time.Hour * 24) { @@ -576,7 +576,7 @@ func processLogs(cmd *cobra.Command, args []string) { } // Cleaner thread - go periodicallyCleanMQueue(mQueue, mqMtx) + go periodicallyCleanMQueue(mQueue, &mqMtx) // Initialize Stdin input... if true == strings.EqualFold(gSyslogListenAddress, "do-not-listen") { From 76207175910d73d52c0aa05cfdd49deb4947729e Mon Sep 17 00:00:00 2001 From: yo Date: Sat, 23 Jul 2022 13:01:38 +0200 Subject: [PATCH 081/100] Version bump to 1.4.1 b/c dumbitude --- postfix-log-parser/cmd/root.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index ba66041..49d17cf 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -77,7 +77,7 @@ var ( File os.File Writer *bufio.Writer - Version = "1.4" + Version = "1.4.1" BuildInfo = promauto.NewGaugeVec(prometheus.GaugeOpts{ Name: "postfixlogparser_build_info", From 60cb15360ef4326f67f77fc652f67f26090d488a Mon Sep 17 00:00:00 2001 From: yo Date: Sat, 23 Jul 2022 13:11:07 +0200 Subject: [PATCH 082/100] Repo change --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 6db4c02..c0f3ee9 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ .DEFAULT_GOAL := help -Owner := youyo +Owner := yo000 Name := postfix-log-parser Repository := "github.com/$(Owner)/$(Name)" GithubToken := ${GITHUB_TOKEN} From 95658f99742bfe23a17d4ac6a12607eee06e9466 Mon Sep 17 00:00:00 2001 From: yo Date: Sat, 23 Jul 2022 13:20:09 +0200 Subject: [PATCH 083/100] mport log --- postfix-log-parser/cmd/root.go | 1 + 1 file changed, 1 insertion(+) diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index 4ff7032..361e73e 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -3,6 +3,7 @@ package cmd import ( "os" "fmt" + "log" "net" "sync" "time" From 46eee9e75f1ee2b22308a13f1323b66e12511459 Mon Sep 17 00:00:00 2001 From: yo Date: Sat, 23 Jul 2022 13:20:54 +0200 Subject: [PATCH 084/100] version bump to 1.4.2 b/c dumbitude is intense --- postfix-log-parser/cmd/root.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index 361e73e..47a98e5 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -77,7 +77,7 @@ var ( File os.File Writer *bufio.Writer - Version = "1.4.1" + Version = "1.4.2" BuildInfo = promauto.NewGaugeVec(prometheus.GaugeOpts{ Name: "postfixlogparser_build_info", From 10c5389e3be865ded8e0d71d1d5b5a0bc2f94ec6 Mon Sep 17 00:00:00 2001 From: yo Date: Mon, 17 Oct 2022 17:10:59 +0200 Subject: [PATCH 085/100] BUGFIX: cleanMQueue was not removing messages --- postfix-log-parser/cmd/root.go | 64 +++++++++++++++++++++++----------- 1 file changed, 44 insertions(+), 20 deletions(-) diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index 47a98e5..b3a591b 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -77,7 +77,7 @@ var ( File os.File Writer *bufio.Writer - Version = "1.4.2" + Version = "1.4.3" BuildInfo = promauto.NewGaugeVec(prometheus.GaugeOpts{ Name: "postfixlogparser_build_info", @@ -219,34 +219,46 @@ func writeOut(msg string, filename string) error { return nil } -// Every 24H, remove sent, milter-rejected and deferred that entered queue more than 5 days ago -func periodicallyCleanMQueue(mqueue map[string]*PostfixLogParser, mqMtx *sync.Mutex) { + +// Remove sent, milter-rejected and deferred that entered queue more than "duration" ago +func cleanMQueue(mqueue map[string]*PostfixLogParser, mqMtx *sync.Mutex, age time.Duration) { var ok int - for range time.Tick(time.Hour * 24) { - // Do we need read lock? - for _, inmail := range mqueue { - ok = 0 - // Check all mails were sent (multiple destinations mails) - // or rejected - for _, outmail := range inmail.Messages { - if outmail.Status == "sent" || outmail.Status == "milter-reject" { + log.Printf("Start cleaning queue task: %d items in queue", len(mqueue)) + + // Do we need read lock? + for qid, inmail := range mqueue { + ok = 0 + // Check all mails were sent (multiple destinations mails) + for _, outmail := range inmail.Messages { + // Sent and Rejected mails won't have any other event, we can rm + if outmail.Status == "sent" || outmail.Status == "milter-reject" { + ok += 1 + } else if outmail.Status == "deferred" { + if inmail.Time.Add(age).Before(time.Now()) { ok += 1 - } else if outmail.Status == "deferred" { - if inmail.Time.Add(time.Hour * 5 * 24).Before(time.Now()) { - ok += 1 - } } } - if ok == len(inmail.Messages) { - mqMtx.Lock() - delete(mqueue, inmail.MessageId) - mqMtx.Unlock() - } } + + if ok == len(inmail.Messages) { + mqMtx.Lock() + delete(mqueue, qid) + mqMtx.Unlock() + } + } + log.Printf("Finished cleaning queue task: %d items in queue", len(mqueue)) +} + + +// Every 24H, remove sent, milter-rejected and deferred that entered queue more than 5 days ago +func periodicallyCleanMQueue(mqueue map[string]*PostfixLogParser, mqMtx *sync.Mutex) { + for range time.Tick(time.Hour * 24) { + cleanMQueue(mqueue, mqMtx, 5 * 24 * time.Hour) } } + func initConfig() {} func init() { @@ -622,6 +634,18 @@ func processLogs(cmd *cobra.Command, args []string) { // Cleaner thread go periodicallyCleanMQueue(mQueue, &mqMtx) + + // On demand Mqueue cleaning... For debug, dont try this at home, kids! +/* sig2 := make(chan os.Signal) + signal.Notify(sig2, syscall.SIGUSR2) + go func() { + for { + <-sig2 + cleanMQueue(mQueue, &mqMtx, 1 * time.Hour) + } + }() +*/ + // Initialize Stdin input... if true == strings.EqualFold(gSyslogListenAddress, "do-not-listen") { useStdin = true From c2d76f51eeba9704192cf4f67843820c3e577249 Mon Sep 17 00:00:00 2001 From: yo Date: Fri, 18 Nov 2022 13:45:41 +0100 Subject: [PATCH 086/100] RW Lock when cleanMQueue/iterating mQueue --- postfix-log-parser/cmd/root.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index b3a591b..ff9f161 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -226,7 +226,8 @@ func cleanMQueue(mqueue map[string]*PostfixLogParser, mqMtx *sync.Mutex, age tim log.Printf("Start cleaning queue task: %d items in queue", len(mqueue)) - // Do we need read lock? + // We need read lock: fatal error: concurrent map iteration and map write + mqMtx.Lock() for qid, inmail := range mqueue { ok = 0 // Check all mails were sent (multiple destinations mails) @@ -242,11 +243,11 @@ func cleanMQueue(mqueue map[string]*PostfixLogParser, mqMtx *sync.Mutex, age tim } if ok == len(inmail.Messages) { - mqMtx.Lock() + // We already in rw lock delete(mqueue, qid) - mqMtx.Unlock() } } + mqMtx.Unlock() log.Printf("Finished cleaning queue task: %d items in queue", len(mqueue)) } From 64764600074259653008199c925fd2effbd61fdd Mon Sep 17 00:00:00 2001 From: yo Date: Fri, 18 Nov 2022 13:47:20 +0100 Subject: [PATCH 087/100] Change version number --- postfix-log-parser/cmd/root.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index ff9f161..ca51698 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -77,7 +77,7 @@ var ( File os.File Writer *bufio.Writer - Version = "1.4.3" + Version = "1.4.4" BuildInfo = promauto.NewGaugeVec(prometheus.GaugeOpts{ Name: "postfixlogparser_build_info", From b67affb2f44adeefb13bcf00d77a9343334003b8 Mon Sep 17 00:00:00 2001 From: yo Date: Tue, 23 Jan 2024 19:10:57 +0100 Subject: [PATCH 088/100] Handle "reject:" logs --- Makefile | 2 +- postfix-log-parser.go | 12 ++++++++++-- postfix-log-parser/cmd/root.go | 10 ++++++---- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index c0f3ee9..f539fba 100644 --- a/Makefile +++ b/Makefile @@ -28,7 +28,7 @@ vendoring: ## Build build: go get - goxz -os=darwin,linux -arch=amd64 -d=pkg ./$(Name) + goxz -os=freebsd,darwin,linux -arch=amd64 -d=pkg ./$(Name) ## Release release: diff --git a/postfix-log-parser.go b/postfix-log-parser.go index 66ccee3..d17a60c 100644 --- a/postfix-log-parser.go +++ b/postfix-log-parser.go @@ -21,7 +21,8 @@ const ( SenderNDNRegexpFormat = `(?:sender non-delivery notification: ([0-9A-Z]*))?` MilterRegexpFormat = `(?:(milter-.*): .* from (.+)\[(.+)\]: .*from=<(.+@.+)?> to=<(.+@.+)> .*)?` AuthentFailedRegexpFormat = `(?:warning: (.+)\[(.+)\]: SASL (.*) authentication failed: (.*))?` - MessageDetailsRegexpFormat = `(` + ClientRegexpFormat + MessageIdRegexpFormat + FromRegexpFormat + ToRegexpFormat + SenderNDNRegexpFormat + MilterRegexpFormat + AuthentFailedRegexpFormat + `.*)` + PostfixRejectRegexpFormat = `(?:reject: RCPT from (.+)\[(.+)\]: [0-9]{3} [0-9\.]{5} [^;]*; from=<(.+@.+)?> to=<(.+@[^>]+)>.*$)?` + MessageDetailsRegexpFormat = `(` + ClientRegexpFormat + MessageIdRegexpFormat + FromRegexpFormat + ToRegexpFormat + SenderNDNRegexpFormat + MilterRegexpFormat + AuthentFailedRegexpFormat + PostfixRejectRegexpFormat + `.*)` RegexpFormat = SyslogPri + TimeRegexpFormat + ` ` + HostRegexpFormat + ` ` + ProcessRegexpFormat + `:? ` + QueueIdRegexpFormat + `(?:\: )?` + MessageDetailsRegexpFormat ) @@ -94,12 +95,19 @@ func (p *PostfixLog) Parse(text []byte) (LogFormat, error) { logFormat.ClinetIp = string(group[19]) logFormat.From = string(group[20]) logFormat.To = string(group[21]) - // Authentication failure + // Authentication failure } else if len(group[22]) > 0 { logFormat.ClientHostname = string(group[22]) logFormat.ClinetIp = string(group[23]) logFormat.SaslMethod = string(group[24]) logFormat.Status = "auth-failed" + // postfix-reject + } else if len(group[26]) > 0 { + logFormat.ClientHostname = string(group[26]) + logFormat.ClinetIp = string(group[27]) + logFormat.From = string(group[28]) + logFormat.To = string(group[29]) + logFormat.Status = "postfix-reject" } else { logFormat.Status = string(group[15]) logFormat.ClientHostname = string(group[6]) diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index ca51698..58fdf72 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -77,7 +77,7 @@ var ( File os.File Writer *bufio.Writer - Version = "1.4.4" + Version = "1.4.5" BuildInfo = promauto.NewGaugeVec(prometheus.GaugeOpts{ Name: "postfixlogparser_build_info", @@ -233,7 +233,7 @@ func cleanMQueue(mqueue map[string]*PostfixLogParser, mqMtx *sync.Mutex, age tim // Check all mails were sent (multiple destinations mails) for _, outmail := range inmail.Messages { // Sent and Rejected mails won't have any other event, we can rm - if outmail.Status == "sent" || outmail.Status == "milter-reject" { + if outmail.Status == "sent" || outmail.Status == "milter-reject" || outmail.Status == "postfix-reject" { ok += 1 } else if outmail.Status == "deferred" { if inmail.Time.Add(age).Before(time.Now()) { @@ -427,16 +427,18 @@ func parseStoreAndWrite(input []byte, mq map[string]*PostfixLogParser, mqMtx *sy Oct 10 04:02:08 mail.example.com postfix/qmgr[18719]: DFBEFDBF00C5: removed or 2021-02-05T14:17:51+01:00 smtp.server.com postfix/cleanup[38982]: D8C136A3A: milter-reject: END-OF-MESSAGE from unknown[1.2.3.4]: 4.7.1 Greylisting in action, try again later; from= to= proto=ESMTP helo= + or + 2023-12-22T09:11:42.627010+01:00 smtp-02.example.org postfix/smtpd[2717] 6CB5F45B78: reject: RCPT from unknown[11.12.13.14]: 450 4.1.2 : Recipient address rejected: Domain not found; from= to= proto=ESMTP helo= */ // "removed" message is end of logs. then flush. - if logFormat.Messages == "removed" || strings.HasPrefix(logFormat.Status, "milter-") { + if logFormat.Messages == "removed" || strings.HasPrefix(logFormat.Status, "milter-") || strings.EqualFold(logFormat.Status, "postfix-reject") { mqMtx.Lock() if plp, ok := mq[logFormat.QueueId]; ok { for _, plpf := range PlpToFlat(plp) { switch plpf.Status { case "sent": MsgSentCnt.WithLabelValues(plpf.Hostname).Inc() - case "milter-reject": + case "milter-reject", "postfix-reject": MsgRejectedCnt.WithLabelValues(plpf.Hostname).Inc() case "milter-hold": MsgHoldCnt.WithLabelValues(plpf.Hostname).Inc() From 50ec39c04569e3153653b52088b4744530223d87 Mon Sep 17 00:00:00 2001 From: Emmanuel Thierry Date: Sun, 1 Dec 2024 23:47:48 +0100 Subject: [PATCH 089/100] Fix parsing of PIDs On Linux 64bit systems, PIDs can go up to 2^22 (7 digits). Signed-off-by: Emmanuel Thierry --- postfix-log-parser.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/postfix-log-parser.go b/postfix-log-parser.go index d17a60c..877756f 100644 --- a/postfix-log-parser.go +++ b/postfix-log-parser.go @@ -12,7 +12,7 @@ const ( TimeFormatISO8601 = "2006-01-02T15:04:05.999999-07:00" TimeRegexpFormat = `([A-Za-z]{3}\s*[0-9]{1,2} [0-9]{2}:[0-9]{2}:[0-9]{2}|^\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d(?:\.\d+)?(?:[+-][0-2]\d:[0-5]\d|Z))` HostRegexpFormat = `([0-9A-Za-z\-\.]*)` - ProcessRegexpFormat = `(postfix.*(?:\/[a-z]*)+\[[0-9]{1,5}\])?` + ProcessRegexpFormat = `(postfix.*(?:\/[a-z]*)+\[[0-9]{1,7}\])?` QueueIdRegexpFormat = `([0-9A-Z]*)` ClientRegexpFormat = `(?:client=(.+)\[(.+)\](?:, sasl_method=(.+), sasl_username=(.+))?)?` MessageIdRegexpFormat = `(?:message-id=<(.+)>)?` From ed9dbdfb8bb4dd520ccf080c0650dd38e3a71d4d Mon Sep 17 00:00:00 2001 From: yo Date: Sun, 23 Feb 2025 11:24:56 +0100 Subject: [PATCH 090/100] Add postfix rate-limit message parsing --- postfix-log-parser.go | 10 ++++++- postfix-log-parser/cmd/root.go | 53 ++++++++++++++++++++++++++++++++-- 2 files changed, 59 insertions(+), 4 deletions(-) diff --git a/postfix-log-parser.go b/postfix-log-parser.go index 877756f..41f1b94 100644 --- a/postfix-log-parser.go +++ b/postfix-log-parser.go @@ -14,6 +14,7 @@ const ( HostRegexpFormat = `([0-9A-Za-z\-\.]*)` ProcessRegexpFormat = `(postfix.*(?:\/[a-z]*)+\[[0-9]{1,7}\])?` QueueIdRegexpFormat = `([0-9A-Z]*)` + // These are the main messages ClientRegexpFormat = `(?:client=(.+)\[(.+)\](?:, sasl_method=(.+), sasl_username=(.+))?)?` MessageIdRegexpFormat = `(?:message-id=<(.+)>)?` FromRegexpFormat = `(?:from=<(.+@.+)>(?:, size=(\d+), nrcpt=(\d+))?)?` @@ -22,7 +23,8 @@ const ( MilterRegexpFormat = `(?:(milter-.*): .* from (.+)\[(.+)\]: .*from=<(.+@.+)?> to=<(.+@.+)> .*)?` AuthentFailedRegexpFormat = `(?:warning: (.+)\[(.+)\]: SASL (.*) authentication failed: (.*))?` PostfixRejectRegexpFormat = `(?:reject: RCPT from (.+)\[(.+)\]: [0-9]{3} [0-9\.]{5} [^;]*; from=<(.+@.+)?> to=<(.+@[^>]+)>.*$)?` - MessageDetailsRegexpFormat = `(` + ClientRegexpFormat + MessageIdRegexpFormat + FromRegexpFormat + ToRegexpFormat + SenderNDNRegexpFormat + MilterRegexpFormat + AuthentFailedRegexpFormat + PostfixRejectRegexpFormat + `.*)` + PostfixRLimitExceedFormat = `(?:warning: Message delivery request rate limit exceeded: ([0-9]+) from (.+)\[(.+)\] for service [a-zA-Z]+)?` + MessageDetailsRegexpFormat = `(` + ClientRegexpFormat + MessageIdRegexpFormat + FromRegexpFormat + ToRegexpFormat + SenderNDNRegexpFormat + MilterRegexpFormat + AuthentFailedRegexpFormat + PostfixRejectRegexpFormat + PostfixRLimitExceedFormat + `.*)` RegexpFormat = SyslogPri + TimeRegexpFormat + ` ` + HostRegexpFormat + ` ` + ProcessRegexpFormat + `:? ` + QueueIdRegexpFormat + `(?:\: )?` + MessageDetailsRegexpFormat ) @@ -108,6 +110,12 @@ func (p *PostfixLog) Parse(text []byte) (LogFormat, error) { logFormat.From = string(group[28]) logFormat.To = string(group[29]) logFormat.Status = "postfix-reject" + // postfix rate-limit + } else if len(group[30]) > 0 { + logFormat.ClientHostname = string(group[31]) + logFormat.ClinetIp = string(group[32]) + logFormat.Status = "postfix-ratelimit" + // bounced by remote } else { logFormat.Status = string(group[15]) logFormat.ClientHostname = string(group[6]) diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index 58fdf72..c88d7fc 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -77,7 +77,7 @@ var ( File os.File Writer *bufio.Writer - Version = "1.4.5" + Version = "1.4.6a" BuildInfo = promauto.NewGaugeVec(prometheus.GaugeOpts{ Name: "postfixlogparser_build_info", @@ -293,6 +293,52 @@ func parseStoreAndWrite(input []byte, mq map[string]*PostfixLogParser, mqMtx *sy return err } + /************************************************* + * + * First process one line messages, as we dont + * need to store them + * + ************************************************/ + /* + * 2025-02-13T12:44:28.836533+01:00 srv-smtp postfix/smtpd[65735] warning: Message delivery request rate limit exceeded: 19 from unknown[41.42.43.44] for service smtp + */ + // A postfix-ratelimit message is not queued, we just write and forget + if strings.EqualFold(logFormat.Status, "postfix-ratelimit") { + MsgRejectedCnt.WithLabelValues(logFormat.Hostname).Inc() + message := Message{ + Time: logFormat.Time, + Status: logFormat.Status, + Message: logFormat.Messages, + } + + tmpplp := PostfixLogParser{ + Time: logFormat.Time, + Hostname: logFormat.Hostname, + Process: logFormat.Process, + ClientHostname: logFormat.ClientHostname, + ClinetIp: logFormat.ClinetIp, + } + tmpplp.Messages = append(tmpplp.Messages, message) + + var jsonBytes []byte + if gFlatten { + jsonBytes, err = json.Marshal(PlpToFlat(&tmpplp)[0]) + } else { + jsonBytes, err = json.Marshal(tmpplp) + } + if err != nil { + log.Fatal(err) + } + outfMtx.Lock() + err = writeOut(string(jsonBytes), gOutputFile) + outfMtx.Unlock() + if err != nil { + log.Fatal(err) + } + + return nil + } + /* Oct 10 04:02:02 mail.example.com postfix/smtpd[22941]: DFBEFDBF00C5: client=example.net[127.0.0.1], sasl_method=PLAIN, sasl_username=user@example.com */ @@ -423,6 +469,7 @@ func parseStoreAndWrite(input []byte, mq map[string]*PostfixLogParser, mqMtx *sy } mqMtx.Unlock() } + /* Oct 10 04:02:08 mail.example.com postfix/qmgr[18719]: DFBEFDBF00C5: removed or @@ -477,8 +524,8 @@ func parseStoreAndWrite(input []byte, mq map[string]*PostfixLogParser, mqMtx *sy } /* - 2022-06-29T10:55:18.498553+02:00 srv-smtp-01.domain.com postfix/smtpd[75994] warning: unknown[10.11.12.13]: SASL LOGIN authentication failed: authentication failure - */ + * 2022-06-29T10:55:18.498553+02:00 srv-smtp-01.domain.com postfix/smtpd[75994] warning: unknown[10.11.12.13]: SASL LOGIN authentication failed: authentication failure + */ // An auth failed message is not queued, we just write and forget if strings.EqualFold(logFormat.Status, "auth-failed") { MsgAuthFailed.WithLabelValues(logFormat.Hostname).Inc() From 32aa919e07cfc4ff0f09dffb943c57fdcf55e527 Mon Sep 17 00:00:00 2001 From: yo Date: Sun, 23 Feb 2025 11:25:32 +0100 Subject: [PATCH 091/100] Add postfix rate-limit message test --- test/test-iso8601.log | 1 + 1 file changed, 1 insertion(+) diff --git a/test/test-iso8601.log b/test/test-iso8601.log index fe43cbf..a07dabf 100644 --- a/test/test-iso8601.log +++ b/test/test-iso8601.log @@ -45,3 +45,4 @@ 2021-02-20T13:18:56+01:00 srv-smtp postfix/qmgr[28825]: 9ABCD1BCA9: from=, size=5712, nrcpt=1 (queue active) 2021-02-20T13:18:56+01:00 srv-smtp postfix/smtp[62771]: 9ABCD1BCA9: host gmail-smtp-in.l.google.com[74.125.140.27] said: 450-4.2.1 The user you are trying to contact is receiving mail at a rate that 450-4.2.1 prevents additional messages from being delivered. Please resend your 450-4.2.1 message at a later time. If the user is able to receive mail at that 450-4.2.1 time, your message will be delivered. For more information, please 450-4.2.1 visit 450 4.2.1 https://support.google.com/mail/?p=ReceivingRate f22si54329842vxg.12 - gsmtp (in reply to RCPT TO command) 2021-02-20T13:18:57+01:00 srv-smtp postfix/smtp[62771]: 9ABCD1BCA9: to=, relay=gmail-smtp-in.l.google.com[2a00:1450:400c:c02::1b]:25, delay=0.41, delays=0.19/0/0.19/0.03, dsn=4.2.1, status=deferred (host gmail-smtp-in.l.google.com[2a00:1450:400c:c02::1b] said: 450-4.2.1 The user you are trying to contact is receiving mail at a rate that 450-4.2.1 prevents additional messages from being delivered. Please resend your 450-4.2.1 message at a later time. If the user is able to receive mail at that 450-4.2.1 time, your message will be delivered. For more information, please 450-4.2.1 visit 450 4.2.1 https://support.google.com/mail/?p=ReceivingRate f22si54329842vxg.042 - gsmtp (in reply to RCPT TO command)) +2025-02-13T12:44:28.836533+01:00 srv-smtp postfix/smtpd[65735] warning: Message delivery request rate limit exceeded: 19 from unknown[41.42.43.44] for service smtp From 74e2d633547e06a450b8abe9a5afc0becda5f8cc Mon Sep 17 00:00:00 2001 From: yo Date: Sun, 23 Feb 2025 11:31:28 +0100 Subject: [PATCH 092/100] Move auth-failed at beginning of parseStoreAndWrite --- postfix-log-parser/cmd/root.go | 82 +++++++++++++++++----------------- 1 file changed, 42 insertions(+), 40 deletions(-) diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index c88d7fc..1f52372 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -77,7 +77,7 @@ var ( File os.File Writer *bufio.Writer - Version = "1.4.6a" + Version = "1.4.6" BuildInfo = promauto.NewGaugeVec(prometheus.GaugeOpts{ Name: "postfixlogparser_build_info", @@ -339,6 +339,47 @@ func parseStoreAndWrite(input []byte, mq map[string]*PostfixLogParser, mqMtx *sy return nil } + /* + * 2022-06-29T10:55:18.498553+02:00 srv-smtp-01.domain.com postfix/smtpd[75994] warning: unknown[10.11.12.13]: SASL LOGIN authentication failed: authentication failure + */ + // An auth failed message is not queued, we just write and forget + if strings.EqualFold(logFormat.Status, "auth-failed") { + MsgAuthFailed.WithLabelValues(logFormat.Hostname).Inc() + message := Message{ + Time: logFormat.Time, + Status: logFormat.Status, + Message: logFormat.Messages, + } + + tmpplp := PostfixLogParser{ + Time: logFormat.Time, + Hostname: logFormat.Hostname, + Process: logFormat.Process, + ClientHostname: logFormat.ClientHostname, + ClinetIp: logFormat.ClinetIp, + SaslMethod: logFormat.SaslMethod, + } + tmpplp.Messages = append(tmpplp.Messages, message) + + var jsonBytes []byte + if gFlatten { + jsonBytes, err = json.Marshal(PlpToFlat(&tmpplp)[0]) + } else { + jsonBytes, err = json.Marshal(tmpplp) + } + if err != nil { + log.Fatal(err) + } + outfMtx.Lock() + err = writeOut(string(jsonBytes), gOutputFile) + outfMtx.Unlock() + if err != nil { + log.Fatal(err) + } + + return nil + } + /* Oct 10 04:02:02 mail.example.com postfix/smtpd[22941]: DFBEFDBF00C5: client=example.net[127.0.0.1], sasl_method=PLAIN, sasl_username=user@example.com */ @@ -523,45 +564,6 @@ func parseStoreAndWrite(input []byte, mq map[string]*PostfixLogParser, mqMtx *sy mqMtx.Unlock() } - /* - * 2022-06-29T10:55:18.498553+02:00 srv-smtp-01.domain.com postfix/smtpd[75994] warning: unknown[10.11.12.13]: SASL LOGIN authentication failed: authentication failure - */ - // An auth failed message is not queued, we just write and forget - if strings.EqualFold(logFormat.Status, "auth-failed") { - MsgAuthFailed.WithLabelValues(logFormat.Hostname).Inc() - message := Message{ - Time: logFormat.Time, - Status: logFormat.Status, - Message: logFormat.Messages, - } - - tmpplp := PostfixLogParser{ - Time: logFormat.Time, - Hostname: logFormat.Hostname, - Process: logFormat.Process, - ClientHostname: logFormat.ClientHostname, - ClinetIp: logFormat.ClinetIp, - SaslMethod: logFormat.SaslMethod, - } - tmpplp.Messages = append(tmpplp.Messages, message) - - var jsonBytes []byte - if gFlatten { - jsonBytes, err = json.Marshal(PlpToFlat(&tmpplp)[0]) - } else { - jsonBytes, err = json.Marshal(tmpplp) - } - if err != nil { - log.Fatal(err) - } - outfMtx.Lock() - err = writeOut(string(jsonBytes), gOutputFile) - outfMtx.Unlock() - if err != nil { - log.Fatal(err) - } - } - return nil } From 6227336bfb710f9b5f98c28e29d1154d4f0a5c23 Mon Sep 17 00:00:00 2001 From: yo Date: Sun, 23 Feb 2025 11:32:08 +0100 Subject: [PATCH 093/100] Add test wanted outputs for reference --- test/test-iso8601.log.146.out | 9 +++++++++ test/test-milter.146.out | 2 ++ test/test.146.out | 3 +++ 3 files changed, 14 insertions(+) create mode 100644 test/test-iso8601.log.146.out create mode 100644 test/test-milter.146.out create mode 100644 test/test.146.out diff --git a/test/test-iso8601.log.146.out b/test/test-iso8601.log.146.out new file mode 100644 index 0000000..755aa3c --- /dev/null +++ b/test/test-iso8601.log.146.out @@ -0,0 +1,9 @@ +{"time":"2020-01-30T06:26:06.404714+09:00","hostname":"mail","process":"postfix/smtpd[1827]","queue_id":"3D74ADB7400B","client_hostname":"example.com","client_ip":"127.0.0.1","sasl_method":"","sasl_username":"","message_id":"f93388828093534f92d85ffe21b2a719@example.info","from":"test2@example.info","size":"2140","nrcpt":"1","messages":[{"time":"2020-01-30T06:26:07.404714+09:00","to":"test@example.to","status":"sent","message":"to=\u003ctest@example.to\u003e, relay=example.to[192.168.0.20]:25, delay=1.7, delays=0.02/0/1.7/0.06, dsn=2.0.0, status=sent (250 [Sniper] OK 1539154772 snipe-queue 10549)","bounce_id":""},{"time":"2020-01-30T06:26:07.404714+09:00","to":"test2@example.to","status":"sent","message":"to=\u003ctest2@example.to\u003e, relay=example.to[192.168.0.20]:25, delay=1.7, delays=0.02/0/1.7/0.06, dsn=2.0.0, status=sent (250 [Sniper] OK 1539154772 snipe-queue 10549)","bounce_id":""}]} +{"time":"2020-01-30T06:26:08.404714+09:00","hostname":"mail","process":"postfix/smtpd[1830]","queue_id":"6526CDB7400B","client_hostname":"example.com","client_ip":"127.0.0.1","sasl_method":"","sasl_username":"","message_id":"153915476795520900002087@example.aaa","from":"test@example.bbb","size":"4118","nrcpt":"1","messages":[{"time":"2020-01-30T06:26:08.404714+09:00","to":"test@example.ccc","status":"sent","message":"to=\u003ctest@example.ccc\u003e, relay=example.ccc[192.168.0.10]:25, delay=0.1, delays=0.02/0/0.03/0.05, dsn=2.0.0, status=sent (250 2.0.0 Ok: queued as 71111424269E)","bounce_id":""}]} +{"time":"2020-01-30T06:26:05.404714+09:00","hostname":"mail","process":"postfix/smtpd[1830]","queue_id":"C6E0DDB74006","client_hostname":"example.com","client_ip":"127.0.0.1","sasl_method":"","sasl_username":"","message_id":"A40CF64D-7F2D-42E4-8A76-CBFFF64A6EB1@example.com","from":"test@example.com","size":"309891","nrcpt":"1","messages":[{"time":"2020-01-30T06:26:09.404714+09:00","to":"test@example.ddd","status":"sent","message":"to=\u003ctest@example.ddd\u003e, relay=example.ddd[192.168.0.30]:25, delay=3.4, delays=0.11/0/0.38/2.9, dsn=2.0.0, status=sent (250 2.0.0 OK 1539154772 az9-v6si5976496plb.190 - gsmtp)","bounce_id":""}]} +{"time":"2021-02-06T10:57:57+01:00","hostname":"mail.server.com","process":"postfix/smtpd[59633]","queue_id":"D6DDA23B0F","client_hostname":"unknown","client_ip":"99.88.77.66","sasl_method":"","sasl_username":"","message_id":"20210206095814.2B34E19A19F6@host.sender.domain","from":"user2@example.com","size":"","nrcpt":"","messages":[{"time":"2021-02-06T10:57:58+01:00","to":"some.dest@destdomain.com","status":"milter-reject","message":"milter-reject: END-OF-MESSAGE from unknown[99.88.77.66]: 4.7.1 Greylisting in action, try again later; from=\u003cuser2@example.com\u003e to=\u003csome.dest@destdomain.com\u003e proto=ESMTP helo=\u003chost.sender.domain\u003e","bounce_id":""}]} +{"time":"2021-02-06T09:58:59.184925+01:00","hostname":"mail.server.com","process":"postfix/submission/smtpd[99525]","queue_id":"2D19728491","client_hostname":"unknown","client_ip":"10.11.12.13","sasl_method":"PLAIN","sasl_username":"user1@domain.example.com","message_id":"1612601937133741374.16589280196177750880@user1.domain.example.com","from":"user1@example.com","size":"11991","nrcpt":"1","messages":[{"time":"2021-02-06T09:58:59.560624+01:00","to":"destuser@destination.com","status":"sent","message":"to=\u003cdestuser@destination.com\u003e, relay=mail2.domain.com[2b00:128:64::1]:25, delay=9.7, delays=9.5/0.01/0.03/0.12, dsn=2.0.0, status=sent (250 2.0.0 Ok: queued as 702A02A5BF)","bounce_id":""}]} +{"time":"2021-02-06T11:19:18.130932+01:00","hostname":"mail2.server.com","process":"postfix/smtpd[61691]","queue_id":"1FEE23DED3","client_hostname":"unknown","client_ip":"2b00:128:64::42","sasl_method":"","sasl_username":"","message_id":"20210206101916.672CC1B4E5@sender.example.com","from":"","size":"","nrcpt":"","messages":[{"time":"2021-02-06T11:19:18.189624+01:00","to":"dest@domain.com","status":"bounced","message":"to=\u003cdest@domain.com\u003e, relay=relay.domain.com[22.33.44.55]:25, delay=0.06, delays=0/0/0.05/0.01, dsn=5.7.1, status=bounced (host relay.domain.com[22.33.44.55] said: 554 5.7.1 \u003cdest@domain.com\u003e: Relay access denied (in reply to RCPT TO command))","bounce_id":""}]} +{"time":"2021-02-06T11:19:25+01:00","hostname":"mail1.example.com","process":"postfix/smtpd[64346]","queue_id":"42E683DD0B","client_hostname":"mail.sender.com","client_ip":"85.42.66.4","sasl_method":"","sasl_username":"","message_id":"0tgs2nv56aexq9wn-dz6hijmzyfbesxxz-bcd8-493db@some.com","from":"18546-55426-655542-8520-recipient1=sender1@mail.some.com","size":"8703","nrcpt":"1","messages":[{"time":"2021-02-06T11:19:26+01:00","to":"recipient1@destdom.com","status":"bounced","message":"to=\u003crecipient1@destdom.com\u003e, relay=aspmx.l.google.com[2a00:1450:400c:c04::1b]:25, delay=0.59, delays=0.23/0/0.17/0.19, dsn=5.7.26, status=bounced (host aspmx.l.google.com[2a00:1450:400c:c04::1b] said: 550-5.7.26 This message does not have authentication information or fails to 550-5.7.26 pass authentication checks. To best protect our users from spam, the 550-5.7.26 message has been blocked. Please visit 550-5.7.26 https://support.google.com/mail/answer/81126#authentication for more 550 5.7.26 information. a42si85499772wrx.219 - gsmtp (in reply to end of DATA command))","bounce_id":"1F99E4BC9A"}]} +{"time":"2021-02-20T13:18:56+01:00","hostname":"srv-smtp","process":"postfix/smtpd[63060]","queue_id":"9ABCD1BCA9","client_hostname":"mailout.domain.com","client_ip":"10.11.12.13","sasl_method":"","sasl_username":"","message_id":"20210220121856.17088.40693@internal.domain.lan","from":"user1@domain.com","size":"5712","nrcpt":"1","messages":[{"time":"2021-02-20T13:18:57+01:00","to":"recipient@gmail.com","status":"deferred","message":"to=\u003crecipient@gmail.com\u003e, relay=gmail-smtp-in.l.google.com[2a00:1450:400c:c02::1b]:25, delay=0.41, delays=0.19/0/0.19/0.03, dsn=4.2.1, status=deferred (host gmail-smtp-in.l.google.com[2a00:1450:400c:c02::1b] said: 450-4.2.1 The user you are trying to contact is receiving mail at a rate that 450-4.2.1 prevents additional messages from being delivered. Please resend your 450-4.2.1 message at a later time. If the user is able to receive mail at that 450-4.2.1 time, your message will be delivered. For more information, please 450-4.2.1 visit 450 4.2.1 https://support.google.com/mail/?p=ReceivingRate f22si54329842vxg.042 - gsmtp (in reply to RCPT TO command))","bounce_id":""}]} +{"time":"2025-02-13T12:44:28.836533+01:00","hostname":"srv-smtp","process":"postfix/smtpd[65735]","queue_id":"","client_hostname":"unknown","client_ip":"41.42.43.44","sasl_method":"","sasl_username":"","message_id":"","from":"","size":"","nrcpt":"","messages":[{"time":"2025-02-13T12:44:28.836533+01:00","to":"","status":"postfix-ratelimit","message":"warning: Message delivery request rate limit exceeded: 19 from unknown[41.42.43.44] for service smtp","bounce_id":""}]} diff --git a/test/test-milter.146.out b/test/test-milter.146.out new file mode 100644 index 0000000..0b7913a --- /dev/null +++ b/test/test-milter.146.out @@ -0,0 +1,2 @@ +{"time":"2021-02-19T14:32:14.976132+01:00","hostname":"smtp01.example.org","process":"postfix/cleanup[52106]","queue_id":"E89FA2DEDA","client_hostname":"srv05.source.com","client_ip":"10.11.12.13","sasl_method":"","sasl_username":"","message_id":"","from":"","size":"","nrcpt":"","messages":[{"time":"2021-02-19T14:32:14.976132+01:00","to":"this_user@this.domain.com","status":"milter-hold","message":"milter-hold: END-OF-MESSAGE from srv05.source.com[10.11.12.13]: milter triggers HOLD action; from=\u003c\u003e to=\u003cthis_user@this.domain.com\u003e proto=ESMTP helo=\u003cthis.domain.com\u003e","bounce_id":""}]} +{"time":"2021-12-03T06:27:33.887389+01:00","hostname":"smtp01.example.org","process":"postfix/smtpd[50419]","queue_id":"B42E77AB32","client_hostname":"42-64-53-170.subs.proxad.net","client_ip":"42.64.53.170","sasl_method":"PLAIN","sasl_username":"user42@smtp01.example.com","message_id":"164017759823.25920.10144256874843932242@12f2bb757669","from":"youssefh@senderdomain.com","size":"","nrcpt":"","messages":[{"time":"2021-12-03T06:27:34.105536+01:00","to":"archive@senderdomain.com","status":"milter-reject","message":"milter-reject: END-OF-MESSAGE from 42-64-53-170.subs.proxad.net[42.64.53.170]: 4.7.1 Ratelimit \"05\" exceeded; from=\u003cyoussefh@senderdomain.com\u003e to=\u003carchive@senderdomain.com\u003e proto=ESMTP helo=\u003c[172.18.0.2]\u003e","bounce_id":""}]} diff --git a/test/test.146.out b/test/test.146.out new file mode 100644 index 0000000..b8d64c0 --- /dev/null +++ b/test/test.146.out @@ -0,0 +1,3 @@ +{"time":"0000-10-10T15:59:29+00:09","hostname":"mail","process":"postfix/smtpd[1827]","queue_id":"3D74ADB7400B","client_hostname":"example.com","client_ip":"127.0.0.1","sasl_method":"","sasl_username":"","message_id":"f93388828093534f92d85ffe21b2a719@example.info","from":"test2@example.info","size":"2140","nrcpt":"1","messages":[{"time":"0000-10-10T15:59:30+00:09","to":"test@example.to","status":"sent","message":"to=\u003ctest@example.to\u003e, relay=example.to[192.168.0.20]:25, delay=1.7, delays=0.02/0/1.7/0.06, dsn=2.0.0, status=sent (250 [Sniper] OK 1539154772 snipe-queue 10549)","bounce_id":""},{"time":"0000-10-10T15:59:30+00:09","to":"test2@example.to","status":"sent","message":"to=\u003ctest2@example.to\u003e, relay=example.to[192.168.0.20]:25, delay=1.7, delays=0.02/0/1.7/0.06, dsn=2.0.0, status=sent (250 [Sniper] OK 1539154772 snipe-queue 10549)","bounce_id":""}]} +{"time":"0000-10-10T15:59:31+00:09","hostname":"mail","process":"postfix/smtpd[1830]","queue_id":"6526CDB7400B","client_hostname":"example.com","client_ip":"127.0.0.1","sasl_method":"","sasl_username":"","message_id":"153915476795520900002087@example.aaa","from":"test@example.bbb","size":"4118","nrcpt":"1","messages":[{"time":"0000-10-10T15:59:31+00:09","to":"test@example.ccc","status":"sent","message":"to=\u003ctest@example.ccc\u003e, relay=example.ccc[192.168.0.10]:25, delay=0.1, delays=0.02/0/0.03/0.05, dsn=2.0.0, status=sent (250 2.0.0 Ok: queued as 71111424269E)","bounce_id":""}]} +{"time":"0000-10-10T15:59:28+00:09","hostname":"mail","process":"postfix/smtpd[1830]","queue_id":"C6E0DDB74006","client_hostname":"example.com","client_ip":"127.0.0.1","sasl_method":"","sasl_username":"","message_id":"A40CF64D-7F2D-42E4-8A76-CBFFF64A6EB1@example.com","from":"test@example.com","size":"309891","nrcpt":"1","messages":[{"time":"0000-10-10T15:59:32+00:09","to":"test@example.ddd","status":"sent","message":"to=\u003ctest@example.ddd\u003e, relay=example.ddd[192.168.0.30]:25, delay=3.4, delays=0.11/0/0.38/2.9, dsn=2.0.0, status=sent (250 2.0.0 OK 1539154772 az9-v6si5976496plb.190 - gsmtp)","bounce_id":""}]} From c00b2fc1af9a1214e09229833e9436e43ace2dae Mon Sep 17 00:00:00 2001 From: yo Date: Sun, 23 Feb 2025 11:51:17 +0100 Subject: [PATCH 094/100] Update dependencies --- go.mod | 35 ++-- go.sum | 602 ++++----------------------------------------------------- 2 files changed, 55 insertions(+), 582 deletions(-) diff --git a/go.mod b/go.mod index e066c23..759cb73 100644 --- a/go.mod +++ b/go.mod @@ -1,22 +1,25 @@ module github.com/yo000/postfix-log-parser +go 1.21 + +toolchain go1.23.2 + require ( - github.com/beorn7/perks v1.0.1 // indirect - github.com/cespare/xxhash/v2 v2.1.1 // indirect - github.com/golang/protobuf v1.5.2 // indirect - github.com/google/go-cmp v0.5.6 // indirect - github.com/inconshreveable/mousetrap v1.0.0 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect - github.com/prometheus/client_golang v1.11.0 - github.com/prometheus/client_model v0.2.0 // indirect - github.com/prometheus/common v0.29.0 // indirect - github.com/prometheus/procfs v0.6.0 // indirect - github.com/spf13/cobra v1.1.3 - github.com/spf13/pflag v1.0.5 // indirect + github.com/prometheus/client_golang v1.21.0 + github.com/spf13/cobra v1.9.1 github.com/tabalt/pidfile v1.1.0 - golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 // indirect - golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect - google.golang.org/protobuf v1.26.0 // indirect ) -go 1.13 +require ( + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/klauspost/compress v1.17.11 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.62.0 // indirect + github.com/prometheus/procfs v0.15.1 // indirect + github.com/spf13/pflag v1.0.6 // indirect + golang.org/x/sys v0.28.0 // indirect + google.golang.org/protobuf v1.36.1 // indirect +) diff --git a/go.sum b/go.sum index fd948a3..d8766ff 100644 --- a/go.sum +++ b/go.sum @@ -1,573 +1,43 @@ -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 v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -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/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -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/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -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/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/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 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= 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/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -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/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.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -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/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/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -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/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -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-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -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-20191227052852-215e87163ea7/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/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -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.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= -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/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -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.4.1/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.1/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-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -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/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -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/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -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/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -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-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/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/sdk v0.1.1/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-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-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.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/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -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.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -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/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -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.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/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -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/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 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/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/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/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -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/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= +github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 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/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/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.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= -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.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.29.0 h1:3jqPBvKT4OHAbje2Ql7KeaaSicDBCxMYwEJU1zRJceE= -github.com/prometheus/common v0.29.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/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.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= -github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -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/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -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.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -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/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 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8= -github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v1.1.3 h1:xghbfqPkxzxP3C/f3n5DdpAbdKLj4ZE4BWQI362l53M= -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.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -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/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/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/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/prometheus/client_golang v1.21.0 h1:DIsaGmiaBkSangBgMtWdNfxbMNdku5IK6iNhrEqWvdA= +github.com/prometheus/client_golang v1.21.0/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg= +github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= +github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= +github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= +github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= +github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= +github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= +github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/tabalt/pidfile v1.1.0 h1:Q7qQGZ4MoAXE+rvM5tB4/eAIrawewYewByhMiPoDE50= github.com/tabalt/pidfile v1.1.0/go.mod h1:7F1QwNrjfAApsuX4Nyah3RsbHVAdY/D9qZWp0nnJ/Uw= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -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.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -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-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/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/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/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -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/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -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.1.1-0.20191107180719-034126e5016b/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/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-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-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-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-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/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-20200222125558-5a598a2470a0/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-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/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-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/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= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -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-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-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/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-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-20190215142949-d0b11bdaac8a/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-20190412213103-97732733099d/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-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/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-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/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-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 h1:RqytpXGR1iVNX7psjB3ff8y7sNFinVFvkx1c8SjBkio= -golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -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.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -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-20180917221912-90fa682c2a6e/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-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-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-20190606124116-d0a3d012864b/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-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-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/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-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -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 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -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/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -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/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -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-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-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.23.0/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.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -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/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk= +google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= 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/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -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.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -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= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From 451fe05a39ba2d13ee70624f8a6f35ab9f6456c2 Mon Sep 17 00:00:00 2001 From: yo Date: Sun, 16 Mar 2025 15:50:51 +0100 Subject: [PATCH 095/100] Add "filter: RCPT" support" --- postfix-log-parser.go | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/postfix-log-parser.go b/postfix-log-parser.go index 41f1b94..73d37a7 100644 --- a/postfix-log-parser.go +++ b/postfix-log-parser.go @@ -24,7 +24,8 @@ const ( AuthentFailedRegexpFormat = `(?:warning: (.+)\[(.+)\]: SASL (.*) authentication failed: (.*))?` PostfixRejectRegexpFormat = `(?:reject: RCPT from (.+)\[(.+)\]: [0-9]{3} [0-9\.]{5} [^;]*; from=<(.+@.+)?> to=<(.+@[^>]+)>.*$)?` PostfixRLimitExceedFormat = `(?:warning: Message delivery request rate limit exceeded: ([0-9]+) from (.+)\[(.+)\] for service [a-zA-Z]+)?` - MessageDetailsRegexpFormat = `(` + ClientRegexpFormat + MessageIdRegexpFormat + FromRegexpFormat + ToRegexpFormat + SenderNDNRegexpFormat + MilterRegexpFormat + AuthentFailedRegexpFormat + PostfixRejectRegexpFormat + PostfixRLimitExceedFormat + `.*)` + FilterRcptRegexpFormat = `(?:filter: RCPT from (.+)\[(.+)\]: <(.+@.+)?>: ([^;]+); from=<(.+@.+)?> to=<(.+@[^>]+)> proto=[^ ]+ helo=<.+>)?` + MessageDetailsRegexpFormat = `(` + ClientRegexpFormat + MessageIdRegexpFormat + FromRegexpFormat + ToRegexpFormat + SenderNDNRegexpFormat + MilterRegexpFormat + AuthentFailedRegexpFormat + PostfixRejectRegexpFormat + PostfixRLimitExceedFormat + FilterRcptRegexpFormat + `.*)` RegexpFormat = SyslogPri + TimeRegexpFormat + ` ` + HostRegexpFormat + ` ` + ProcessRegexpFormat + `:? ` + QueueIdRegexpFormat + `(?:\: )?` + MessageDetailsRegexpFormat ) @@ -39,7 +40,7 @@ type ( Hostname string `json:"hostname"` Process string `json:"process"` QueueId string `json:"queue_id"` - Messages string `json:"messages"` + Message string `json:"message"` ClientHostname string `json:"client_hostname"` ClinetIp string `json:"client_ip"` SaslMethod string `json:"sasl_method"` @@ -81,7 +82,7 @@ func (p *PostfixLog) Parse(text []byte) (LogFormat, error) { Hostname: string(group[2]), Process: string(group[3]), QueueId: string(group[4]), - Messages: string(group[5]), + Message: string(group[5]), SaslMethod: string(group[8]), SaslUsername: string(group[9]), MessageId: string(group[10]), @@ -115,6 +116,13 @@ func (p *PostfixLog) Parse(text []byte) (LogFormat, error) { logFormat.ClientHostname = string(group[31]) logFormat.ClinetIp = string(group[32]) logFormat.Status = "postfix-ratelimit" + // postfix filter RCPT + } else if len(group[33]) > 0 { + logFormat.ClientHostname = string(group[33]) + logFormat.ClinetIp = string(group[34]) + logFormat.From = string(group[37]) + logFormat.To = string(group[38]) + logFormat.Status = "postfix-filter" // bounced by remote } else { logFormat.Status = string(group[15]) From ddf3d89b4fe3a4f8d8037e523eefafe128aca642 Mon Sep 17 00:00:00 2001 From: yo Date: Sun, 16 Mar 2025 15:51:11 +0100 Subject: [PATCH 096/100] Fix NRcpt, add "filter: RCPT" support, show regex option --- postfix-log-parser/cmd/root.go | 185 +++++++++++++++++++++++++++++---- 1 file changed, 166 insertions(+), 19 deletions(-) diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index 1f52372..f9fd96b 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -41,9 +41,12 @@ type ( From string `json:"from"` Size string `json:"size"` NRcpt string `json:"nrcpt"` - Messages []Message `json:"messages"` + Nrcpti int `json:"-"` + Messages Messages `json:"messages"` } + Messages []Message + Message struct { Time *time.Time `json:"time"` To string `json:"to"` @@ -77,7 +80,7 @@ var ( File os.File Writer *bufio.Writer - Version = "1.4.6" + Version = "1.4.7" BuildInfo = promauto.NewGaugeVec(prometheus.GaugeOpts{ Name: "postfixlogparser_build_info", @@ -141,6 +144,7 @@ var ( }, } + gShowRegex bool gFlatten bool gOutputFile string gPidFilePath string @@ -149,6 +153,42 @@ var ( gPromMetricPath string ) + +func (p *PostfixLogParser) Copy() PostfixLogParser { + var nm []Message + + for _, m := range p.Messages { + nm = append(nm, m) + } + + return PostfixLogParser{ + Time: p.Time, + Hostname: p.Hostname, + Process: p.Process, + QueueId: p.QueueId, + ClientHostname: p.ClientHostname, + ClinetIp: p.ClinetIp, + SaslMethod: p.SaslMethod, + SaslUsername: p.SaslUsername, + MessageId: p.MessageId, + From: p.From, + Size: p.Size, + NRcpt: p.NRcpt, + Nrcpti: p.Nrcpti, + Messages: nm, + } +} + +func (m *Messages) GetByRecipient(recipient string) (*Message, int, error) { + for i, msg := range *m { + if strings.EqualFold(msg.To, recipient) { + return &msg, i, nil + } + } + return &Message{}, -1, fmt.Errorf("Not found") +} + + func Execute() { if err := rootCmd.Execute(); err != nil { rootCmd.SetOutput(os.Stderr) @@ -173,7 +213,9 @@ func PlpToFlat(plp *PostfixLogParser) []PostfixLogParserFlat { MessageId: plp.MessageId, From: plp.From, Size: plp.Size, - NRcpt: plp.NRcpt, + // PlpToFlat make 1 json object per message, so we need to override NRcpt + //NRcpt: plp.NRcpt, + NRcpt: "1", TimeSent: plp.Messages[i].Time, To: plp.Messages[i].To, Status: plp.Messages[i].Status, @@ -205,6 +247,7 @@ func NewWriter(file string) (*bufio.Writer, *os.File, error) { } } +// TODO: Remove filename arg func writeOut(msg string, filename string) error { _, err := fmt.Fprintln(Writer, msg) Writer.Flush() @@ -263,9 +306,9 @@ func periodicallyCleanMQueue(mqueue map[string]*PostfixLogParser, mqMtx *sync.Mu func initConfig() {} func init() { - rootCmd.Version = Version + rootCmd.Flags().BoolVarP(&gShowRegex, "regex", "r", false, "Show parsing regex then exit") rootCmd.Flags().BoolVarP(&gFlatten, "gFlatten", "f", false, "Flatten output for using with syslog") rootCmd.Flags().StringVarP(&gOutputFile, "out", "o", "", "Output to file, append if exists") rootCmd.Flags().StringVarP(&gPidFilePath, "pidfile", "p", "", "pid file path") @@ -281,8 +324,8 @@ func init() { * Each input is stored in a map, * then written to output when we recognize it as the last line */ -func parseStoreAndWrite(input []byte, mq map[string]*PostfixLogParser, mqMtx *sync.Mutex, - outfMtx *sync.Mutex, p *postfixlog.PostfixLog) error { +func parseStoreAndWrite(input []byte, mq map[string]*PostfixLogParser, mqMtx *sync.Mutex, outfMtx *sync.Mutex, p *postfixlog.PostfixLog) error { + logFormat, err := p.Parse(input) if err != nil { // Incorrect line, just skip it @@ -308,7 +351,7 @@ func parseStoreAndWrite(input []byte, mq map[string]*PostfixLogParser, mqMtx *sy message := Message{ Time: logFormat.Time, Status: logFormat.Status, - Message: logFormat.Messages, + Message: logFormat.Message, } tmpplp := PostfixLogParser{ @@ -348,7 +391,7 @@ func parseStoreAndWrite(input []byte, mq map[string]*PostfixLogParser, mqMtx *sy message := Message{ Time: logFormat.Time, Status: logFormat.Status, - Message: logFormat.Messages, + Message: logFormat.Message, } tmpplp := PostfixLogParser{ @@ -380,10 +423,59 @@ func parseStoreAndWrite(input []byte, mq map[string]*PostfixLogParser, mqMtx *sy return nil } + // postfix-filter : We don't store message, only increment nrcpt and add recipient. + // postfix-filter is a transitional state : mail will either be sent, or milter-rejected + // check if queue_id already exist. Check if queue_id already exist. If so, +1 to Nrcpt and add recipient to list + if strings.EqualFold(logFormat.Status, "postfix-filter") { + mqMtx.Lock() + message := Message{ + Time: logFormat.Time, + To: logFormat.To, + Status: logFormat.Status, + Message: logFormat.Message, + BounceId: "", + } + if plp, ok := mq[logFormat.QueueId]; ok { + nplp := plp.Copy() + if len(nplp.From) == 0 { + nplp.From = logFormat.From + } + if len(nplp.ClientHostname) == 0 { + nplp.ClientHostname = logFormat.ClientHostname + } + if len(nplp.ClinetIp) == 0 { + nplp.ClinetIp = logFormat.ClinetIp + } + nplp.Messages = append(nplp.Messages, message) + nplp.Nrcpti++ + nplp.NRcpt = strconv.Itoa(nplp.Nrcpti) + mq[logFormat.QueueId] = &nplp + } else { + nplp := &PostfixLogParser{ + Time: logFormat.Time, + Hostname: logFormat.Hostname, + Process: logFormat.Process, + QueueId: logFormat.QueueId, + ClientHostname: logFormat.ClientHostname, + ClinetIp: logFormat.ClinetIp, + SaslMethod: logFormat.SaslMethod, + SaslUsername: logFormat.SaslUsername, + From: logFormat.From, + Nrcpti: 1, + NRcpt: "1", + } + nplp.Messages = append(nplp.Messages, message) + mq[logFormat.QueueId] = nplp + } + mqMtx.Unlock() + + return nil + } + /* Oct 10 04:02:02 mail.example.com postfix/smtpd[22941]: DFBEFDBF00C5: client=example.net[127.0.0.1], sasl_method=PLAIN, sasl_username=user@example.com */ - if logFormat.ClientHostname != "" && !strings.HasPrefix(logFormat.Messages, "milter-reject:") { + if logFormat.ClientHostname != "" && !strings.HasPrefix(logFormat.Message, "milter-reject:") { mqMtx.Lock() mq[logFormat.QueueId] = &PostfixLogParser{ Time: logFormat.Time, @@ -411,13 +503,17 @@ func parseStoreAndWrite(input []byte, mq map[string]*PostfixLogParser, mqMtx *sy /* Oct 10 04:02:03 mail.example.com postfix/qmgr[18719]: DFBEFDBF00C5: from=, size=3578, nrcpt=1 (queue active) + or + 2021-02-06T10:57:58+01:00 mail.server.com postfix/cleanup[59586]: D6DDA23B0F: milter-reject: END-OF-MESSAGE from unknown[99.88.77.66]: 4.7.1 Greylisting in action, try again later; from= to= proto=ESMTP helo= */ if logFormat.From != "" { mqMtx.Lock() if plp, ok := mq[logFormat.QueueId]; ok { plp.From = logFormat.From - plp.Size = logFormat.Size - plp.NRcpt = logFormat.NRcpt + if logFormat.Size != "" { + plp.Size = logFormat.Size + } + // Do not handle NRcpt here, it will be done next block } mqMtx.Unlock() nrcpt, _ := strconv.ParseFloat(logFormat.NRcpt, 64) @@ -426,7 +522,10 @@ func parseStoreAndWrite(input []byte, mq map[string]*PostfixLogParser, mqMtx *sy /* Oct 10 04:02:08 mail.example.com postfix/smtp[22928]: DFBEFDBF00C5: to=, relay=mail.example-to.com[192.168.0.10]:25, delay=5.3, delays=0.26/0/0.31/4.7, dsn=2.0.0, status=sent (250 2.0.0 Ok: queued as C598F1B0002D) + or + 2025-03-15T11:10:11.206043+01:00 mail.example.com postfix/cleanup[58145] 53AEC4F144: milter-reject: END-OF-MESSAGE from unknown[1.2.3.4]: 4.7.1 Ratelimit "to_ip_from" exceeded; from= to= proto=ESMTP helo= */ + //if logFormat.To != "" { if logFormat.To != "" { mqMtx.Lock() if plp, ok := mq[logFormat.QueueId]; ok { @@ -434,7 +533,7 @@ func parseStoreAndWrite(input []byte, mq map[string]*PostfixLogParser, mqMtx *sy Time: logFormat.Time, To: logFormat.To, Status: logFormat.Status, - Message: logFormat.Messages, + Message: logFormat.Message, BounceId: "", } /* When a message is deferred, it won't be written out until it is either sent, expired, or generates a non delivery notification. @@ -455,7 +554,8 @@ func parseStoreAndWrite(input []byte, mq map[string]*PostfixLogParser, mqMtx *sy MessageId: plp.MessageId, From: plp.From, Size: plp.Size, - NRcpt: plp.NRcpt, + // Could it be > 1 ? + NRcpt: "1", } tmpplp.Messages = append(tmpplp.Messages, message) @@ -478,7 +578,14 @@ func parseStoreAndWrite(input []byte, mq map[string]*PostfixLogParser, mqMtx *sy // cannot use nil as type PostfixLogParser in assignment //tmpplp = nil } else { - plp.Messages = append(plp.Messages, message) + _, i, err := plp.Messages.GetByRecipient(logFormat.To) + if err != nil && strings.EqualFold(err.Error(), "Not found") { + plp.Messages = append(plp.Messages, message) + plp.Nrcpti++ + plp.NRcpt = strconv.Itoa(plp.Nrcpti) + } else { + plp.Messages[i] = message + } } } mqMtx.Unlock() @@ -511,15 +618,34 @@ func parseStoreAndWrite(input []byte, mq map[string]*PostfixLogParser, mqMtx *sy mqMtx.Unlock() } + // milter-reject reject message for all recipient, so we need to update status for every message + if logFormat.Status == "milter-reject" { + mqMtx.Lock() + if plp, ok := mq[logFormat.QueueId]; ok { + for i, _ := range plp.Messages { + plp.Messages[i].Time = logFormat.Time + plp.Messages[i].Status = logFormat.Status + // FIXME: We override message, but the originating mesage from postfix log could be wrong as it have the last recipient in "to=<>" + // and we don't want to insert manipulated log line + // Would it be better to clear message? or leave previous one which could be unrelated to the postfix decision? eg: + // "filter: RCPT from unknown[192.168.0.22]: : Recipient address triggers FILTER relay:[pod-02.smtpout.exploit.intra]; from= to= proto=ESMTP helo=" + plp.Messages[i].Message = logFormat.Message + } + } else { + // TODO : error case : milter reject for unknown message ?! + } + mqMtx.Unlock() + } + /* Oct 10 04:02:08 mail.example.com postfix/qmgr[18719]: DFBEFDBF00C5: removed - or + or 2021-02-05T14:17:51+01:00 smtp.server.com postfix/cleanup[38982]: D8C136A3A: milter-reject: END-OF-MESSAGE from unknown[1.2.3.4]: 4.7.1 Greylisting in action, try again later; from= to= proto=ESMTP helo= - or + or 2023-12-22T09:11:42.627010+01:00 smtp-02.example.org postfix/smtpd[2717] 6CB5F45B78: reject: RCPT from unknown[11.12.13.14]: 450 4.1.2 : Recipient address rejected: Domain not found; from= to= proto=ESMTP helo= */ // "removed" message is end of logs. then flush. - if logFormat.Messages == "removed" || strings.HasPrefix(logFormat.Status, "milter-") || strings.EqualFold(logFormat.Status, "postfix-reject") { + if logFormat.Message == "removed" || strings.HasPrefix(logFormat.Status, "milter-") || strings.EqualFold(logFormat.Status, "postfix-reject") { mqMtx.Lock() if plp, ok := mq[logFormat.QueueId]; ok { for _, plpf := range PlpToFlat(plp) { @@ -617,6 +743,11 @@ func processLogs(cmd *cobra.Command, args []string) { var mqMtx sync.Mutex var useStdin bool + if gShowRegex == true { + fmt.Printf("%s\n", postfixlog.RegexpFormat) + return + } + // create map of messages mQueue := make(map[string]*PostfixLogParser) @@ -686,7 +817,6 @@ func processLogs(cmd *cobra.Command, args []string) { // Cleaner thread go periodicallyCleanMQueue(mQueue, &mqMtx) - // On demand Mqueue cleaning... For debug, dont try this at home, kids! /* sig2 := make(chan os.Signal) signal.Notify(sig2, syscall.SIGUSR2) @@ -696,8 +826,25 @@ func processLogs(cmd *cobra.Command, args []string) { cleanMQueue(mQueue, &mqMtx, 1 * time.Hour) } }() -*/ +*/ + // On demand queue dump +/* sig2 := make(chan os.Signal) + signal.Notify(sig2, syscall.SIGUSR2) + go func() { + for { + <-sig2 + fmt.Printf("Dump current in-ram queue. Length: %d\n", len(mQueue)) + + fmt.Printf("%v\n", mQueue) + mqMtx.Lock() + for k, v := range mQueue { + fmt.Printf("%v: %v\n", k, v) + } + mqMtx.Unlock() + } + }() +*/ // Initialize Stdin input... if true == strings.EqualFold(gSyslogListenAddress, "do-not-listen") { useStdin = true From 0d63de1c4b462a1153be75d1a1d888c6d3069a77 Mon Sep 17 00:00:00 2001 From: yo Date: Sun, 16 Mar 2025 15:59:31 +0100 Subject: [PATCH 097/100] Update tests --- test/test-iso8601.log | 11 ++ test/test-iso8601.log.142.json | 191 ++++++++++++++++++++++++ test/test-iso8601.log.146.json | 214 +++++++++++++++++++++++++++ test/test-iso8601.log.146.out | 9 -- test/test-iso8601.log.147.json | 258 +++++++++++++++++++++++++++++++++ test/test-milter.142.json | 46 ++++++ test/test-milter.146.json | 46 ++++++ test/test-milter.146.out | 2 - test/test-milter.147.json | 46 ++++++ test/test.142.json | 76 ++++++++++ test/test.146.json | 76 ++++++++++ test/test.146.out | 3 - test/test.147.json | 76 ++++++++++ test/test.rcpt.147.json | 44 ++++++ test/test.rcpt.log | 11 ++ 15 files changed, 1095 insertions(+), 14 deletions(-) create mode 100644 test/test-iso8601.log.142.json create mode 100644 test/test-iso8601.log.146.json delete mode 100644 test/test-iso8601.log.146.out create mode 100644 test/test-iso8601.log.147.json create mode 100644 test/test-milter.142.json create mode 100644 test/test-milter.146.json delete mode 100644 test/test-milter.146.out create mode 100644 test/test-milter.147.json create mode 100644 test/test.142.json create mode 100644 test/test.146.json delete mode 100644 test/test.146.out create mode 100644 test/test.147.json create mode 100644 test/test.rcpt.147.json create mode 100644 test/test.rcpt.log diff --git a/test/test-iso8601.log b/test/test-iso8601.log index a07dabf..a729141 100644 --- a/test/test-iso8601.log +++ b/test/test-iso8601.log @@ -46,3 +46,14 @@ 2021-02-20T13:18:56+01:00 srv-smtp postfix/smtp[62771]: 9ABCD1BCA9: host gmail-smtp-in.l.google.com[74.125.140.27] said: 450-4.2.1 The user you are trying to contact is receiving mail at a rate that 450-4.2.1 prevents additional messages from being delivered. Please resend your 450-4.2.1 message at a later time. If the user is able to receive mail at that 450-4.2.1 time, your message will be delivered. For more information, please 450-4.2.1 visit 450 4.2.1 https://support.google.com/mail/?p=ReceivingRate f22si54329842vxg.12 - gsmtp (in reply to RCPT TO command) 2021-02-20T13:18:57+01:00 srv-smtp postfix/smtp[62771]: 9ABCD1BCA9: to=, relay=gmail-smtp-in.l.google.com[2a00:1450:400c:c02::1b]:25, delay=0.41, delays=0.19/0/0.19/0.03, dsn=4.2.1, status=deferred (host gmail-smtp-in.l.google.com[2a00:1450:400c:c02::1b] said: 450-4.2.1 The user you are trying to contact is receiving mail at a rate that 450-4.2.1 prevents additional messages from being delivered. Please resend your 450-4.2.1 message at a later time. If the user is able to receive mail at that 450-4.2.1 time, your message will be delivered. For more information, please 450-4.2.1 visit 450 4.2.1 https://support.google.com/mail/?p=ReceivingRate f22si54329842vxg.042 - gsmtp (in reply to RCPT TO command)) 2025-02-13T12:44:28.836533+01:00 srv-smtp postfix/smtpd[65735] warning: Message delivery request rate limit exceeded: 19 from unknown[41.42.43.44] for service smtp +2025-03-15T13:30:29.169282+01:00 smtp.example.org postfix/smtpd[17507] warning: hostname smtp.internal.local does not resolve to address 192.168.0.22: Name does not resolve +2025-03-15T13:30:29.169332+01:00 smtp.example.org postfix/smtpd[17507] connect from unknown[192.168.0.22] +2025-03-15T13:30:29.221416+01:00 smtp.example.org postfix/smtpd[17507] Anonymous TLS connection established from unknown[192.168.0.22]: TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits) +2025-03-15T13:30:30.342887+01:00 smtp.example.org postfix/smtpd[17507] 53AEC4F144: client=unknown[192.168.0.22], sasl_method=LOGIN, sasl_username=smtpuser@smtp.example.org +2025-03-15T13:30:30.352537+01:00 smtp.example.org postfix/smtpd[17507] 53AEC4F144: filter: RCPT from unknown[192.168.0.22]: : Recipient address triggers FILTER relay:[pod-02.smtpout.exploit.intra]; from= to= proto=ESMTP helo= +2025-03-15T13:30:30.355033+01:00 smtp.example.org postfix/smtpd[17507] 53AEC4F144: filter: RCPT from unknown[192.168.0.22]: : Recipient address triggers FILTER relay:[pod-02.smtpout.exploit.intra]; from= to= proto=ESMTP helo= +2025-03-15T13:30:30.362445+01:00 smtp.example.org postfix/smtpd[17507] 53AEC4F144: filter: RCPT from unknown[192.168.0.22]: : Recipient address triggers FILTER relay:[pod-02.smtpout.exploit.intra]; from= to= proto=ESMTP helo= +2025-03-15T13:30:31.158051+01:00 smtp.example.org postfix/cleanup[58145] 53AEC4F144: replace: header Received: from smtp.internal.local (unknown [192.168.0.22])??(using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits))??(No client certificate requested)??(Authenticated sender: smtpuser from unknown[192.168.0.22]; from= to= proto=ESMTP helo=: Received: from smtp.internal.local (unknown [192.168.0.22])??(using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits))??(No client certificate requested)??(Authenticated sender: anonymized)??by smtp.example.org (Postfix) with ESMTPSA id 53AEC4F144;??Sat, 15 Mar 2025 13:30:29 +0100 (CET) +2025-03-15T13:30:31.158169+01:00 smtp.example.org postfix/cleanup[58145] 53AEC4F144: message-id=<94851c4c5f9b5ef98755296606bf32ab@example.org> +2025-03-15T13:30:31.206043+01:00 smtp.example.org postfix/cleanup[58145] 53AEC4F144: milter-reject: END-OF-MESSAGE from unknown[192.168.0.22]: 4.7.1 Ratelimit "to_ip_from" exceeded; from= to= proto=ESMTP helo= +2025-03-15T13:30:31.238801+01:00 smtp.example.org postfix/smtpd[17507] disconnect from unknown[192.168.0.22] ehlo=2 starttls=1 auth=1 mail=1 rcpt=4/4 bdat=0/1 rset=1 quit=1 commands=8/10 diff --git a/test/test-iso8601.log.142.json b/test/test-iso8601.log.142.json new file mode 100644 index 0000000..524c205 --- /dev/null +++ b/test/test-iso8601.log.142.json @@ -0,0 +1,191 @@ +{ + "time": "2020-01-30T06:26:06.404714+09:00", + "hostname": "mail", + "process": "postfix/smtpd[1827]", + "queue_id": "3D74ADB7400B", + "client_hostname": "example.com", + "client_ip": "127.0.0.1", + "sasl_method": "", + "sasl_username": "", + "message_id": "f93388828093534f92d85ffe21b2a719@example.info", + "from": "test2@example.info", + "size": "2140", + "nrcpt": "1", + "messages": [ + { + "time": "2020-01-30T06:26:07.404714+09:00", + "to": "test@example.to", + "status": "sent", + "message": "to=, relay=example.to[192.168.0.20]:25, delay=1.7, delays=0.02/0/1.7/0.06, dsn=2.0.0, status=sent (250 [Sniper] OK 1539154772 snipe-queue 10549)", + "bounce_id": "" + }, + { + "time": "2020-01-30T06:26:07.404714+09:00", + "to": "test2@example.to", + "status": "sent", + "message": "to=, relay=example.to[192.168.0.20]:25, delay=1.7, delays=0.02/0/1.7/0.06, dsn=2.0.0, status=sent (250 [Sniper] OK 1539154772 snipe-queue 10549)", + "bounce_id": "" + } + ] +} +{ + "time": "2020-01-30T06:26:08.404714+09:00", + "hostname": "mail", + "process": "postfix/smtpd[1830]", + "queue_id": "6526CDB7400B", + "client_hostname": "example.com", + "client_ip": "127.0.0.1", + "sasl_method": "", + "sasl_username": "", + "message_id": "153915476795520900002087@example.aaa", + "from": "test@example.bbb", + "size": "4118", + "nrcpt": "1", + "messages": [ + { + "time": "2020-01-30T06:26:08.404714+09:00", + "to": "test@example.ccc", + "status": "sent", + "message": "to=, relay=example.ccc[192.168.0.10]:25, delay=0.1, delays=0.02/0/0.03/0.05, dsn=2.0.0, status=sent (250 2.0.0 Ok: queued as 71111424269E)", + "bounce_id": "" + } + ] +} +{ + "time": "2020-01-30T06:26:05.404714+09:00", + "hostname": "mail", + "process": "postfix/smtpd[1830]", + "queue_id": "C6E0DDB74006", + "client_hostname": "example.com", + "client_ip": "127.0.0.1", + "sasl_method": "", + "sasl_username": "", + "message_id": "A40CF64D-7F2D-42E4-8A76-CBFFF64A6EB1@example.com", + "from": "test@example.com", + "size": "309891", + "nrcpt": "1", + "messages": [ + { + "time": "2020-01-30T06:26:09.404714+09:00", + "to": "test@example.ddd", + "status": "sent", + "message": "to=, relay=example.ddd[192.168.0.30]:25, delay=3.4, delays=0.11/0/0.38/2.9, dsn=2.0.0, status=sent (250 2.0.0 OK 1539154772 az9-v6si5976496plb.190 - gsmtp)", + "bounce_id": "" + } + ] +} +{ + "time": "2021-02-06T10:57:57+01:00", + "hostname": "mail.server.com", + "process": "postfix/smtpd[59633]", + "queue_id": "D6DDA23B0F", + "client_hostname": "unknown", + "client_ip": "99.88.77.66", + "sasl_method": "", + "sasl_username": "", + "message_id": "20210206095814.2B34E19A19F6@host.sender.domain", + "from": "user2@example.com", + "size": "", + "nrcpt": "", + "messages": [ + { + "time": "2021-02-06T10:57:58+01:00", + "to": "some.dest@destdomain.com", + "status": "milter-reject", + "message": "milter-reject: END-OF-MESSAGE from unknown[99.88.77.66]: 4.7.1 Greylisting in action, try again later; from= to= proto=ESMTP helo=", + "bounce_id": "" + } + ] +} +{ + "time": "2021-02-06T09:58:59.184925+01:00", + "hostname": "mail.server.com", + "process": "postfix/submission/smtpd[99525]", + "queue_id": "2D19728491", + "client_hostname": "unknown", + "client_ip": "10.11.12.13", + "sasl_method": "PLAIN", + "sasl_username": "user1@domain.example.com", + "message_id": "1612601937133741374.16589280196177750880@user1.domain.example.com", + "from": "user1@example.com", + "size": "11991", + "nrcpt": "1", + "messages": [ + { + "time": "2021-02-06T09:58:59.560624+01:00", + "to": "destuser@destination.com", + "status": "sent", + "message": "to=, relay=mail2.domain.com[2b00:128:64::1]:25, delay=9.7, delays=9.5/0.01/0.03/0.12, dsn=2.0.0, status=sent (250 2.0.0 Ok: queued as 702A02A5BF)", + "bounce_id": "" + } + ] +} +{ + "time": "2021-02-06T11:19:18.130932+01:00", + "hostname": "mail2.server.com", + "process": "postfix/smtpd[61691]", + "queue_id": "1FEE23DED3", + "client_hostname": "unknown", + "client_ip": "2b00:128:64::42", + "sasl_method": "", + "sasl_username": "", + "message_id": "20210206101916.672CC1B4E5@sender.example.com", + "from": "", + "size": "", + "nrcpt": "", + "messages": [ + { + "time": "2021-02-06T11:19:18.189624+01:00", + "to": "dest@domain.com", + "status": "bounced", + "message": "to=, relay=relay.domain.com[22.33.44.55]:25, delay=0.06, delays=0/0/0.05/0.01, dsn=5.7.1, status=bounced (host relay.domain.com[22.33.44.55] said: 554 5.7.1 : Relay access denied (in reply to RCPT TO command))", + "bounce_id": "" + } + ] +} +{ + "time": "2021-02-06T11:19:25+01:00", + "hostname": "mail1.example.com", + "process": "postfix/smtpd[64346]", + "queue_id": "42E683DD0B", + "client_hostname": "mail.sender.com", + "client_ip": "85.42.66.4", + "sasl_method": "", + "sasl_username": "", + "message_id": "0tgs2nv56aexq9wn-dz6hijmzyfbesxxz-bcd8-493db@some.com", + "from": "18546-55426-655542-8520-recipient1=sender1@mail.some.com", + "size": "8703", + "nrcpt": "1", + "messages": [ + { + "time": "2021-02-06T11:19:26+01:00", + "to": "recipient1@destdom.com", + "status": "bounced", + "message": "to=, relay=aspmx.l.google.com[2a00:1450:400c:c04::1b]:25, delay=0.59, delays=0.23/0/0.17/0.19, dsn=5.7.26, status=bounced (host aspmx.l.google.com[2a00:1450:400c:c04::1b] said: 550-5.7.26 This message does not have authentication information or fails to 550-5.7.26 pass authentication checks. To best protect our users from spam, the 550-5.7.26 message has been blocked. Please visit 550-5.7.26 https://support.google.com/mail/answer/81126#authentication for more 550 5.7.26 information. a42si85499772wrx.219 - gsmtp (in reply to end of DATA command))", + "bounce_id": "1F99E4BC9A" + } + ] +} +{ + "time": "2021-02-20T13:18:56+01:00", + "hostname": "srv-smtp", + "process": "postfix/smtpd[63060]", + "queue_id": "9ABCD1BCA9", + "client_hostname": "mailout.domain.com", + "client_ip": "10.11.12.13", + "sasl_method": "", + "sasl_username": "", + "message_id": "20210220121856.17088.40693@internal.domain.lan", + "from": "user1@domain.com", + "size": "5712", + "nrcpt": "1", + "messages": [ + { + "time": "2021-02-20T13:18:57+01:00", + "to": "recipient@gmail.com", + "status": "deferred", + "message": "to=, relay=gmail-smtp-in.l.google.com[2a00:1450:400c:c02::1b]:25, delay=0.41, delays=0.19/0/0.19/0.03, dsn=4.2.1, status=deferred (host gmail-smtp-in.l.google.com[2a00:1450:400c:c02::1b] said: 450-4.2.1 The user you are trying to contact is receiving mail at a rate that 450-4.2.1 prevents additional messages from being delivered. Please resend your 450-4.2.1 message at a later time. If the user is able to receive mail at that 450-4.2.1 time, your message will be delivered. For more information, please 450-4.2.1 visit 450 4.2.1 https://support.google.com/mail/?p=ReceivingRate f22si54329842vxg.042 - gsmtp (in reply to RCPT TO command))", + "bounce_id": "" + } + ] +} diff --git a/test/test-iso8601.log.146.json b/test/test-iso8601.log.146.json new file mode 100644 index 0000000..5d33b31 --- /dev/null +++ b/test/test-iso8601.log.146.json @@ -0,0 +1,214 @@ +{ + "time": "2020-01-30T06:26:06.404714+09:00", + "hostname": "mail", + "process": "postfix/smtpd[1827]", + "queue_id": "3D74ADB7400B", + "client_hostname": "example.com", + "client_ip": "127.0.0.1", + "sasl_method": "", + "sasl_username": "", + "message_id": "f93388828093534f92d85ffe21b2a719@example.info", + "from": "test2@example.info", + "size": "2140", + "nrcpt": "1", + "messages": [ + { + "time": "2020-01-30T06:26:07.404714+09:00", + "to": "test@example.to", + "status": "sent", + "message": "to=, relay=example.to[192.168.0.20]:25, delay=1.7, delays=0.02/0/1.7/0.06, dsn=2.0.0, status=sent (250 [Sniper] OK 1539154772 snipe-queue 10549)", + "bounce_id": "" + }, + { + "time": "2020-01-30T06:26:07.404714+09:00", + "to": "test2@example.to", + "status": "sent", + "message": "to=, relay=example.to[192.168.0.20]:25, delay=1.7, delays=0.02/0/1.7/0.06, dsn=2.0.0, status=sent (250 [Sniper] OK 1539154772 snipe-queue 10549)", + "bounce_id": "" + } + ] +} +{ + "time": "2020-01-30T06:26:08.404714+09:00", + "hostname": "mail", + "process": "postfix/smtpd[1830]", + "queue_id": "6526CDB7400B", + "client_hostname": "example.com", + "client_ip": "127.0.0.1", + "sasl_method": "", + "sasl_username": "", + "message_id": "153915476795520900002087@example.aaa", + "from": "test@example.bbb", + "size": "4118", + "nrcpt": "1", + "messages": [ + { + "time": "2020-01-30T06:26:08.404714+09:00", + "to": "test@example.ccc", + "status": "sent", + "message": "to=, relay=example.ccc[192.168.0.10]:25, delay=0.1, delays=0.02/0/0.03/0.05, dsn=2.0.0, status=sent (250 2.0.0 Ok: queued as 71111424269E)", + "bounce_id": "" + } + ] +} +{ + "time": "2020-01-30T06:26:05.404714+09:00", + "hostname": "mail", + "process": "postfix/smtpd[1830]", + "queue_id": "C6E0DDB74006", + "client_hostname": "example.com", + "client_ip": "127.0.0.1", + "sasl_method": "", + "sasl_username": "", + "message_id": "A40CF64D-7F2D-42E4-8A76-CBFFF64A6EB1@example.com", + "from": "test@example.com", + "size": "309891", + "nrcpt": "1", + "messages": [ + { + "time": "2020-01-30T06:26:09.404714+09:00", + "to": "test@example.ddd", + "status": "sent", + "message": "to=, relay=example.ddd[192.168.0.30]:25, delay=3.4, delays=0.11/0/0.38/2.9, dsn=2.0.0, status=sent (250 2.0.0 OK 1539154772 az9-v6si5976496plb.190 - gsmtp)", + "bounce_id": "" + } + ] +} +{ + "time": "2021-02-06T10:57:57+01:00", + "hostname": "mail.server.com", + "process": "postfix/smtpd[59633]", + "queue_id": "D6DDA23B0F", + "client_hostname": "unknown", + "client_ip": "99.88.77.66", + "sasl_method": "", + "sasl_username": "", + "message_id": "20210206095814.2B34E19A19F6@host.sender.domain", + "from": "user2@example.com", + "size": "", + "nrcpt": "", + "messages": [ + { + "time": "2021-02-06T10:57:58+01:00", + "to": "some.dest@destdomain.com", + "status": "milter-reject", + "message": "milter-reject: END-OF-MESSAGE from unknown[99.88.77.66]: 4.7.1 Greylisting in action, try again later; from= to= proto=ESMTP helo=", + "bounce_id": "" + } + ] +} +{ + "time": "2021-02-06T09:58:59.184925+01:00", + "hostname": "mail.server.com", + "process": "postfix/submission/smtpd[99525]", + "queue_id": "2D19728491", + "client_hostname": "unknown", + "client_ip": "10.11.12.13", + "sasl_method": "PLAIN", + "sasl_username": "user1@domain.example.com", + "message_id": "1612601937133741374.16589280196177750880@user1.domain.example.com", + "from": "user1@example.com", + "size": "11991", + "nrcpt": "1", + "messages": [ + { + "time": "2021-02-06T09:58:59.560624+01:00", + "to": "destuser@destination.com", + "status": "sent", + "message": "to=, relay=mail2.domain.com[2b00:128:64::1]:25, delay=9.7, delays=9.5/0.01/0.03/0.12, dsn=2.0.0, status=sent (250 2.0.0 Ok: queued as 702A02A5BF)", + "bounce_id": "" + } + ] +} +{ + "time": "2021-02-06T11:19:18.130932+01:00", + "hostname": "mail2.server.com", + "process": "postfix/smtpd[61691]", + "queue_id": "1FEE23DED3", + "client_hostname": "unknown", + "client_ip": "2b00:128:64::42", + "sasl_method": "", + "sasl_username": "", + "message_id": "20210206101916.672CC1B4E5@sender.example.com", + "from": "", + "size": "", + "nrcpt": "", + "messages": [ + { + "time": "2021-02-06T11:19:18.189624+01:00", + "to": "dest@domain.com", + "status": "bounced", + "message": "to=, relay=relay.domain.com[22.33.44.55]:25, delay=0.06, delays=0/0/0.05/0.01, dsn=5.7.1, status=bounced (host relay.domain.com[22.33.44.55] said: 554 5.7.1 : Relay access denied (in reply to RCPT TO command))", + "bounce_id": "" + } + ] +} +{ + "time": "2021-02-06T11:19:25+01:00", + "hostname": "mail1.example.com", + "process": "postfix/smtpd[64346]", + "queue_id": "42E683DD0B", + "client_hostname": "mail.sender.com", + "client_ip": "85.42.66.4", + "sasl_method": "", + "sasl_username": "", + "message_id": "0tgs2nv56aexq9wn-dz6hijmzyfbesxxz-bcd8-493db@some.com", + "from": "18546-55426-655542-8520-recipient1=sender1@mail.some.com", + "size": "8703", + "nrcpt": "1", + "messages": [ + { + "time": "2021-02-06T11:19:26+01:00", + "to": "recipient1@destdom.com", + "status": "bounced", + "message": "to=, relay=aspmx.l.google.com[2a00:1450:400c:c04::1b]:25, delay=0.59, delays=0.23/0/0.17/0.19, dsn=5.7.26, status=bounced (host aspmx.l.google.com[2a00:1450:400c:c04::1b] said: 550-5.7.26 This message does not have authentication information or fails to 550-5.7.26 pass authentication checks. To best protect our users from spam, the 550-5.7.26 message has been blocked. Please visit 550-5.7.26 https://support.google.com/mail/answer/81126#authentication for more 550 5.7.26 information. a42si85499772wrx.219 - gsmtp (in reply to end of DATA command))", + "bounce_id": "1F99E4BC9A" + } + ] +} +{ + "time": "2021-02-20T13:18:56+01:00", + "hostname": "srv-smtp", + "process": "postfix/smtpd[63060]", + "queue_id": "9ABCD1BCA9", + "client_hostname": "mailout.domain.com", + "client_ip": "10.11.12.13", + "sasl_method": "", + "sasl_username": "", + "message_id": "20210220121856.17088.40693@internal.domain.lan", + "from": "user1@domain.com", + "size": "5712", + "nrcpt": "1", + "messages": [ + { + "time": "2021-02-20T13:18:57+01:00", + "to": "recipient@gmail.com", + "status": "deferred", + "message": "to=, relay=gmail-smtp-in.l.google.com[2a00:1450:400c:c02::1b]:25, delay=0.41, delays=0.19/0/0.19/0.03, dsn=4.2.1, status=deferred (host gmail-smtp-in.l.google.com[2a00:1450:400c:c02::1b] said: 450-4.2.1 The user you are trying to contact is receiving mail at a rate that 450-4.2.1 prevents additional messages from being delivered. Please resend your 450-4.2.1 message at a later time. If the user is able to receive mail at that 450-4.2.1 time, your message will be delivered. For more information, please 450-4.2.1 visit 450 4.2.1 https://support.google.com/mail/?p=ReceivingRate f22si54329842vxg.042 - gsmtp (in reply to RCPT TO command))", + "bounce_id": "" + } + ] +} +{ + "time": "2025-02-13T12:44:28.836533+01:00", + "hostname": "srv-smtp", + "process": "postfix/smtpd[65735]", + "queue_id": "", + "client_hostname": "unknown", + "client_ip": "41.42.43.44", + "sasl_method": "", + "sasl_username": "", + "message_id": "", + "from": "", + "size": "", + "nrcpt": "", + "messages": [ + { + "time": "2025-02-13T12:44:28.836533+01:00", + "to": "", + "status": "postfix-ratelimit", + "message": "warning: Message delivery request rate limit exceeded: 19 from unknown[41.42.43.44] for service smtp", + "bounce_id": "" + } + ] +} diff --git a/test/test-iso8601.log.146.out b/test/test-iso8601.log.146.out deleted file mode 100644 index 755aa3c..0000000 --- a/test/test-iso8601.log.146.out +++ /dev/null @@ -1,9 +0,0 @@ -{"time":"2020-01-30T06:26:06.404714+09:00","hostname":"mail","process":"postfix/smtpd[1827]","queue_id":"3D74ADB7400B","client_hostname":"example.com","client_ip":"127.0.0.1","sasl_method":"","sasl_username":"","message_id":"f93388828093534f92d85ffe21b2a719@example.info","from":"test2@example.info","size":"2140","nrcpt":"1","messages":[{"time":"2020-01-30T06:26:07.404714+09:00","to":"test@example.to","status":"sent","message":"to=\u003ctest@example.to\u003e, relay=example.to[192.168.0.20]:25, delay=1.7, delays=0.02/0/1.7/0.06, dsn=2.0.0, status=sent (250 [Sniper] OK 1539154772 snipe-queue 10549)","bounce_id":""},{"time":"2020-01-30T06:26:07.404714+09:00","to":"test2@example.to","status":"sent","message":"to=\u003ctest2@example.to\u003e, relay=example.to[192.168.0.20]:25, delay=1.7, delays=0.02/0/1.7/0.06, dsn=2.0.0, status=sent (250 [Sniper] OK 1539154772 snipe-queue 10549)","bounce_id":""}]} -{"time":"2020-01-30T06:26:08.404714+09:00","hostname":"mail","process":"postfix/smtpd[1830]","queue_id":"6526CDB7400B","client_hostname":"example.com","client_ip":"127.0.0.1","sasl_method":"","sasl_username":"","message_id":"153915476795520900002087@example.aaa","from":"test@example.bbb","size":"4118","nrcpt":"1","messages":[{"time":"2020-01-30T06:26:08.404714+09:00","to":"test@example.ccc","status":"sent","message":"to=\u003ctest@example.ccc\u003e, relay=example.ccc[192.168.0.10]:25, delay=0.1, delays=0.02/0/0.03/0.05, dsn=2.0.0, status=sent (250 2.0.0 Ok: queued as 71111424269E)","bounce_id":""}]} -{"time":"2020-01-30T06:26:05.404714+09:00","hostname":"mail","process":"postfix/smtpd[1830]","queue_id":"C6E0DDB74006","client_hostname":"example.com","client_ip":"127.0.0.1","sasl_method":"","sasl_username":"","message_id":"A40CF64D-7F2D-42E4-8A76-CBFFF64A6EB1@example.com","from":"test@example.com","size":"309891","nrcpt":"1","messages":[{"time":"2020-01-30T06:26:09.404714+09:00","to":"test@example.ddd","status":"sent","message":"to=\u003ctest@example.ddd\u003e, relay=example.ddd[192.168.0.30]:25, delay=3.4, delays=0.11/0/0.38/2.9, dsn=2.0.0, status=sent (250 2.0.0 OK 1539154772 az9-v6si5976496plb.190 - gsmtp)","bounce_id":""}]} -{"time":"2021-02-06T10:57:57+01:00","hostname":"mail.server.com","process":"postfix/smtpd[59633]","queue_id":"D6DDA23B0F","client_hostname":"unknown","client_ip":"99.88.77.66","sasl_method":"","sasl_username":"","message_id":"20210206095814.2B34E19A19F6@host.sender.domain","from":"user2@example.com","size":"","nrcpt":"","messages":[{"time":"2021-02-06T10:57:58+01:00","to":"some.dest@destdomain.com","status":"milter-reject","message":"milter-reject: END-OF-MESSAGE from unknown[99.88.77.66]: 4.7.1 Greylisting in action, try again later; from=\u003cuser2@example.com\u003e to=\u003csome.dest@destdomain.com\u003e proto=ESMTP helo=\u003chost.sender.domain\u003e","bounce_id":""}]} -{"time":"2021-02-06T09:58:59.184925+01:00","hostname":"mail.server.com","process":"postfix/submission/smtpd[99525]","queue_id":"2D19728491","client_hostname":"unknown","client_ip":"10.11.12.13","sasl_method":"PLAIN","sasl_username":"user1@domain.example.com","message_id":"1612601937133741374.16589280196177750880@user1.domain.example.com","from":"user1@example.com","size":"11991","nrcpt":"1","messages":[{"time":"2021-02-06T09:58:59.560624+01:00","to":"destuser@destination.com","status":"sent","message":"to=\u003cdestuser@destination.com\u003e, relay=mail2.domain.com[2b00:128:64::1]:25, delay=9.7, delays=9.5/0.01/0.03/0.12, dsn=2.0.0, status=sent (250 2.0.0 Ok: queued as 702A02A5BF)","bounce_id":""}]} -{"time":"2021-02-06T11:19:18.130932+01:00","hostname":"mail2.server.com","process":"postfix/smtpd[61691]","queue_id":"1FEE23DED3","client_hostname":"unknown","client_ip":"2b00:128:64::42","sasl_method":"","sasl_username":"","message_id":"20210206101916.672CC1B4E5@sender.example.com","from":"","size":"","nrcpt":"","messages":[{"time":"2021-02-06T11:19:18.189624+01:00","to":"dest@domain.com","status":"bounced","message":"to=\u003cdest@domain.com\u003e, relay=relay.domain.com[22.33.44.55]:25, delay=0.06, delays=0/0/0.05/0.01, dsn=5.7.1, status=bounced (host relay.domain.com[22.33.44.55] said: 554 5.7.1 \u003cdest@domain.com\u003e: Relay access denied (in reply to RCPT TO command))","bounce_id":""}]} -{"time":"2021-02-06T11:19:25+01:00","hostname":"mail1.example.com","process":"postfix/smtpd[64346]","queue_id":"42E683DD0B","client_hostname":"mail.sender.com","client_ip":"85.42.66.4","sasl_method":"","sasl_username":"","message_id":"0tgs2nv56aexq9wn-dz6hijmzyfbesxxz-bcd8-493db@some.com","from":"18546-55426-655542-8520-recipient1=sender1@mail.some.com","size":"8703","nrcpt":"1","messages":[{"time":"2021-02-06T11:19:26+01:00","to":"recipient1@destdom.com","status":"bounced","message":"to=\u003crecipient1@destdom.com\u003e, relay=aspmx.l.google.com[2a00:1450:400c:c04::1b]:25, delay=0.59, delays=0.23/0/0.17/0.19, dsn=5.7.26, status=bounced (host aspmx.l.google.com[2a00:1450:400c:c04::1b] said: 550-5.7.26 This message does not have authentication information or fails to 550-5.7.26 pass authentication checks. To best protect our users from spam, the 550-5.7.26 message has been blocked. Please visit 550-5.7.26 https://support.google.com/mail/answer/81126#authentication for more 550 5.7.26 information. a42si85499772wrx.219 - gsmtp (in reply to end of DATA command))","bounce_id":"1F99E4BC9A"}]} -{"time":"2021-02-20T13:18:56+01:00","hostname":"srv-smtp","process":"postfix/smtpd[63060]","queue_id":"9ABCD1BCA9","client_hostname":"mailout.domain.com","client_ip":"10.11.12.13","sasl_method":"","sasl_username":"","message_id":"20210220121856.17088.40693@internal.domain.lan","from":"user1@domain.com","size":"5712","nrcpt":"1","messages":[{"time":"2021-02-20T13:18:57+01:00","to":"recipient@gmail.com","status":"deferred","message":"to=\u003crecipient@gmail.com\u003e, relay=gmail-smtp-in.l.google.com[2a00:1450:400c:c02::1b]:25, delay=0.41, delays=0.19/0/0.19/0.03, dsn=4.2.1, status=deferred (host gmail-smtp-in.l.google.com[2a00:1450:400c:c02::1b] said: 450-4.2.1 The user you are trying to contact is receiving mail at a rate that 450-4.2.1 prevents additional messages from being delivered. Please resend your 450-4.2.1 message at a later time. If the user is able to receive mail at that 450-4.2.1 time, your message will be delivered. For more information, please 450-4.2.1 visit 450 4.2.1 https://support.google.com/mail/?p=ReceivingRate f22si54329842vxg.042 - gsmtp (in reply to RCPT TO command))","bounce_id":""}]} -{"time":"2025-02-13T12:44:28.836533+01:00","hostname":"srv-smtp","process":"postfix/smtpd[65735]","queue_id":"","client_hostname":"unknown","client_ip":"41.42.43.44","sasl_method":"","sasl_username":"","message_id":"","from":"","size":"","nrcpt":"","messages":[{"time":"2025-02-13T12:44:28.836533+01:00","to":"","status":"postfix-ratelimit","message":"warning: Message delivery request rate limit exceeded: 19 from unknown[41.42.43.44] for service smtp","bounce_id":""}]} diff --git a/test/test-iso8601.log.147.json b/test/test-iso8601.log.147.json new file mode 100644 index 0000000..c8d142b --- /dev/null +++ b/test/test-iso8601.log.147.json @@ -0,0 +1,258 @@ +{ + "time": "2020-01-30T06:26:06.404714+09:00", + "hostname": "mail", + "process": "postfix/smtpd[1827]", + "queue_id": "3D74ADB7400B", + "client_hostname": "example.com", + "client_ip": "127.0.0.1", + "sasl_method": "", + "sasl_username": "", + "message_id": "f93388828093534f92d85ffe21b2a719@example.info", + "from": "test2@example.info", + "size": "2140", + "nrcpt": "2", + "messages": [ + { + "time": "2020-01-30T06:26:07.404714+09:00", + "to": "test@example.to", + "status": "sent", + "message": "to=, relay=example.to[192.168.0.20]:25, delay=1.7, delays=0.02/0/1.7/0.06, dsn=2.0.0, status=sent (250 [Sniper] OK 1539154772 snipe-queue 10549)", + "bounce_id": "" + }, + { + "time": "2020-01-30T06:26:07.404714+09:00", + "to": "test2@example.to", + "status": "sent", + "message": "to=, relay=example.to[192.168.0.20]:25, delay=1.7, delays=0.02/0/1.7/0.06, dsn=2.0.0, status=sent (250 [Sniper] OK 1539154772 snipe-queue 10549)", + "bounce_id": "" + } + ] +} +{ + "time": "2020-01-30T06:26:08.404714+09:00", + "hostname": "mail", + "process": "postfix/smtpd[1830]", + "queue_id": "6526CDB7400B", + "client_hostname": "example.com", + "client_ip": "127.0.0.1", + "sasl_method": "", + "sasl_username": "", + "message_id": "153915476795520900002087@example.aaa", + "from": "test@example.bbb", + "size": "4118", + "nrcpt": "1", + "messages": [ + { + "time": "2020-01-30T06:26:08.404714+09:00", + "to": "test@example.ccc", + "status": "sent", + "message": "to=, relay=example.ccc[192.168.0.10]:25, delay=0.1, delays=0.02/0/0.03/0.05, dsn=2.0.0, status=sent (250 2.0.0 Ok: queued as 71111424269E)", + "bounce_id": "" + } + ] +} +{ + "time": "2020-01-30T06:26:05.404714+09:00", + "hostname": "mail", + "process": "postfix/smtpd[1830]", + "queue_id": "C6E0DDB74006", + "client_hostname": "example.com", + "client_ip": "127.0.0.1", + "sasl_method": "", + "sasl_username": "", + "message_id": "A40CF64D-7F2D-42E4-8A76-CBFFF64A6EB1@example.com", + "from": "test@example.com", + "size": "309891", + "nrcpt": "1", + "messages": [ + { + "time": "2020-01-30T06:26:09.404714+09:00", + "to": "test@example.ddd", + "status": "sent", + "message": "to=, relay=example.ddd[192.168.0.30]:25, delay=3.4, delays=0.11/0/0.38/2.9, dsn=2.0.0, status=sent (250 2.0.0 OK 1539154772 az9-v6si5976496plb.190 - gsmtp)", + "bounce_id": "" + } + ] +} +{ + "time": "2021-02-06T10:57:57+01:00", + "hostname": "mail.server.com", + "process": "postfix/smtpd[59633]", + "queue_id": "D6DDA23B0F", + "client_hostname": "unknown", + "client_ip": "99.88.77.66", + "sasl_method": "", + "sasl_username": "", + "message_id": "20210206095814.2B34E19A19F6@host.sender.domain", + "from": "user2@example.com", + "size": "", + "nrcpt": "1", + "messages": [ + { + "time": "2021-02-06T10:57:58+01:00", + "to": "some.dest@destdomain.com", + "status": "milter-reject", + "message": "milter-reject: END-OF-MESSAGE from unknown[99.88.77.66]: 4.7.1 Greylisting in action, try again later; from= to= proto=ESMTP helo=", + "bounce_id": "" + } + ] +} +{ + "time": "2021-02-06T09:58:59.184925+01:00", + "hostname": "mail.server.com", + "process": "postfix/submission/smtpd[99525]", + "queue_id": "2D19728491", + "client_hostname": "unknown", + "client_ip": "10.11.12.13", + "sasl_method": "PLAIN", + "sasl_username": "user1@domain.example.com", + "message_id": "1612601937133741374.16589280196177750880@user1.domain.example.com", + "from": "user1@example.com", + "size": "11991", + "nrcpt": "1", + "messages": [ + { + "time": "2021-02-06T09:58:59.560624+01:00", + "to": "destuser@destination.com", + "status": "sent", + "message": "to=, relay=mail2.domain.com[2b00:128:64::1]:25, delay=9.7, delays=9.5/0.01/0.03/0.12, dsn=2.0.0, status=sent (250 2.0.0 Ok: queued as 702A02A5BF)", + "bounce_id": "" + } + ] +} +{ + "time": "2021-02-06T11:19:18.130932+01:00", + "hostname": "mail2.server.com", + "process": "postfix/smtpd[61691]", + "queue_id": "1FEE23DED3", + "client_hostname": "unknown", + "client_ip": "2b00:128:64::42", + "sasl_method": "", + "sasl_username": "", + "message_id": "20210206101916.672CC1B4E5@sender.example.com", + "from": "", + "size": "", + "nrcpt": "1", + "messages": [ + { + "time": "2021-02-06T11:19:18.189624+01:00", + "to": "dest@domain.com", + "status": "bounced", + "message": "to=, relay=relay.domain.com[22.33.44.55]:25, delay=0.06, delays=0/0/0.05/0.01, dsn=5.7.1, status=bounced (host relay.domain.com[22.33.44.55] said: 554 5.7.1 : Relay access denied (in reply to RCPT TO command))", + "bounce_id": "" + } + ] +} +{ + "time": "2021-02-06T11:19:25+01:00", + "hostname": "mail1.example.com", + "process": "postfix/smtpd[64346]", + "queue_id": "42E683DD0B", + "client_hostname": "mail.sender.com", + "client_ip": "85.42.66.4", + "sasl_method": "", + "sasl_username": "", + "message_id": "0tgs2nv56aexq9wn-dz6hijmzyfbesxxz-bcd8-493db@some.com", + "from": "18546-55426-655542-8520-recipient1=sender1@mail.some.com", + "size": "8703", + "nrcpt": "1", + "messages": [ + { + "time": "2021-02-06T11:19:26+01:00", + "to": "recipient1@destdom.com", + "status": "bounced", + "message": "to=, relay=aspmx.l.google.com[2a00:1450:400c:c04::1b]:25, delay=0.59, delays=0.23/0/0.17/0.19, dsn=5.7.26, status=bounced (host aspmx.l.google.com[2a00:1450:400c:c04::1b] said: 550-5.7.26 This message does not have authentication information or fails to 550-5.7.26 pass authentication checks. To best protect our users from spam, the 550-5.7.26 message has been blocked. Please visit 550-5.7.26 https://support.google.com/mail/answer/81126#authentication for more 550 5.7.26 information. a42si85499772wrx.219 - gsmtp (in reply to end of DATA command))", + "bounce_id": "1F99E4BC9A" + } + ] +} +{ + "time": "2021-02-20T13:18:56+01:00", + "hostname": "srv-smtp", + "process": "postfix/smtpd[63060]", + "queue_id": "9ABCD1BCA9", + "client_hostname": "mailout.domain.com", + "client_ip": "10.11.12.13", + "sasl_method": "", + "sasl_username": "", + "message_id": "20210220121856.17088.40693@internal.domain.lan", + "from": "user1@domain.com", + "size": "5712", + "nrcpt": "1", + "messages": [ + { + "time": "2021-02-20T13:18:57+01:00", + "to": "recipient@gmail.com", + "status": "deferred", + "message": "to=, relay=gmail-smtp-in.l.google.com[2a00:1450:400c:c02::1b]:25, delay=0.41, delays=0.19/0/0.19/0.03, dsn=4.2.1, status=deferred (host gmail-smtp-in.l.google.com[2a00:1450:400c:c02::1b] said: 450-4.2.1 The user you are trying to contact is receiving mail at a rate that 450-4.2.1 prevents additional messages from being delivered. Please resend your 450-4.2.1 message at a later time. If the user is able to receive mail at that 450-4.2.1 time, your message will be delivered. For more information, please 450-4.2.1 visit 450 4.2.1 https://support.google.com/mail/?p=ReceivingRate f22si54329842vxg.042 - gsmtp (in reply to RCPT TO command))", + "bounce_id": "" + } + ] +} +{ + "time": "2025-02-13T12:44:28.836533+01:00", + "hostname": "srv-smtp", + "process": "postfix/smtpd[65735]", + "queue_id": "", + "client_hostname": "unknown", + "client_ip": "41.42.43.44", + "sasl_method": "", + "sasl_username": "", + "message_id": "", + "from": "", + "size": "", + "nrcpt": "", + "messages": [ + { + "time": "2025-02-13T12:44:28.836533+01:00", + "to": "", + "status": "postfix-ratelimit", + "message": "warning: Message delivery request rate limit exceeded: 19 from unknown[41.42.43.44] for service smtp", + "bounce_id": "" + } + ] +} +{ + "time": "2025-03-15T13:30:30.342887+01:00", + "hostname": "smtp.example.org", + "process": "postfix/smtpd[17507]", + "queue_id": "53AEC4F144", + "client_hostname": "unknown", + "client_ip": "192.168.0.22", + "sasl_method": "LOGIN", + "sasl_username": "smtpuser@smtp.example.org", + "message_id": "94851c4c5f9b5ef98755296606bf32ab@example.org", + "from": "sender@example.org", + "size": "", + "nrcpt": "4", + "messages": [ + { + "time": "2025-03-15T13:30:31.206043+01:00", + "to": "recipient1@domain1.net", + "status": "milter-reject", + "message": "milter-reject: END-OF-MESSAGE from unknown[192.168.0.22]: 4.7.1 Ratelimit \"to_ip_from\" exceeded; from= to= proto=ESMTP helo=", + "bounce_id": "" + }, + { + "time": "2025-03-15T13:30:31.206043+01:00", + "to": "recipient2@foreign.net", + "status": "milter-reject", + "message": "milter-reject: END-OF-MESSAGE from unknown[192.168.0.22]: 4.7.1 Ratelimit \"to_ip_from\" exceeded; from= to= proto=ESMTP helo=", + "bounce_id": "" + }, + { + "time": "2025-03-15T13:30:31.206043+01:00", + "to": "recipient3@somewhere.in", + "status": "milter-reject", + "message": "milter-reject: END-OF-MESSAGE from unknown[192.168.0.22]: 4.7.1 Ratelimit \"to_ip_from\" exceeded; from= to= proto=ESMTP helo=", + "bounce_id": "" + }, + { + "time": "2025-03-15T13:30:31.206043+01:00", + "to": "recipient4@anotherdomain.com", + "status": "milter-reject", + "message": "milter-reject: END-OF-MESSAGE from unknown[192.168.0.22]: 4.7.1 Ratelimit \"to_ip_from\" exceeded; from= to= proto=ESMTP helo=", + "bounce_id": "" + } + ] +} diff --git a/test/test-milter.142.json b/test/test-milter.142.json new file mode 100644 index 0000000..79ef6b8 --- /dev/null +++ b/test/test-milter.142.json @@ -0,0 +1,46 @@ +{ + "time": "2021-02-19T14:32:14.976132+01:00", + "hostname": "smtp01.example.org", + "process": "postfix/cleanup[52106]", + "queue_id": "E89FA2DEDA", + "client_hostname": "srv05.source.com", + "client_ip": "10.11.12.13", + "sasl_method": "", + "sasl_username": "", + "message_id": "", + "from": "", + "size": "", + "nrcpt": "", + "messages": [ + { + "time": "2021-02-19T14:32:14.976132+01:00", + "to": "this_user@this.domain.com", + "status": "milter-hold", + "message": "milter-hold: END-OF-MESSAGE from srv05.source.com[10.11.12.13]: milter triggers HOLD action; from=<> to= proto=ESMTP helo=", + "bounce_id": "" + } + ] +} +{ + "time": "2021-12-03T06:27:33.887389+01:00", + "hostname": "smtp01.example.org", + "process": "postfix/smtpd[50419]", + "queue_id": "B42E77AB32", + "client_hostname": "42-64-53-170.subs.proxad.net", + "client_ip": "42.64.53.170", + "sasl_method": "PLAIN", + "sasl_username": "user42@smtp01.example.com", + "message_id": "164017759823.25920.10144256874843932242@12f2bb757669", + "from": "youssefh@senderdomain.com", + "size": "", + "nrcpt": "", + "messages": [ + { + "time": "2021-12-03T06:27:34.105536+01:00", + "to": "archive@senderdomain.com", + "status": "milter-reject", + "message": "milter-reject: END-OF-MESSAGE from 42-64-53-170.subs.proxad.net[42.64.53.170]: 4.7.1 Ratelimit \"05\" exceeded; from= to= proto=ESMTP helo=<[172.18.0.2]>", + "bounce_id": "" + } + ] +} diff --git a/test/test-milter.146.json b/test/test-milter.146.json new file mode 100644 index 0000000..79ef6b8 --- /dev/null +++ b/test/test-milter.146.json @@ -0,0 +1,46 @@ +{ + "time": "2021-02-19T14:32:14.976132+01:00", + "hostname": "smtp01.example.org", + "process": "postfix/cleanup[52106]", + "queue_id": "E89FA2DEDA", + "client_hostname": "srv05.source.com", + "client_ip": "10.11.12.13", + "sasl_method": "", + "sasl_username": "", + "message_id": "", + "from": "", + "size": "", + "nrcpt": "", + "messages": [ + { + "time": "2021-02-19T14:32:14.976132+01:00", + "to": "this_user@this.domain.com", + "status": "milter-hold", + "message": "milter-hold: END-OF-MESSAGE from srv05.source.com[10.11.12.13]: milter triggers HOLD action; from=<> to= proto=ESMTP helo=", + "bounce_id": "" + } + ] +} +{ + "time": "2021-12-03T06:27:33.887389+01:00", + "hostname": "smtp01.example.org", + "process": "postfix/smtpd[50419]", + "queue_id": "B42E77AB32", + "client_hostname": "42-64-53-170.subs.proxad.net", + "client_ip": "42.64.53.170", + "sasl_method": "PLAIN", + "sasl_username": "user42@smtp01.example.com", + "message_id": "164017759823.25920.10144256874843932242@12f2bb757669", + "from": "youssefh@senderdomain.com", + "size": "", + "nrcpt": "", + "messages": [ + { + "time": "2021-12-03T06:27:34.105536+01:00", + "to": "archive@senderdomain.com", + "status": "milter-reject", + "message": "milter-reject: END-OF-MESSAGE from 42-64-53-170.subs.proxad.net[42.64.53.170]: 4.7.1 Ratelimit \"05\" exceeded; from= to= proto=ESMTP helo=<[172.18.0.2]>", + "bounce_id": "" + } + ] +} diff --git a/test/test-milter.146.out b/test/test-milter.146.out deleted file mode 100644 index 0b7913a..0000000 --- a/test/test-milter.146.out +++ /dev/null @@ -1,2 +0,0 @@ -{"time":"2021-02-19T14:32:14.976132+01:00","hostname":"smtp01.example.org","process":"postfix/cleanup[52106]","queue_id":"E89FA2DEDA","client_hostname":"srv05.source.com","client_ip":"10.11.12.13","sasl_method":"","sasl_username":"","message_id":"","from":"","size":"","nrcpt":"","messages":[{"time":"2021-02-19T14:32:14.976132+01:00","to":"this_user@this.domain.com","status":"milter-hold","message":"milter-hold: END-OF-MESSAGE from srv05.source.com[10.11.12.13]: milter triggers HOLD action; from=\u003c\u003e to=\u003cthis_user@this.domain.com\u003e proto=ESMTP helo=\u003cthis.domain.com\u003e","bounce_id":""}]} -{"time":"2021-12-03T06:27:33.887389+01:00","hostname":"smtp01.example.org","process":"postfix/smtpd[50419]","queue_id":"B42E77AB32","client_hostname":"42-64-53-170.subs.proxad.net","client_ip":"42.64.53.170","sasl_method":"PLAIN","sasl_username":"user42@smtp01.example.com","message_id":"164017759823.25920.10144256874843932242@12f2bb757669","from":"youssefh@senderdomain.com","size":"","nrcpt":"","messages":[{"time":"2021-12-03T06:27:34.105536+01:00","to":"archive@senderdomain.com","status":"milter-reject","message":"milter-reject: END-OF-MESSAGE from 42-64-53-170.subs.proxad.net[42.64.53.170]: 4.7.1 Ratelimit \"05\" exceeded; from=\u003cyoussefh@senderdomain.com\u003e to=\u003carchive@senderdomain.com\u003e proto=ESMTP helo=\u003c[172.18.0.2]\u003e","bounce_id":""}]} diff --git a/test/test-milter.147.json b/test/test-milter.147.json new file mode 100644 index 0000000..f5dbea4 --- /dev/null +++ b/test/test-milter.147.json @@ -0,0 +1,46 @@ +{ + "time": "2021-02-19T14:32:14.976132+01:00", + "hostname": "smtp01.example.org", + "process": "postfix/cleanup[52106]", + "queue_id": "E89FA2DEDA", + "client_hostname": "srv05.source.com", + "client_ip": "10.11.12.13", + "sasl_method": "", + "sasl_username": "", + "message_id": "", + "from": "", + "size": "", + "nrcpt": "1", + "messages": [ + { + "time": "2021-02-19T14:32:14.976132+01:00", + "to": "this_user@this.domain.com", + "status": "milter-hold", + "message": "milter-hold: END-OF-MESSAGE from srv05.source.com[10.11.12.13]: milter triggers HOLD action; from=<> to= proto=ESMTP helo=", + "bounce_id": "" + } + ] +} +{ + "time": "2021-12-03T06:27:33.887389+01:00", + "hostname": "smtp01.example.org", + "process": "postfix/smtpd[50419]", + "queue_id": "B42E77AB32", + "client_hostname": "42-64-53-170.subs.proxad.net", + "client_ip": "42.64.53.170", + "sasl_method": "PLAIN", + "sasl_username": "user42@smtp01.example.com", + "message_id": "164017759823.25920.10144256874843932242@12f2bb757669", + "from": "youssefh@senderdomain.com", + "size": "", + "nrcpt": "1", + "messages": [ + { + "time": "2021-12-03T06:27:34.105536+01:00", + "to": "archive@senderdomain.com", + "status": "milter-reject", + "message": "milter-reject: END-OF-MESSAGE from 42-64-53-170.subs.proxad.net[42.64.53.170]: 4.7.1 Ratelimit \"05\" exceeded; from= to= proto=ESMTP helo=<[172.18.0.2]>", + "bounce_id": "" + } + ] +} diff --git a/test/test.142.json b/test/test.142.json new file mode 100644 index 0000000..8044519 --- /dev/null +++ b/test/test.142.json @@ -0,0 +1,76 @@ +{ + "time": "0000-10-10T15:59:29+00:09", + "hostname": "mail", + "process": "postfix/smtpd[1827]", + "queue_id": "3D74ADB7400B", + "client_hostname": "example.com", + "client_ip": "127.0.0.1", + "sasl_method": "", + "sasl_username": "", + "message_id": "f93388828093534f92d85ffe21b2a719@example.info", + "from": "test2@example.info", + "size": "2140", + "nrcpt": "1", + "messages": [ + { + "time": "0000-10-10T15:59:30+00:09", + "to": "test@example.to", + "status": "sent", + "message": "to=, relay=example.to[192.168.0.20]:25, delay=1.7, delays=0.02/0/1.7/0.06, dsn=2.0.0, status=sent (250 [Sniper] OK 1539154772 snipe-queue 10549)", + "bounce_id": "" + }, + { + "time": "0000-10-10T15:59:30+00:09", + "to": "test2@example.to", + "status": "sent", + "message": "to=, relay=example.to[192.168.0.20]:25, delay=1.7, delays=0.02/0/1.7/0.06, dsn=2.0.0, status=sent (250 [Sniper] OK 1539154772 snipe-queue 10549)", + "bounce_id": "" + } + ] +} +{ + "time": "0000-10-10T15:59:31+00:09", + "hostname": "mail", + "process": "postfix/smtpd[1830]", + "queue_id": "6526CDB7400B", + "client_hostname": "example.com", + "client_ip": "127.0.0.1", + "sasl_method": "", + "sasl_username": "", + "message_id": "153915476795520900002087@example.aaa", + "from": "test@example.bbb", + "size": "4118", + "nrcpt": "1", + "messages": [ + { + "time": "0000-10-10T15:59:31+00:09", + "to": "test@example.ccc", + "status": "sent", + "message": "to=, relay=example.ccc[192.168.0.10]:25, delay=0.1, delays=0.02/0/0.03/0.05, dsn=2.0.0, status=sent (250 2.0.0 Ok: queued as 71111424269E)", + "bounce_id": "" + } + ] +} +{ + "time": "0000-10-10T15:59:28+00:09", + "hostname": "mail", + "process": "postfix/smtpd[1830]", + "queue_id": "C6E0DDB74006", + "client_hostname": "example.com", + "client_ip": "127.0.0.1", + "sasl_method": "", + "sasl_username": "", + "message_id": "A40CF64D-7F2D-42E4-8A76-CBFFF64A6EB1@example.com", + "from": "test@example.com", + "size": "309891", + "nrcpt": "1", + "messages": [ + { + "time": "0000-10-10T15:59:32+00:09", + "to": "test@example.ddd", + "status": "sent", + "message": "to=, relay=example.ddd[192.168.0.30]:25, delay=3.4, delays=0.11/0/0.38/2.9, dsn=2.0.0, status=sent (250 2.0.0 OK 1539154772 az9-v6si5976496plb.190 - gsmtp)", + "bounce_id": "" + } + ] +} diff --git a/test/test.146.json b/test/test.146.json new file mode 100644 index 0000000..8044519 --- /dev/null +++ b/test/test.146.json @@ -0,0 +1,76 @@ +{ + "time": "0000-10-10T15:59:29+00:09", + "hostname": "mail", + "process": "postfix/smtpd[1827]", + "queue_id": "3D74ADB7400B", + "client_hostname": "example.com", + "client_ip": "127.0.0.1", + "sasl_method": "", + "sasl_username": "", + "message_id": "f93388828093534f92d85ffe21b2a719@example.info", + "from": "test2@example.info", + "size": "2140", + "nrcpt": "1", + "messages": [ + { + "time": "0000-10-10T15:59:30+00:09", + "to": "test@example.to", + "status": "sent", + "message": "to=, relay=example.to[192.168.0.20]:25, delay=1.7, delays=0.02/0/1.7/0.06, dsn=2.0.0, status=sent (250 [Sniper] OK 1539154772 snipe-queue 10549)", + "bounce_id": "" + }, + { + "time": "0000-10-10T15:59:30+00:09", + "to": "test2@example.to", + "status": "sent", + "message": "to=, relay=example.to[192.168.0.20]:25, delay=1.7, delays=0.02/0/1.7/0.06, dsn=2.0.0, status=sent (250 [Sniper] OK 1539154772 snipe-queue 10549)", + "bounce_id": "" + } + ] +} +{ + "time": "0000-10-10T15:59:31+00:09", + "hostname": "mail", + "process": "postfix/smtpd[1830]", + "queue_id": "6526CDB7400B", + "client_hostname": "example.com", + "client_ip": "127.0.0.1", + "sasl_method": "", + "sasl_username": "", + "message_id": "153915476795520900002087@example.aaa", + "from": "test@example.bbb", + "size": "4118", + "nrcpt": "1", + "messages": [ + { + "time": "0000-10-10T15:59:31+00:09", + "to": "test@example.ccc", + "status": "sent", + "message": "to=, relay=example.ccc[192.168.0.10]:25, delay=0.1, delays=0.02/0/0.03/0.05, dsn=2.0.0, status=sent (250 2.0.0 Ok: queued as 71111424269E)", + "bounce_id": "" + } + ] +} +{ + "time": "0000-10-10T15:59:28+00:09", + "hostname": "mail", + "process": "postfix/smtpd[1830]", + "queue_id": "C6E0DDB74006", + "client_hostname": "example.com", + "client_ip": "127.0.0.1", + "sasl_method": "", + "sasl_username": "", + "message_id": "A40CF64D-7F2D-42E4-8A76-CBFFF64A6EB1@example.com", + "from": "test@example.com", + "size": "309891", + "nrcpt": "1", + "messages": [ + { + "time": "0000-10-10T15:59:32+00:09", + "to": "test@example.ddd", + "status": "sent", + "message": "to=, relay=example.ddd[192.168.0.30]:25, delay=3.4, delays=0.11/0/0.38/2.9, dsn=2.0.0, status=sent (250 2.0.0 OK 1539154772 az9-v6si5976496plb.190 - gsmtp)", + "bounce_id": "" + } + ] +} diff --git a/test/test.146.out b/test/test.146.out deleted file mode 100644 index b8d64c0..0000000 --- a/test/test.146.out +++ /dev/null @@ -1,3 +0,0 @@ -{"time":"0000-10-10T15:59:29+00:09","hostname":"mail","process":"postfix/smtpd[1827]","queue_id":"3D74ADB7400B","client_hostname":"example.com","client_ip":"127.0.0.1","sasl_method":"","sasl_username":"","message_id":"f93388828093534f92d85ffe21b2a719@example.info","from":"test2@example.info","size":"2140","nrcpt":"1","messages":[{"time":"0000-10-10T15:59:30+00:09","to":"test@example.to","status":"sent","message":"to=\u003ctest@example.to\u003e, relay=example.to[192.168.0.20]:25, delay=1.7, delays=0.02/0/1.7/0.06, dsn=2.0.0, status=sent (250 [Sniper] OK 1539154772 snipe-queue 10549)","bounce_id":""},{"time":"0000-10-10T15:59:30+00:09","to":"test2@example.to","status":"sent","message":"to=\u003ctest2@example.to\u003e, relay=example.to[192.168.0.20]:25, delay=1.7, delays=0.02/0/1.7/0.06, dsn=2.0.0, status=sent (250 [Sniper] OK 1539154772 snipe-queue 10549)","bounce_id":""}]} -{"time":"0000-10-10T15:59:31+00:09","hostname":"mail","process":"postfix/smtpd[1830]","queue_id":"6526CDB7400B","client_hostname":"example.com","client_ip":"127.0.0.1","sasl_method":"","sasl_username":"","message_id":"153915476795520900002087@example.aaa","from":"test@example.bbb","size":"4118","nrcpt":"1","messages":[{"time":"0000-10-10T15:59:31+00:09","to":"test@example.ccc","status":"sent","message":"to=\u003ctest@example.ccc\u003e, relay=example.ccc[192.168.0.10]:25, delay=0.1, delays=0.02/0/0.03/0.05, dsn=2.0.0, status=sent (250 2.0.0 Ok: queued as 71111424269E)","bounce_id":""}]} -{"time":"0000-10-10T15:59:28+00:09","hostname":"mail","process":"postfix/smtpd[1830]","queue_id":"C6E0DDB74006","client_hostname":"example.com","client_ip":"127.0.0.1","sasl_method":"","sasl_username":"","message_id":"A40CF64D-7F2D-42E4-8A76-CBFFF64A6EB1@example.com","from":"test@example.com","size":"309891","nrcpt":"1","messages":[{"time":"0000-10-10T15:59:32+00:09","to":"test@example.ddd","status":"sent","message":"to=\u003ctest@example.ddd\u003e, relay=example.ddd[192.168.0.30]:25, delay=3.4, delays=0.11/0/0.38/2.9, dsn=2.0.0, status=sent (250 2.0.0 OK 1539154772 az9-v6si5976496plb.190 - gsmtp)","bounce_id":""}]} diff --git a/test/test.147.json b/test/test.147.json new file mode 100644 index 0000000..e811faa --- /dev/null +++ b/test/test.147.json @@ -0,0 +1,76 @@ +{ + "time": "0000-10-10T15:59:29+00:09", + "hostname": "mail", + "process": "postfix/smtpd[1827]", + "queue_id": "3D74ADB7400B", + "client_hostname": "example.com", + "client_ip": "127.0.0.1", + "sasl_method": "", + "sasl_username": "", + "message_id": "f93388828093534f92d85ffe21b2a719@example.info", + "from": "test2@example.info", + "size": "2140", + "nrcpt": "2", + "messages": [ + { + "time": "0000-10-10T15:59:30+00:09", + "to": "test@example.to", + "status": "sent", + "message": "to=, relay=example.to[192.168.0.20]:25, delay=1.7, delays=0.02/0/1.7/0.06, dsn=2.0.0, status=sent (250 [Sniper] OK 1539154772 snipe-queue 10549)", + "bounce_id": "" + }, + { + "time": "0000-10-10T15:59:30+00:09", + "to": "test2@example.to", + "status": "sent", + "message": "to=, relay=example.to[192.168.0.20]:25, delay=1.7, delays=0.02/0/1.7/0.06, dsn=2.0.0, status=sent (250 [Sniper] OK 1539154772 snipe-queue 10549)", + "bounce_id": "" + } + ] +} +{ + "time": "0000-10-10T15:59:31+00:09", + "hostname": "mail", + "process": "postfix/smtpd[1830]", + "queue_id": "6526CDB7400B", + "client_hostname": "example.com", + "client_ip": "127.0.0.1", + "sasl_method": "", + "sasl_username": "", + "message_id": "153915476795520900002087@example.aaa", + "from": "test@example.bbb", + "size": "4118", + "nrcpt": "1", + "messages": [ + { + "time": "0000-10-10T15:59:31+00:09", + "to": "test@example.ccc", + "status": "sent", + "message": "to=, relay=example.ccc[192.168.0.10]:25, delay=0.1, delays=0.02/0/0.03/0.05, dsn=2.0.0, status=sent (250 2.0.0 Ok: queued as 71111424269E)", + "bounce_id": "" + } + ] +} +{ + "time": "0000-10-10T15:59:28+00:09", + "hostname": "mail", + "process": "postfix/smtpd[1830]", + "queue_id": "C6E0DDB74006", + "client_hostname": "example.com", + "client_ip": "127.0.0.1", + "sasl_method": "", + "sasl_username": "", + "message_id": "A40CF64D-7F2D-42E4-8A76-CBFFF64A6EB1@example.com", + "from": "test@example.com", + "size": "309891", + "nrcpt": "1", + "messages": [ + { + "time": "0000-10-10T15:59:32+00:09", + "to": "test@example.ddd", + "status": "sent", + "message": "to=, relay=example.ddd[192.168.0.30]:25, delay=3.4, delays=0.11/0/0.38/2.9, dsn=2.0.0, status=sent (250 2.0.0 OK 1539154772 az9-v6si5976496plb.190 - gsmtp)", + "bounce_id": "" + } + ] +} diff --git a/test/test.rcpt.147.json b/test/test.rcpt.147.json new file mode 100644 index 0000000..a8cc988 --- /dev/null +++ b/test/test.rcpt.147.json @@ -0,0 +1,44 @@ +{ + "time": "2025-03-15T13:30:30.342887+01:00", + "hostname": "smtp.example.org", + "process": "postfix/smtpd[17507]", + "queue_id": "53AEC4F144", + "client_hostname": "unknown", + "client_ip": "192.168.0.22", + "sasl_method": "LOGIN", + "sasl_username": "smtpuser@smtp.example.org", + "message_id": "94851c4c5f9b5ef98755296606bf32ab@example.org", + "from": "sender@example.org", + "size": "", + "nrcpt": "4", + "messages": [ + { + "time": "2025-03-15T13:30:31.206043+01:00", + "to": "recipient1@domain1.net", + "status": "milter-reject", + "message": "milter-reject: END-OF-MESSAGE from unknown[192.168.0.22]: 4.7.1 Ratelimit \"to_ip_from\" exceeded; from= to= proto=ESMTP helo=", + "bounce_id": "" + }, + { + "time": "2025-03-15T13:30:31.206043+01:00", + "to": "recipient2@foreign.net", + "status": "milter-reject", + "message": "milter-reject: END-OF-MESSAGE from unknown[192.168.0.22]: 4.7.1 Ratelimit \"to_ip_from\" exceeded; from= to= proto=ESMTP helo=", + "bounce_id": "" + }, + { + "time": "2025-03-15T13:30:31.206043+01:00", + "to": "recipient3@somewhere.in", + "status": "milter-reject", + "message": "milter-reject: END-OF-MESSAGE from unknown[192.168.0.22]: 4.7.1 Ratelimit \"to_ip_from\" exceeded; from= to= proto=ESMTP helo=", + "bounce_id": "" + }, + { + "time": "2025-03-15T13:30:31.206043+01:00", + "to": "recipient4@anotherdomain.com", + "status": "milter-reject", + "message": "milter-reject: END-OF-MESSAGE from unknown[192.168.0.22]: 4.7.1 Ratelimit \"to_ip_from\" exceeded; from= to= proto=ESMTP helo=", + "bounce_id": "" + } + ] +} diff --git a/test/test.rcpt.log b/test/test.rcpt.log new file mode 100644 index 0000000..aace8dc --- /dev/null +++ b/test/test.rcpt.log @@ -0,0 +1,11 @@ +2025-03-15T13:30:29.169282+01:00 smtp.example.org postfix/smtpd[17507] warning: hostname smtp.internal.local does not resolve to address 192.168.0.22: Name does not resolve +2025-03-15T13:30:29.169332+01:00 smtp.example.org postfix/smtpd[17507] connect from unknown[192.168.0.22] +2025-03-15T13:30:29.221416+01:00 smtp.example.org postfix/smtpd[17507] Anonymous TLS connection established from unknown[192.168.0.22]: TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits) +2025-03-15T13:30:30.342887+01:00 smtp.example.org postfix/smtpd[17507] 53AEC4F144: client=unknown[192.168.0.22], sasl_method=LOGIN, sasl_username=smtpuser@smtp.example.org +2025-03-15T13:30:30.352537+01:00 smtp.example.org postfix/smtpd[17507] 53AEC4F144: filter: RCPT from unknown[192.168.0.22]: : Recipient address triggers FILTER relay:[pod-02.smtpout.exploit.intra]; from= to= proto=ESMTP helo= +2025-03-15T13:30:30.355033+01:00 smtp.example.org postfix/smtpd[17507] 53AEC4F144: filter: RCPT from unknown[192.168.0.22]: : Recipient address triggers FILTER relay:[pod-02.smtpout.exploit.intra]; from= to= proto=ESMTP helo= +2025-03-15T13:30:30.362445+01:00 smtp.example.org postfix/smtpd[17507] 53AEC4F144: filter: RCPT from unknown[192.168.0.22]: : Recipient address triggers FILTER relay:[pod-02.smtpout.exploit.intra]; from= to= proto=ESMTP helo= +2025-03-15T13:30:31.158051+01:00 smtp.example.org postfix/cleanup[58145] 53AEC4F144: replace: header Received: from smtp.internal.local (unknown [192.168.0.22])??(using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits))??(No client certificate requested)??(Authenticated sender: smtpuser from unknown[192.168.0.22]; from= to= proto=ESMTP helo=: Received: from smtp.internal.local (unknown [192.168.0.22])??(using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits))??(No client certificate requested)??(Authenticated sender: anonymized)??by smtp.example.org (Postfix) with ESMTPSA id 53AEC4F144;??Sat, 15 Mar 2025 13:30:29 +0100 (CET) +2025-03-15T13:30:31.158169+01:00 smtp.example.org postfix/cleanup[58145] 53AEC4F144: message-id=<94851c4c5f9b5ef98755296606bf32ab@example.org> +2025-03-15T13:30:31.206043+01:00 smtp.example.org postfix/cleanup[58145] 53AEC4F144: milter-reject: END-OF-MESSAGE from unknown[192.168.0.22]: 4.7.1 Ratelimit "to_ip_from" exceeded; from= to= proto=ESMTP helo= +2025-03-15T13:30:31.238801+01:00 smtp.example.org postfix/smtpd[17507] disconnect from unknown[192.168.0.22] ehlo=2 starttls=1 auth=1 mail=1 rcpt=4/4 bdat=0/1 rset=1 quit=1 commands=8/10 From b7b9a936763dc19d5102c9029230bc4c8d9fb278 Mon Sep 17 00:00:00 2001 From: yo Date: Sun, 16 Mar 2025 19:31:56 +0100 Subject: [PATCH 098/100] Remove unused filename parameter --- postfix-log-parser/cmd/root.go | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/postfix-log-parser/cmd/root.go b/postfix-log-parser/cmd/root.go index f9fd96b..6aa84dc 100644 --- a/postfix-log-parser/cmd/root.go +++ b/postfix-log-parser/cmd/root.go @@ -247,8 +247,7 @@ func NewWriter(file string) (*bufio.Writer, *os.File, error) { } } -// TODO: Remove filename arg -func writeOut(msg string, filename string) error { +func writeOut(msg string) error { _, err := fmt.Fprintln(Writer, msg) Writer.Flush() if err != nil { @@ -373,7 +372,7 @@ func parseStoreAndWrite(input []byte, mq map[string]*PostfixLogParser, mqMtx *sy log.Fatal(err) } outfMtx.Lock() - err = writeOut(string(jsonBytes), gOutputFile) + err = writeOut(string(jsonBytes)) outfMtx.Unlock() if err != nil { log.Fatal(err) @@ -414,7 +413,7 @@ func parseStoreAndWrite(input []byte, mq map[string]*PostfixLogParser, mqMtx *sy log.Fatal(err) } outfMtx.Lock() - err = writeOut(string(jsonBytes), gOutputFile) + err = writeOut(string(jsonBytes)) outfMtx.Unlock() if err != nil { log.Fatal(err) @@ -569,7 +568,7 @@ func parseStoreAndWrite(input []byte, mq map[string]*PostfixLogParser, mqMtx *sy log.Fatal(err) } outfMtx.Lock() - err = writeOut(string(jsonBytes), gOutputFile) + err = writeOut(string(jsonBytes)) outfMtx.Unlock() if err != nil { log.Fatal(err) @@ -666,7 +665,7 @@ func parseStoreAndWrite(input []byte, mq map[string]*PostfixLogParser, mqMtx *sy log.Fatal(err) } outfMtx.Lock() - err = writeOut(string(jsonBytes), gOutputFile) + err = writeOut(string(jsonBytes)) outfMtx.Unlock() if err != nil { log.Fatal(err) @@ -680,7 +679,7 @@ func parseStoreAndWrite(input []byte, mq map[string]*PostfixLogParser, mqMtx *sy log.Fatal(err) } outfMtx.Lock() - err = writeOut(string(jsonBytes), gOutputFile) + err = writeOut(string(jsonBytes)) outfMtx.Unlock() if err != nil { log.Fatal(err) From eaeb0dd630c50807afe46381d27b4497f21aa04f Mon Sep 17 00:00:00 2001 From: yo Date: Sun, 16 Mar 2025 19:46:06 +0100 Subject: [PATCH 099/100] Fix reject from not a fully qualified address, comments --- postfix-log-parser.go | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/postfix-log-parser.go b/postfix-log-parser.go index 73d37a7..efb0363 100644 --- a/postfix-log-parser.go +++ b/postfix-log-parser.go @@ -10,20 +10,34 @@ const ( SyslogPri = `(?:<\d{1,3}>)?` TimeFormat = "Jan 2 15:04:05" TimeFormatISO8601 = "2006-01-02T15:04:05.999999-07:00" + // Capture group 1 TimeRegexpFormat = `([A-Za-z]{3}\s*[0-9]{1,2} [0-9]{2}:[0-9]{2}:[0-9]{2}|^\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d(?:\.\d+)?(?:[+-][0-2]\d:[0-5]\d|Z))` + // Capture group 2 HostRegexpFormat = `([0-9A-Za-z\-\.]*)` + // Capture group 3 ProcessRegexpFormat = `(postfix.*(?:\/[a-z]*)+\[[0-9]{1,7}\])?` + // Capture group 4 QueueIdRegexpFormat = `([0-9A-Z]*)` - // These are the main messages + // These are the main messages : Capture group 5 get all the following + // Capture groups 6, 7, 8, 9 ClientRegexpFormat = `(?:client=(.+)\[(.+)\](?:, sasl_method=(.+), sasl_username=(.+))?)?` + // CG 10 MessageIdRegexpFormat = `(?:message-id=<(.+)>)?` + // CG 11, 12, 13 FromRegexpFormat = `(?:from=<(.+@.+)>(?:, size=(\d+), nrcpt=(\d+))?)?` + // CG 14, 15 ToRegexpFormat = `(?:to=<(.+@.+)>.*status=([a-z]+))?` + // CG 16 SenderNDNRegexpFormat = `(?:sender non-delivery notification: ([0-9A-Z]*))?` + // CG 17, 18, 19, 20, 21 MilterRegexpFormat = `(?:(milter-.*): .* from (.+)\[(.+)\]: .*from=<(.+@.+)?> to=<(.+@.+)> .*)?` + // CG 22, 23, 24, 25 AuthentFailedRegexpFormat = `(?:warning: (.+)\[(.+)\]: SASL (.*) authentication failed: (.*))?` - PostfixRejectRegexpFormat = `(?:reject: RCPT from (.+)\[(.+)\]: [0-9]{3} [0-9\.]{5} [^;]*; from=<(.+@.+)?> to=<(.+@[^>]+)>.*$)?` + // CG 26, 27, 28, 29. from regex is voluntarily lazy, b/c some senders really do not put '@' in from + PostfixRejectRegexpFormat = `(?:reject: RCPT from (.+)\[(.+)\]: [0-9]{3} [0-9\.]{5} [^;]*; from=<(.+)?> to=<(.+@[^>]+)>.*$)?` + // CG 30, 31, 32 PostfixRLimitExceedFormat = `(?:warning: Message delivery request rate limit exceeded: ([0-9]+) from (.+)\[(.+)\] for service [a-zA-Z]+)?` + // CG 33, 34, 35, 36, 37, 38 FilterRcptRegexpFormat = `(?:filter: RCPT from (.+)\[(.+)\]: <(.+@.+)?>: ([^;]+); from=<(.+@.+)?> to=<(.+@[^>]+)> proto=[^ ]+ helo=<.+>)?` MessageDetailsRegexpFormat = `(` + ClientRegexpFormat + MessageIdRegexpFormat + FromRegexpFormat + ToRegexpFormat + SenderNDNRegexpFormat + MilterRegexpFormat + AuthentFailedRegexpFormat + PostfixRejectRegexpFormat + PostfixRLimitExceedFormat + FilterRcptRegexpFormat + `.*)` RegexpFormat = SyslogPri + TimeRegexpFormat + ` ` + HostRegexpFormat + ` ` + ProcessRegexpFormat + `:? ` + QueueIdRegexpFormat + `(?:\: )?` + MessageDetailsRegexpFormat From 25f6a145015b39884ad245ff4e0868835aa189b2 Mon Sep 17 00:00:00 2001 From: yo Date: Sun, 16 Mar 2025 19:48:01 +0100 Subject: [PATCH 100/100] Update tests --- test/test-iso8601.log | 4 ++++ test/test-iso8601.log.147.json | 23 +++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/test/test-iso8601.log b/test/test-iso8601.log index a729141..a9912b5 100644 --- a/test/test-iso8601.log +++ b/test/test-iso8601.log @@ -57,3 +57,7 @@ 2025-03-15T13:30:31.158169+01:00 smtp.example.org postfix/cleanup[58145] 53AEC4F144: message-id=<94851c4c5f9b5ef98755296606bf32ab@example.org> 2025-03-15T13:30:31.206043+01:00 smtp.example.org postfix/cleanup[58145] 53AEC4F144: milter-reject: END-OF-MESSAGE from unknown[192.168.0.22]: 4.7.1 Ratelimit "to_ip_from" exceeded; from= to= proto=ESMTP helo= 2025-03-15T13:30:31.238801+01:00 smtp.example.org postfix/smtpd[17507] disconnect from unknown[192.168.0.22] ehlo=2 starttls=1 auth=1 mail=1 rcpt=4/4 bdat=0/1 rset=1 quit=1 commands=8/10 +2025-03-09T16:41:59.516331+01:00 smtp.example.org postfix/smtpd[78309] connect from unknown[192.168.0.11] +2025-03-09T16:41:59.568868+01:00 smtp.example.org postfix/smtpd[78309] NOQUEUE: reject: RCPT from unknown[192.168.0.11]: 504 5.5.2 : Sender address rejected: need fully-qualified address; from= to= proto=ESMTP helo= +2025-03-09T16:41:59.568892+01:00 smtp.example.org postfix/smtpd[78309] using backwards-compatible default setting smtpd_relay_before_recipient_restrictions=no to reject recipient "recipient@another.domain.com" from client "unknown[192.168.0.11]" +2025-03-09T16:41:59.587032+01:00 smtp.example.org postfix/smtpd[78309] disconnect from unknown[192.168.0.11] ehlo=1 mail=1 rcpt=0/1 quit=1 commands=3/4 diff --git a/test/test-iso8601.log.147.json b/test/test-iso8601.log.147.json index c8d142b..d961fb3 100644 --- a/test/test-iso8601.log.147.json +++ b/test/test-iso8601.log.147.json @@ -256,3 +256,26 @@ } ] } +{ + "time": "2025-03-09T16:41:59.568868+01:00", + "hostname": "smtp.example.org", + "process": "postfix/smtpd[78309]", + "queue_id": "NOQUEUE", + "client_hostname": "unknown", + "client_ip": "192.168.0.11", + "sasl_method": "", + "sasl_username": "", + "message_id": "", + "from": "not_an_email", + "size": "", + "nrcpt": "1", + "messages": [ + { + "time": "2025-03-09T16:41:59.568868+01:00", + "to": "recipient@another.domain.com", + "status": "postfix-reject", + "message": "reject: RCPT from unknown[192.168.0.11]: 504 5.5.2 : Sender address rejected: need fully-qualified address; from= to= proto=ESMTP helo=", + "bounce_id": "" + } + ] +}