From 3bf2433591653a709d74d6c644f778558683f0ad Mon Sep 17 00:00:00 2001 From: Tit Petric Date: Thu, 2 Apr 2020 11:12:45 +0200 Subject: [PATCH 1/9] tcpchecker: honor proper timeout value Fixes #82 --- tcpchecker.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tcpchecker.go b/tcpchecker.go index f728f1f..0c3c891 100644 --- a/tcpchecker.go +++ b/tcpchecker.go @@ -98,7 +98,7 @@ func (c TCPChecker) doChecks() Attempts { conn.Close() } } else { - if conn, err = net.DialTimeout("tcp", c.URL, c.Timeout); err == nil { + if conn, err = net.DialTimeout("tcp", c.URL, timeout); err == nil { conn.Close() } } From 4cac709b6a3f5d3ddf9253e19632c59996aac36b Mon Sep 17 00:00:00 2001 From: Tit Petric Date: Thu, 2 Apr 2020 11:57:17 +0200 Subject: [PATCH 2/9] checkup: fix race condition in checkup test When running `go test -race ./...`, a race condition is reported for a fake implementation of a filesystem for TestCheckAndStore. A mutex lock has been added into the structure functions and test to avoid the error report. Fixes #107 --- checkup_test.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/checkup_test.go b/checkup_test.go index fe603f4..3b966f8 100644 --- a/checkup_test.go +++ b/checkup_test.go @@ -4,6 +4,7 @@ import ( "bytes" "encoding/json" "errors" + "sync" "testing" "time" ) @@ -71,6 +72,8 @@ func TestCheckAndStoreEvery(t *testing.T) { time.Sleep(170 * time.Millisecond) ticker.Stop() + f.Lock() + defer f.Unlock() if got, want := f.checked, 3; got != want { t.Errorf("Expected %d checks while sleeping, had: %d", want, got) } @@ -193,6 +196,8 @@ func TestJSON(t *testing.T) { var errTest = errors.New("i'm an error") type fake struct { + sync.Mutex + returnErr bool checked int stored []Result @@ -201,6 +206,9 @@ type fake struct { } func (f *fake) Check() (Result, error) { + f.Lock() + defer f.Unlock() + f.checked++ r := Result{Timestamp: time.Now().UTC().UnixNano()} if f.returnErr { @@ -210,6 +218,9 @@ func (f *fake) Check() (Result, error) { } func (f *fake) Store(results []Result) error { + f.Lock() + defer f.Unlock() + f.stored = results if f.returnErr { return errTest @@ -218,11 +229,17 @@ func (f *fake) Store(results []Result) error { } func (f *fake) Maintain() error { + f.Lock() + defer f.Unlock() + f.maintained++ return nil } func (f *fake) Notify(results []Result) error { + f.Lock() + defer f.Unlock() + f.notified++ return nil } From cb66e80c0518f92d5d714bd93b709df635e0f748 Mon Sep 17 00:00:00 2001 From: Tit Petric Date: Thu, 2 Apr 2020 11:58:05 +0200 Subject: [PATCH 3/9] feature: implement exec checker to run custom commands/scripts A special checker has been implemented that runs external commands via os.Exec. This opens up the possibility of implementing more complex, protocol-aware checks, without necessarily implementing a custom checker in Go code. --- execchecker.go | 168 ++++++++++++++++++++++++++++++++++++++++++++ execchecker_test.go | 39 ++++++++++ testdata/exec.sh | 3 + 3 files changed, 210 insertions(+) create mode 100644 execchecker.go create mode 100644 execchecker_test.go create mode 100755 testdata/exec.sh diff --git a/execchecker.go b/execchecker.go new file mode 100644 index 0000000..d976a24 --- /dev/null +++ b/execchecker.go @@ -0,0 +1,168 @@ +package checkup + +import ( + "context" + "fmt" + "os/exec" + "strings" + "time" +) + +// ExecChecker implements a Checker by running programs with os.Exec. +type ExecChecker struct { + // Name is the name of the endpoint. + Name string `json:"name"` + + // Command is the main program entrypoint. + Command string `json:"command"` + + // Arguments are individual program parameters. + Arguments []string `json:"arguments,omitempty"` + + // ThresholdRTT is the maximum round trip time to + // allow for a healthy endpoint. If non-zero and a + // request takes longer than ThresholdRTT, the + // endpoint will be considered unhealthy. Note that + // this duration includes any in-between network + // latency. + ThresholdRTT time.Duration `json:"threshold_rtt,omitempty"` + + // MustContain is a string that the response body + // must contain in order to be considered up. + // NOTE: If set, the entire response body will + // be consumed, which has the potential of using + // lots of memory and slowing down checks if the + // response body is large. + MustContain string `json:"must_contain,omitempty"` + + // MustNotContain is a string that the response + // body must NOT contain in order to be considered + // up. If both MustContain and MustNotContain are + // set, they are and-ed together. NOTE: If set, + // the entire response body will be consumed, which + // has the potential of using lots of memory and + // slowing down checks if the response body is large. + MustNotContain string `json:"must_not_contain,omitempty"` + + // Raise is a string that tells us if we should throw + // a hard error ("error" - the default), or if we should + // just mark something as degraded ("warn" or "warning"). + Raise string `json:"raise,omitempty"` + + // Attempts is how many requests the client will + // make to the endpoint in a single check. + Attempts int `json:"attempts,omitempty"` + + // AttemptSpacing spaces out each attempt in a check + // by this duration to avoid hitting a remote too + // quickly in succession. By default, no waiting + // occurs between attempts. + AttemptSpacing time.Duration `json:"attempt_spacing,omitempty"` +} + +// Check performs checks using c according to its configuration. +// An error is only returned if there is a configuration error. +func (c ExecChecker) Check() (Result, error) { + if c.Attempts < 1 { + c.Attempts = 1 + } + + result := Result{ + Title: c.Name, + Endpoint: c.Command, + Timestamp: Timestamp(), + } + + result.Times = c.doChecks() + + return c.conclude(result), nil +} + +// doChecks executes command and returns each attempt. +func (c ExecChecker) doChecks() Attempts { + checks := make(Attempts, c.Attempts) + for i := 0; i < c.Attempts; i++ { + start := time.Now() + + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + command := exec.CommandContext(ctx, c.Command, c.Arguments...) + output, err := command.CombinedOutput() + + checks[i].RTT = time.Since(start) + + if err != nil { + stringify := func(s string) string { + if strings.TrimSpace(s) == "" { + return "empty" + } + return s + } + checks[i].Error = fmt.Sprintf("Error: %s\nOutput: %s\n", err.Error(), stringify(string(output))) + continue + } + + if err := c.checkDown(string(output)); err != nil { + checks[i].Error = err.Error() + } + + if c.AttemptSpacing > 0 { + time.Sleep(c.AttemptSpacing) + } + } + return checks +} + +// conclude takes the data in result from the attempts and +// computes remaining values needed to fill out the result. +// It detects degraded (high-latency) responses and makes +// the conclusion about the result's status. +func (c ExecChecker) conclude(result Result) Result { + result.ThresholdRTT = c.ThresholdRTT + + warning := c.Raise == "warn" || c.Raise == "warning" + + // Check errors (down) + for i := range result.Times { + if result.Times[i].Error != "" { + if warning { + result.Notice = result.Times[i].Error + result.Degraded = true + return result + } + result.Down = true + return result + } + } + + // Check round trip time (degraded) + if c.ThresholdRTT > 0 { + stats := result.ComputeStats() + if stats.Median > c.ThresholdRTT { + result.Notice = fmt.Sprintf("median round trip time exceeded threshold (%s)", c.ThresholdRTT) + result.Degraded = true + return result + } + } + + result.Healthy = true + return result +} + +// checkDown checks whether the endpoint is down based on resp and +// the configuration of c. It returns a non-nil error if down. +// Note that it does not check for degraded response. +func (c ExecChecker) checkDown(body string) error { + // Check response body + if c.MustContain == "" && c.MustNotContain == "" { + return nil + } + if c.MustContain != "" && !strings.Contains(body, c.MustContain) { + return fmt.Errorf("response does not contain '%s'", c.MustContain) + } + if c.MustNotContain != "" && strings.Contains(body, c.MustNotContain) { + return fmt.Errorf("response contains '%s'", c.MustNotContain) + } + return nil +} diff --git a/execchecker_test.go b/execchecker_test.go new file mode 100644 index 0000000..cb0eb59 --- /dev/null +++ b/execchecker_test.go @@ -0,0 +1,39 @@ +package checkup + +import ( + "testing" +) + +func TestExecChecker(t *testing.T) { + + assert := func(ok bool, format string, args ...interface{}) { + if !ok { + t.Fatalf(format, args...) + } + } + + command := "testdata/exec.sh" + + // check non-zero exit code + { + testName := "Non-zero exit" + hc := ExecChecker{Name: testName, Command: command, Arguments: []string{"1", testName}, Attempts: 2} + + result, err := hc.Check() + assert(err == nil, "expected no error, got %v, %#v", err, result) + assert(result.Title == testName, "expected result.Title == %s, got %s", testName, result.Title) + assert(result.Down == true, "expected result.Down = true, got %v", result.Down) + } + + // check zero exit code + { + testName := "Non-zero exit" + hc := ExecChecker{Name: testName, Command: command, Arguments: []string{"0", testName}, Attempts: 2} + + result, err := hc.Check() + t.Logf("%#v", result) + assert(err == nil, "expected no error, got %v, %#v", err, result) + assert(result.Title == testName, "expected result.Title == %s, got %s", testName, result.Title) + assert(result.Down == false, "expected result.Down = false, got %v", result.Down) + } +} diff --git a/testdata/exec.sh b/testdata/exec.sh new file mode 100755 index 0000000..e0ba094 --- /dev/null +++ b/testdata/exec.sh @@ -0,0 +1,3 @@ +#!/bin/bash +echo "$@" +exit $1 \ No newline at end of file From 764214ab2739ed7c6743df34272a4f44d7d5e570 Mon Sep 17 00:00:00 2001 From: Tit Petric Date: Thu, 2 Apr 2020 11:58:34 +0200 Subject: [PATCH 4/9] checkup: add makefile and go.mod/go.sum With the upgrade to a recent go version, we're upgrading to use go modules. Makefile was added to provide the proper build commands, test commands and disable cached test output with -count=1. --- Makefile | 10 +++ go.mod | 21 ++++++ go.sum | 213 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 244 insertions(+) create mode 100644 Makefile create mode 100644 go.mod create mode 100644 go.sum diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..2379301 --- /dev/null +++ b/Makefile @@ -0,0 +1,10 @@ +.PHONY: all build test + +all: build test + +build: + mkdir -p builds/ + go build -o builds/ ./cmd/... + +test: + go test -race -count=1 -v ./... diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..9395ccb --- /dev/null +++ b/go.mod @@ -0,0 +1,21 @@ +module github.com/sourcegraph/checkup + +go 1.13 + +require ( + github.com/ashwanthkumar/slack-go-webhook v0.0.0-20200209025033-430dd4e66960 + github.com/aws/aws-sdk-go v1.30.2 + github.com/elazarl/goproxy v0.0.0-20200315184450-1f3cb6622dad // indirect + github.com/fatih/color v1.9.0 + github.com/google/go-github v17.0.0+incompatible + github.com/google/go-querystring v1.0.0 // indirect + github.com/jmoiron/sqlx v1.2.0 + github.com/lib/pq v1.3.0 + github.com/mattn/go-sqlite3 v2.0.3+incompatible + github.com/miekg/dns v1.1.29 + github.com/parnurzeal/gorequest v0.2.16 // indirect + github.com/smartystreets/goconvey v1.6.4 // indirect + github.com/spf13/cobra v0.0.7 + golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d + moul.io/http2curl v1.0.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..e84c2e9 --- /dev/null +++ b/go.sum @@ -0,0 +1,213 @@ +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= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +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/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/ashwanthkumar/slack-go-webhook v0.0.0-20200209025033-430dd4e66960 h1:MIEURpsIpyLyy+dZ+GnL8T5P49Tco0ik9cYaUQNnAxE= +github.com/ashwanthkumar/slack-go-webhook v0.0.0-20200209025033-430dd4e66960/go.mod h1:97O1qkjJBHSSaWJxsTShRIeFy0HWiygk+jnugO9aX3I= +github.com/aws/aws-sdk-go v1.30.2 h1:0vuroAsbPwVbP91MMaUmFLnrQcFBhmjQnnXaH1kcnPw= +github.com/aws/aws-sdk-go v1.30.2/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= +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/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-semver v0.2.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 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/elazarl/goproxy v0.0.0-20200315184450-1f3cb6622dad h1:zPs0fNF2Io1Qytf92EI2CDJ9oCXZr+NmjEVexrUEdq4= +github.com/elazarl/goproxy v0.0.0-20200315184450-1f3cb6622dad/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= +github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2 h1:dWB6v3RcOy03t/bUadywsbyrQwCqZeNIEX6M1OtSZOM= +github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8= +github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= +github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= +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-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +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-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= +github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +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/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/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-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY= +github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= +github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= +github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +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/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jmespath/go-jmespath v0.3.0 h1:OS12ieG61fsCg5+qLJ+SsW9NicxNkg3b25OyT2yCeUc= +github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= +github.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA= +github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +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/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/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +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 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.3.0 h1:/qkRGz8zljWiDcFvgpwUpwIAPu3r07TDvs3Rws+o/pU= +github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM= +github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= +github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U= +github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/miekg/dns v1.1.29 h1:xHBEhR+t5RzcFJjBLJlax2daXOrTYtr9z4WdKEfWFzg= +github.com/miekg/dns v1.1.29/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/parnurzeal/gorequest v0.2.16 h1:T/5x+/4BT+nj+3eSknXmCTnEVGSzFzPGdpqmUVVZXHQ= +github.com/parnurzeal/gorequest v0.2.16/go.mod h1:3Kh2QUMJoqw3icWAecsyzkpY7UzRfDhbRdTjtNwNiUE= +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.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +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/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_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/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/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/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-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +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/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= +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.7 h1:FfTH+vuMXOas8jmfb5/M7dzEYx7LpcLb7a0LPe34uOU= +github.com/spf13/cobra v0.0.7/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= +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/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= +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.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +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-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +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-20181114220301-adae6a3d119a/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-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-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +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-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-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/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-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +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 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +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 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +moul.io/http2curl v1.0.0 h1:6XwpyZOYsgZJrU8exnG87ncVkU1FVCcTRpwzOkTDUi8= +moul.io/http2curl v1.0.0/go.mod h1:f6cULg+e4Md/oW1cYmwW4IWQOVl2lGbmCNGOHvzX2kE= From ae9ad7ba4205490ea37020d2c340589642ea6b96 Mon Sep 17 00:00:00 2001 From: Tit Petric Date: Thu, 2 Apr 2020 12:41:05 +0200 Subject: [PATCH 5/9] sql: optionally exclude sqlite/pgsql filesystems In order to support building with CGO_ENABLED=0, build tags were added to the sql filesystem implementation. Sqlite requires cgo, and it now needs to be explicitly enabled with `-tags sql` option at build time. Fixes #102 --- Makefile | 7 +++++-- sql.go | 2 ++ sql_disabled.go | 13 +++++++++++++ sql_test.go | 2 ++ 4 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 sql_disabled.go diff --git a/Makefile b/Makefile index 2379301..e4be0d7 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ -.PHONY: all build test +.PHONY: all build test docker -all: build test +all: build test docker build: mkdir -p builds/ @@ -8,3 +8,6 @@ build: test: go test -race -count=1 -v ./... + +docker: + docker build --no-cache . -t checkup \ No newline at end of file diff --git a/sql.go b/sql.go index 914124f..b338b79 100644 --- a/sql.go +++ b/sql.go @@ -1,3 +1,5 @@ +// +build sql + package checkup import ( diff --git a/sql_disabled.go b/sql_disabled.go new file mode 100644 index 0000000..428fe81 --- /dev/null +++ b/sql_disabled.go @@ -0,0 +1,13 @@ +// +build !sql + +package checkup + +import ( + "errors" +) + +type SQL struct {} + +func (sql SQL) Store(results []Result) error { + return errors.New("sql data store is disabled") +} \ No newline at end of file diff --git a/sql_test.go b/sql_test.go index a987299..30955d0 100644 --- a/sql_test.go +++ b/sql_test.go @@ -1,3 +1,5 @@ +// +build sql + package checkup import ( From ed3a263b34e558014de0b850cf18abba3af6ed16 Mon Sep 17 00:00:00 2001 From: Tit Petric Date: Fri, 3 Apr 2020 12:00:49 +0200 Subject: [PATCH 6/9] docker: add Dockerfile for building a checkup docker image --- Dockerfile | 15 +++++++++++++++ Makefile | 6 ++++-- 2 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 Dockerfile diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..43f1ed9 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,15 @@ +FROM golang:1.13-alpine as builder + +ENV CGO_ENABLED=0 + +COPY . /app +WORKDIR /app +RUN apk --no-cache add make && make build + +FROM alpine:latest + +WORKDIR /app + +COPY --from=builder /app/builds/checkup /usr/local/bin/checkup + +ENTRYPOINT ["checkup"] diff --git a/Makefile b/Makefile index e4be0d7..ee1e353 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,8 @@ .PHONY: all build test docker -all: build test docker +all: build test + +DOCKER_IMAGE := checkup build: mkdir -p builds/ @@ -10,4 +12,4 @@ test: go test -race -count=1 -v ./... docker: - docker build --no-cache . -t checkup \ No newline at end of file + docker build --no-cache . -t $(DOCKER_IMAGE) From dd04ef1d42d73866aeb206d53a9d3ac6df4704a9 Mon Sep 17 00:00:00 2001 From: Tit Petric Date: Fri, 3 Apr 2020 12:01:15 +0200 Subject: [PATCH 7/9] travis: bump go version to 1.13 --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index 4f2ee4d..2cdf750 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1 +1,4 @@ language: go + +go: +- 1.13 \ No newline at end of file From bf67ea3401efe1efa906d19d64d9b3f8a48ac85e Mon Sep 17 00:00:00 2001 From: Tit Petric Date: Fri, 3 Apr 2020 12:09:07 +0200 Subject: [PATCH 8/9] checkup: enable ExecChecker for usage --- checkup.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/checkup.go b/checkup.go index 40d6b39..bae7d41 100644 --- a/checkup.go +++ b/checkup.go @@ -175,6 +175,8 @@ func (c Checkup) MarshalJSON() ([]byte, error) { } var typeName string switch ch.(type) { + case ExecChecker: + typeName = "exec" case HTTPChecker: typeName = "http" case TCPChecker: @@ -287,6 +289,13 @@ func (c *Checkup) UnmarshalJSON(b []byte) error { // assertions with the help of the type information for i, t := range types.Checkers { switch t.Type { + case "exec": + var checker ExecChecker + err = json.Unmarshal(raw.Checkers[i], &checker) + if err != nil { + return err + } + c.Checkers = append(c.Checkers, checker) case "http": var checker HTTPChecker err = json.Unmarshal(raw.Checkers[i], &checker) From 61e429e37c0d6195fb3dc4cbab8f383a54b5643a Mon Sep 17 00:00:00 2001 From: Tit Petric Date: Fri, 3 Apr 2020 14:10:11 +0200 Subject: [PATCH 9/9] tcpchecker: fix expired testdata certificates Tcpchecker tests were failing due to expired self signed certificates. This commit adds new certificates, a Makefile for regenerating them, and adds test output in case of similar certificate errors. Fixes #108 --- sql_disabled.go | 4 +-- tcpchecker_test.go | 28 ++++++++++++++++++--- testdata/Makefile | 16 ++++++++++++ testdata/ca.pem | 38 ----------------------------- testdata/client.debug.crt | 43 +++++++++++++++++++++++++++++++++ testdata/client.key | 5 ++++ testdata/client.pem | 10 ++++++++ testdata/key.pem | 51 --------------------------------------- testdata/leaf.debug.crt | 47 ++++++++++++++++++++++++++++++++++++ testdata/leaf.key | 5 ++++ testdata/leaf.pem | 11 +++++++++ testdata/root.debug.crt | 45 ++++++++++++++++++++++++++++++++++ testdata/root.key | 5 ++++ testdata/root.pem | 11 +++++++++ testdata/server.pem | 31 ------------------------ 15 files changed, 224 insertions(+), 126 deletions(-) create mode 100644 testdata/Makefile delete mode 100644 testdata/ca.pem create mode 100644 testdata/client.debug.crt create mode 100644 testdata/client.key create mode 100644 testdata/client.pem delete mode 100644 testdata/key.pem create mode 100644 testdata/leaf.debug.crt create mode 100644 testdata/leaf.key create mode 100644 testdata/leaf.pem create mode 100644 testdata/root.debug.crt create mode 100644 testdata/root.key create mode 100644 testdata/root.pem delete mode 100644 testdata/server.pem diff --git a/sql_disabled.go b/sql_disabled.go index 428fe81..7ae0026 100644 --- a/sql_disabled.go +++ b/sql_disabled.go @@ -6,8 +6,8 @@ import ( "errors" ) -type SQL struct {} +type SQL struct{} func (sql SQL) Store(results []Result) error { return errors.New("sql data store is disabled") -} \ No newline at end of file +} diff --git a/tcpchecker_test.go b/tcpchecker_test.go index 1947355..4016b3b 100644 --- a/tcpchecker_test.go +++ b/tcpchecker_test.go @@ -144,7 +144,7 @@ func TestTCPCheckerWithAgressiveTimeout(t *testing.T) { func TestTCPCheckerWithTLSNoVerify(t *testing.T) { // Listen on localhost, random port - certPair, err := tls.LoadX509KeyPair("testdata/server.pem", "testdata/key.pem") + certPair, err := tls.LoadX509KeyPair("testdata/leaf.pem", "testdata/leaf.key") if err != nil { t.Error("Failed to load certificate.", err) } @@ -249,7 +249,7 @@ func TestTCPCheckerWithTLSNoVerify(t *testing.T) { func TestTCPCheckerWithTLSVerifySuccess(t *testing.T) { // Listen on localhost, random port - certPair, err := tls.LoadX509KeyPair("testdata/server.pem", "testdata/key.pem") + certPair, err := tls.LoadX509KeyPair("testdata/leaf.pem", "testdata/leaf.key") if err != nil { t.Error("Failed to load certificate.", err) } @@ -280,13 +280,18 @@ func TestTCPCheckerWithTLSVerifySuccess(t *testing.T) { // Should know the host:port by now endpt := srv.Addr().String() testName := "TestWithTLSNoVerify" - hc := TCPChecker{Name: testName, URL: endpt, TLSEnabled: true, TLSCAFile: "testdata/ca.pem", Attempts: 2} + hc := TCPChecker{Name: testName, URL: endpt, TLSEnabled: true, TLSCAFile: "testdata/root.pem", Attempts: 2} // Try an up server result, err := hc.Check() if err != nil { t.Errorf("Didn't expect an error: %v", err) } + for _, run := range result.Times { + if got, want := run.Error, ""; got != want { + t.Fatalf("Expected no errors, got %s", got) + } + } if got, want := result.Title, testName; got != want { t.Errorf("Expected result.Title='%s', got '%s'", want, got) @@ -316,6 +321,11 @@ func TestTCPCheckerWithTLSVerifySuccess(t *testing.T) { if err != nil { t.Errorf("Didn't expect an error: %v", err) } + for _, run := range result.Times { + if got, want := run.Error, ""; got != want { + t.Fatalf("Expected no errors, got %s", got) + } + } if got, want := result.Healthy, true; got != want { t.Errorf("Expected result.Healthy=%v, got %v", want, got) } @@ -325,6 +335,11 @@ func TestTCPCheckerWithTLSVerifySuccess(t *testing.T) { if err != nil { t.Errorf("Didn't expect an error: %v", err) } + for _, run := range result.Times { + if got, want := run.Error, ""; got != want { + t.Fatalf("Expected no errors, got %s", got) + } + } if got, want := result.Degraded, true; got != want { t.Errorf("Expected result.Degraded=%v, got %v", want, got) } @@ -334,6 +349,11 @@ func TestTCPCheckerWithTLSVerifySuccess(t *testing.T) { if err != nil { t.Errorf("Didn't expect an error: %v", err) } + for _, run := range result.Times { + if got, want := run.Error, ""; got != want { + t.Fatalf("Expected no errors, got %s", got) + } + } if got, want := result.Down, false; got != want { t.Errorf("Expected result.Down=%v, got %v", want, got) } @@ -354,7 +374,7 @@ func TestTCPCheckerWithTLSVerifySuccess(t *testing.T) { func TestTCPCheckerWithTLSVerifyError(t *testing.T) { // Listen on localhost, random port - certPair, err := tls.LoadX509KeyPair("testdata/server.pem", "testdata/key.pem") + certPair, err := tls.LoadX509KeyPair("testdata/leaf.pem", "testdata/leaf.key") if err != nil { t.Error("Failed to load certificate.", err) } diff --git a/testdata/Makefile b/testdata/Makefile new file mode 100644 index 0000000..aea339d --- /dev/null +++ b/testdata/Makefile @@ -0,0 +1,16 @@ +.PHONY: all generate_cert + +all: generate_cert + +GOPATH ?= /root/go +GENERATE_TLS_CERT = $(GOPATH)/bin/generate-tls-cert + +$(GENERATE_TLS_CERT): + go get -u github.com/Shyp/generate-tls-cert + +leaf.pem: | $(GENERATE_TLS_CERT) + rm *.crt *.key *.pem -f + $(GENERATE_TLS_CERT) --host=localhost,127.0.0.1 -duration 876000h + +# Generate TLS certificates for local development. +generate_cert: leaf.pem | $(GENERATE_TLS_CERT) diff --git a/testdata/ca.pem b/testdata/ca.pem deleted file mode 100644 index 17447f9..0000000 --- a/testdata/ca.pem +++ /dev/null @@ -1,38 +0,0 @@ ------BEGIN CERTIFICATE----- -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 -+Svp ------END CERTIFICATE----- diff --git a/testdata/client.debug.crt b/testdata/client.debug.crt new file mode 100644 index 0000000..b3609f4 --- /dev/null +++ b/testdata/client.debug.crt @@ -0,0 +1,43 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 4 (0x4) + Signature Algorithm: ecdsa-with-SHA256 + Issuer: O = Acme Co, CN = Root CA + Validity + Not Before: Apr 3 11:56:34 2020 GMT + Not After : Mar 10 11:56:34 2120 GMT + Subject: O = Acme Co, CN = client_auth_test_cert + Subject Public Key Info: + Public Key Algorithm: id-ecPublicKey + Public-Key: (256 bit) + pub: + 04:29:11:d6:05:0f:f5:a4:ff:82:b8:47:b8:7b:e1: + 9f:c5:3e:4e:99:84:f6:58:4a:99:c0:db:4b:18:da: + 52:d5:57:b5:9d:28:26:be:f3:fb:cd:80:ad:80:c9: + 6b:ec:8f:48:19:cf:97:da:e6:67:e8:50:c1:0d:07: + a2:90:01:9e:22 + ASN1 OID: prime256v1 + NIST CURVE: P-256 + X509v3 extensions: + X509v3 Key Usage: critical + Digital Signature + X509v3 Extended Key Usage: + TLS Web Client Authentication + X509v3 Basic Constraints: critical + CA:FALSE + Signature Algorithm: ecdsa-with-SHA256 + 30:45:02:21:00:f9:85:ad:5d:f0:e3:1b:1a:06:95:b8:6b:2c: + 9d:27:d2:0f:b9:10:b9:f4:5a:6d:95:02:eb:99:fa:da:3c:0b: + bc:02:20:7e:d1:09:56:ba:6d:f6:b0:bf:fc:ca:89:1d:90:4b: + e5:6e:90:bd:fd:77:76:b4:25:5b:d4:c0:a1:1d:b6:b1:fb +-----BEGIN CERTIFICATE----- +MIIBfDCCASKgAwIBAgIBBDAKBggqhkjOPQQDAjAkMRAwDgYDVQQKEwdBY21lIENv +MRAwDgYDVQQDEwdSb290IENBMCAXDTIwMDQwMzExNTYzNFoYDzIxMjAwMzEwMTE1 +NjM0WjAyMRAwDgYDVQQKEwdBY21lIENvMR4wHAYDVQQDDBVjbGllbnRfYXV0aF90 +ZXN0X2NlcnQwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQpEdYFD/Wk/4K4R7h7 +4Z/FPk6ZhPZYSpnA20sY2lLVV7WdKCa+8/vNgK2AyWvsj0gZz5fa5mfoUMENB6KQ +AZ4iozUwMzAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwIwDAYD +VR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEA+YWtXfDjGxoGlbhrLJ0n0g+5 +ELn0Wm2VAuuZ+to8C7wCIH7RCVa6bfawv/zKiR2QS+VukL39d3a0JVvUwKEdtrH7 +-----END CERTIFICATE----- diff --git a/testdata/client.key b/testdata/client.key new file mode 100644 index 0000000..e5942d0 --- /dev/null +++ b/testdata/client.key @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIDxE+LU0KvWeyziyXEHG1WDP/Dk/8tVW1YEhtP3/x8BRoAoGCCqGSM49 +AwEHoUQDQgAEKRHWBQ/1pP+CuEe4e+GfxT5OmYT2WEqZwNtLGNpS1Ve1nSgmvvP7 +zYCtgMlr7I9IGc+X2uZn6FDBDQeikAGeIg== +-----END EC PRIVATE KEY----- diff --git a/testdata/client.pem b/testdata/client.pem new file mode 100644 index 0000000..983bf00 --- /dev/null +++ b/testdata/client.pem @@ -0,0 +1,10 @@ +-----BEGIN CERTIFICATE----- +MIIBfDCCASKgAwIBAgIBBDAKBggqhkjOPQQDAjAkMRAwDgYDVQQKEwdBY21lIENv +MRAwDgYDVQQDEwdSb290IENBMCAXDTIwMDQwMzExNTYzNFoYDzIxMjAwMzEwMTE1 +NjM0WjAyMRAwDgYDVQQKEwdBY21lIENvMR4wHAYDVQQDDBVjbGllbnRfYXV0aF90 +ZXN0X2NlcnQwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQpEdYFD/Wk/4K4R7h7 +4Z/FPk6ZhPZYSpnA20sY2lLVV7WdKCa+8/vNgK2AyWvsj0gZz5fa5mfoUMENB6KQ +AZ4iozUwMzAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwIwDAYD +VR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiEA+YWtXfDjGxoGlbhrLJ0n0g+5 +ELn0Wm2VAuuZ+to8C7wCIH7RCVa6bfawv/zKiR2QS+VukL39d3a0JVvUwKEdtrH7 +-----END CERTIFICATE----- diff --git a/testdata/key.pem b/testdata/key.pem deleted file mode 100644 index 113a87e..0000000 --- a/testdata/key.pem +++ /dev/null @@ -1,51 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -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 ------END RSA PRIVATE KEY----- diff --git a/testdata/leaf.debug.crt b/testdata/leaf.debug.crt new file mode 100644 index 0000000..5de8fc7 --- /dev/null +++ b/testdata/leaf.debug.crt @@ -0,0 +1,47 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 41:1d:fe:89:9c:0c:32:d8:56:1d:57:62:9c:92:53:b0 + Signature Algorithm: ecdsa-with-SHA256 + Issuer: O = Acme Co, CN = Root CA + Validity + Not Before: Apr 3 11:56:34 2020 GMT + Not After : Mar 10 11:56:34 2120 GMT + Subject: O = Acme Co, CN = test_cert_1 + Subject Public Key Info: + Public Key Algorithm: id-ecPublicKey + Public-Key: (256 bit) + pub: + 04:1b:cb:95:ee:e3:97:76:1b:34:d4:e7:7a:05:62: + 7a:ca:cc:c8:e8:87:11:b0:c8:ab:57:b7:c0:6a:31: + 26:dd:b5:5c:f4:46:8b:4d:b2:11:54:c3:74:f3:92: + 0d:9c:a0:48:9e:4d:3b:3c:08:ce:06:e4:ad:1a:b3: + 82:e0:7e:55:6b + ASN1 OID: prime256v1 + NIST CURVE: P-256 + X509v3 extensions: + X509v3 Key Usage: critical + Digital Signature, Key Encipherment + X509v3 Extended Key Usage: + TLS Web Server Authentication + X509v3 Basic Constraints: critical + CA:FALSE + X509v3 Subject Alternative Name: + DNS:localhost, IP Address:127.0.0.1 + Signature Algorithm: ecdsa-with-SHA256 + 30:45:02:20:4d:a9:21:f8:a6:ae:ee:53:1b:6d:9c:1a:18:a7: + ee:d5:d9:2c:71:e1:7d:66:84:ce:8f:cc:0a:47:4b:d4:53:dc: + 02:21:00:8a:26:d8:48:7a:29:a6:c7:ff:db:2f:69:28:5a:88: + b8:7b:dd:cb:bc:5b:f0:7c:ca:14:4a:f6:d0:ad:91:9b:55 +-----BEGIN CERTIFICATE----- +MIIBnTCCAUOgAwIBAgIQQR3+iZwMMthWHVdinJJTsDAKBggqhkjOPQQDAjAkMRAw +DgYDVQQKEwdBY21lIENvMRAwDgYDVQQDEwdSb290IENBMCAXDTIwMDQwMzExNTYz +NFoYDzIxMjAwMzEwMTE1NjM0WjAoMRAwDgYDVQQKEwdBY21lIENvMRQwEgYDVQQD +DAt0ZXN0X2NlcnRfMTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABBvLle7jl3Yb +NNTnegViesrMyOiHEbDIq1e3wGoxJt21XPRGi02yEVTDdPOSDZygSJ5NOzwIzgbk +rRqzguB+VWujUTBPMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcD +ATAMBgNVHRMBAf8EAjAAMBoGA1UdEQQTMBGCCWxvY2FsaG9zdIcEfwAAATAKBggq +hkjOPQQDAgNIADBFAiBNqSH4pq7uUxttnBoYp+7V2Sxx4X1mhM6PzApHS9RT3AIh +AIom2Eh6KabH/9svaShaiLh73cu8W/B8yhRK9tCtkZtV +-----END CERTIFICATE----- diff --git a/testdata/leaf.key b/testdata/leaf.key new file mode 100644 index 0000000..fc829b4 --- /dev/null +++ b/testdata/leaf.key @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIJR5CixdEgUd/xXjwuI6P7dEepdUBsEtBBu340YS9W81oAoGCCqGSM49 +AwEHoUQDQgAEG8uV7uOXdhs01Od6BWJ6yszI6IcRsMirV7fAajEm3bVc9EaLTbIR +VMN085INnKBInk07PAjOBuStGrOC4H5Vaw== +-----END EC PRIVATE KEY----- diff --git a/testdata/leaf.pem b/testdata/leaf.pem new file mode 100644 index 0000000..f740a6b --- /dev/null +++ b/testdata/leaf.pem @@ -0,0 +1,11 @@ +-----BEGIN CERTIFICATE----- +MIIBnTCCAUOgAwIBAgIQQR3+iZwMMthWHVdinJJTsDAKBggqhkjOPQQDAjAkMRAw +DgYDVQQKEwdBY21lIENvMRAwDgYDVQQDEwdSb290IENBMCAXDTIwMDQwMzExNTYz +NFoYDzIxMjAwMzEwMTE1NjM0WjAoMRAwDgYDVQQKEwdBY21lIENvMRQwEgYDVQQD +DAt0ZXN0X2NlcnRfMTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABBvLle7jl3Yb +NNTnegViesrMyOiHEbDIq1e3wGoxJt21XPRGi02yEVTDdPOSDZygSJ5NOzwIzgbk +rRqzguB+VWujUTBPMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcD +ATAMBgNVHRMBAf8EAjAAMBoGA1UdEQQTMBGCCWxvY2FsaG9zdIcEfwAAATAKBggq +hkjOPQQDAgNIADBFAiBNqSH4pq7uUxttnBoYp+7V2Sxx4X1mhM6PzApHS9RT3AIh +AIom2Eh6KabH/9svaShaiLh73cu8W/B8yhRK9tCtkZtV +-----END CERTIFICATE----- diff --git a/testdata/root.debug.crt b/testdata/root.debug.crt new file mode 100644 index 0000000..bd757e5 --- /dev/null +++ b/testdata/root.debug.crt @@ -0,0 +1,45 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 18:5f:e2:b0:ce:76:c6:8b:5f:f7:0c:17:dc:76:da:19 + Signature Algorithm: ecdsa-with-SHA256 + Issuer: O = Acme Co, CN = Root CA + Validity + Not Before: Apr 3 11:56:34 2020 GMT + Not After : Mar 10 11:56:34 2120 GMT + Subject: O = Acme Co, CN = Root CA + Subject Public Key Info: + Public Key Algorithm: id-ecPublicKey + Public-Key: (256 bit) + pub: + 04:0c:6a:d9:96:25:9f:f7:ae:42:d6:42:87:d8:86: + 74:73:42:a9:11:43:f2:1f:1f:8b:33:27:2f:3c:b5: + 09:6f:d2:5b:45:55:2c:c3:a9:b3:c5:ce:ae:83:0f: + 0d:c5:26:10:64:78:a3:60:93:b8:a6:6d:0f:32:3e: + a3:b8:0a:d7:5f + ASN1 OID: prime256v1 + NIST CURVE: P-256 + X509v3 extensions: + X509v3 Key Usage: critical + Certificate Sign + X509v3 Extended Key Usage: + TLS Web Server Authentication + X509v3 Basic Constraints: critical + CA:TRUE + Signature Algorithm: ecdsa-with-SHA256 + 30:45:02:20:15:80:3b:4e:30:4c:d4:46:fa:d9:bd:a2:4a:1d: + 30:df:24:fb:3e:00:17:aa:12:5c:82:93:1c:83:04:e8:6e:05: + 02:21:00:92:89:32:81:03:3e:0a:7c:61:13:f1:32:79:d7:53: + b2:d1:de:3d:b8:c7:e7:6a:05:61:db:2b:a1:e7:0b:9d:0c +-----BEGIN CERTIFICATE----- +MIIBgDCCASagAwIBAgIQGF/isM52xotf9wwX3HbaGTAKBggqhkjOPQQDAjAkMRAw +DgYDVQQKEwdBY21lIENvMRAwDgYDVQQDEwdSb290IENBMCAXDTIwMDQwMzExNTYz +NFoYDzIxMjAwMzEwMTE1NjM0WjAkMRAwDgYDVQQKEwdBY21lIENvMRAwDgYDVQQD +EwdSb290IENBMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEDGrZliWf965C1kKH +2IZ0c0KpEUPyHx+LMycvPLUJb9JbRVUsw6mzxc6ugw8NxSYQZHijYJO4pm0PMj6j +uArXX6M4MDYwDgYDVR0PAQH/BAQDAgIEMBMGA1UdJQQMMAoGCCsGAQUFBwMBMA8G +A1UdEwEB/wQFMAMBAf8wCgYIKoZIzj0EAwIDSAAwRQIgFYA7TjBM1Eb62b2iSh0w +3yT7PgAXqhJcgpMcgwTobgUCIQCSiTKBAz4KfGET8TJ511Oy0d49uMfnagVh2yuh +5wudDA== +-----END CERTIFICATE----- diff --git a/testdata/root.key b/testdata/root.key new file mode 100644 index 0000000..0576365 --- /dev/null +++ b/testdata/root.key @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIJgtDAjzxGibF9c2tdx0CwwUuVzxzwPacyH2RwVEkpULoAoGCCqGSM49 +AwEHoUQDQgAEDGrZliWf965C1kKH2IZ0c0KpEUPyHx+LMycvPLUJb9JbRVUsw6mz +xc6ugw8NxSYQZHijYJO4pm0PMj6juArXXw== +-----END EC PRIVATE KEY----- diff --git a/testdata/root.pem b/testdata/root.pem new file mode 100644 index 0000000..bca84a0 --- /dev/null +++ b/testdata/root.pem @@ -0,0 +1,11 @@ +-----BEGIN CERTIFICATE----- +MIIBgDCCASagAwIBAgIQGF/isM52xotf9wwX3HbaGTAKBggqhkjOPQQDAjAkMRAw +DgYDVQQKEwdBY21lIENvMRAwDgYDVQQDEwdSb290IENBMCAXDTIwMDQwMzExNTYz +NFoYDzIxMjAwMzEwMTE1NjM0WjAkMRAwDgYDVQQKEwdBY21lIENvMRAwDgYDVQQD +EwdSb290IENBMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEDGrZliWf965C1kKH +2IZ0c0KpEUPyHx+LMycvPLUJb9JbRVUsw6mzxc6ugw8NxSYQZHijYJO4pm0PMj6j +uArXX6M4MDYwDgYDVR0PAQH/BAQDAgIEMBMGA1UdJQQMMAoGCCsGAQUFBwMBMA8G +A1UdEwEB/wQFMAMBAf8wCgYIKoZIzj0EAwIDSAAwRQIgFYA7TjBM1Eb62b2iSh0w +3yT7PgAXqhJcgpMcgwTobgUCIQCSiTKBAz4KfGET8TJ511Oy0d49uMfnagVh2yuh +5wudDA== +-----END CERTIFICATE----- diff --git a/testdata/server.pem b/testdata/server.pem deleted file mode 100644 index 46bc913..0000000 --- a/testdata/server.pem +++ /dev/null @@ -1,31 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFQTCCAymgAwIBAgIJAO+k4G7bNTyoMA0GCSqGSIb3DQEBCwUAMIGLMQswCQYD -VQQGEwJVUzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xEzAR -BgNVBAoTCkFwY2VyYSBJbmMxEDAOBgNVBAsTB25hdHMuaW8xEjAQBgNVBAMTCWxv -Y2FsaG9zdDEcMBoGCSqGSIb3DQEJARYNZGVyZWtAbmF0cy5pbzAeFw0xNTExMDUy -MzA2MzRaFw0xOTExMDQyMzA2MzRaMBQxEjAQBgNVBAMTCWxvY2FsaG9zdDCCAiIw -DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALYBy3IEY3kqlf5h2vEtk9CB4Vnt -AD+eaVc3Y9xuFHJ4k0ScsjwrYH3YcwQW0fDpOQCI0102YoQT7tPCBT+rC0w1mM82 -0ZSKS/y2SIK9cM6LHWkUbQcWfeaL+uz2HB3jTEm8tmEEFTLBJFMbMpUsvjA5GLqG -URswsNjYEl8M9wS1BETw2e+eCFa4wxq9oGHp/Dgh0vZglHzQL5osEpRct1aaQo6O -jWzZc1Cgx4SxmMOoMWF8BQzlO7aikbZEJxk03TIFNph/azJt7mviMseW72mP+bX9 -sm/8bsINiYgJMM2HAyjIgFVMXX8AYfEFC4wozYloLqn0yy9TdjhyGbsUjg0yTd4l -A9LkGKroBdY1drPSek5Nj9br27UGGGfU2ddAD5xYBIeeFY+3nqST868oIXB/m1P7 -1p8tpkgujx/RqKr3nvOqBHizmoQaWZsPC3X/Jc4NvVHihpuNzN/u1D5mxGhxsx+R -qnrIkhS0IqNrokggPZazugmHntd95HgTb3JpjY3RGEYXAQNr+mZGUCc+CVu0mhFX -xAMZcfVp5nDg4hKHiaRv0KcaqBmnn8AB5w5FiTppzUbRP0zz7GkwrdulwR6c2Eb5 -75+/022TbgCx8B9SH4zJRTj5mtrK56eFgTcnuXB+YnWaP7/7qmKIZzxrd3UDvnza -bhnMiiIK7vL8qiOTAgMBAAGjHjAcMBoGA1UdEQQTMBGCCWxvY2FsaG9zdIcEfwAA -ATANBgkqhkiG9w0BAQsFAAOCAgEAOrh8XfW6quwBAcCxHf6/uvu/iNq4yHCg2qH6 -VtWs/x38I2t3BRSNsLsJGieh6yLlZDzOus+XYui4uDE50XmcwaIsY0VcXnvdyZVZ -w9+lMyfp00kRF1o3B6eVxq0pRE5VB0cai7XI7tyfpRwGzA+oNLF4vBvxAHm9Ony5 -Q57DC/HFzyUogdkMYciO/kd9oa4HosDEXwaE8UvZUL8OVl/dptMXLL/GGwzZsUAE -1sLAbgm044YChLUDzgBAtDTkB/HNkcPzSKwULuskhe7ndoaEQNXVZuP7quGiZ/W1 -1lE59gnmnyG8ySFCL05jHrKLtFAJe88gQjgDK65ZJv4W/k7ocmT+HhCxWyQWcX6v -abJ0EssqeSQuzRMuZebMJJ8s46d6RcYuMdIX3RDXq+1moJDFopE7lgNrlRhWgaky -Og8f/u8s1j75tk1YaYcY9uBKjKk7f681R9wMumkd6IEmEvkUwHNFsctxi4fGI7h1 -PRdKL0DlhVmnpHlKs6Kvm2sJ3twSAGSrC4u0LuxACeR3XbiBfyhFV/291LSuw/y1 -JtWOW5koh0g1k9xtkiu3/ePVdG/CLp796IyRhdB1jP/vD7W5RLLG/VAlomfjsPsB -AnwFYbVZ8KrmMKYUpTJOH31CRzFdOB6nWqXu5tk3nOtLKo1nIOuVtmp9XLz3VtHe -NiZPnqA= ------END CERTIFICATE-----