diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 00000000..b412d204 --- /dev/null +++ b/.drone.yml @@ -0,0 +1,69 @@ +kind: pipeline +type: exec +name: exec-runner + +platform: + os: linux + arch: amd64 + +steps: + - name: integration-tests + environment: + commands: + - make clean + - make build lint + - make test + - make integration + when: + event: + - pull_request + - push + +--- +kind: pipeline +type: docker +name: docker-runner + +platform: + os: linux + arch: amd64 + +steps: + - name: prepare-release + image: golang:1.18 + commands: + - | + go build \ + -o build/containerpilot \ + -ldflags \ + "-X github.com/greenbaum/containerpilot/version.GitHash=$(git rev-parse --short HEAD) \ + -X github.com/greenbaum/containerpilot/version.Version=${DRONE_TAG}" + - mkdir release && cd build && tar -czf ../release/containerpilot-${DRONE_TAG}.tar.gz containerpilot + - cd ../release && sha1sum containerpilot-${DRONE_TAG}.tar.gz > containerpilot-${DRONE_TAG}.sha1.txt + when: + event: + - tag + + - name: publish-release + image: plugins/github-release + settings: + api_key: + from_secret: github_api-key + files: + - release/containerpilot-${DRONE_TAG}.tar.gz + - release/containerpilot-${DRONE_TAG}.sha1.txt + title: "New release: version ${DRONE_TAG}" + base_url: greenbaum/containerpilot + when: + event: + - tag + depends_on: + - prepare-release + +--- +kind: secret +name: github_api-key +get: + path: secret/data/drone/github + name: api-key + diff --git a/.envrc b/.envrc new file mode 100644 index 00000000..b027ddb2 --- /dev/null +++ b/.envrc @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +# ^ added for shellcheck and file-type detection + +# Watch & reload direnv on change +cd devshell || exit +watch_file devshell.toml + +if [[ $(type -t use_flake) != function ]]; then + echo "ERROR: use_flake function missing." + echo "Please update direnv to v2.30.0 or later." + exit 1 +fi +use flake diff --git a/.gitignore b/.gitignore index f0de52cd..f6a2e888 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,6 @@ cover.html # IDE files .idea *.iml + +# Nix files +.direnv diff --git a/Dockerfile b/Dockerfile index fa856d66..26340d00 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,15 +1,20 @@ -FROM golang:1.9 +FROM golang:1.19 -ENV CONSUL_VERSION=1.0.0 -ENV GLIDE_VERSION=0.12.3 +ENV CONSUL_VERSION=1.13.3 RUN apt-get update \ && apt-get install -y unzip \ - && go get github.com/golang/lint/golint \ - && curl -Lo /tmp/glide.tgz "https://github.com/Masterminds/glide/releases/download/v${GLIDE_VERSION}/glide-v${GLIDE_VERSION}-linux-amd64.tar.gz" \ - && tar -C /usr/bin -xzf /tmp/glide.tgz --strip=1 linux-amd64/glide \ - && curl --fail -Lso consul.zip "https://releases.hashicorp.com/consul/${CONSUL_VERSION}/consul_${CONSUL_VERSION}_linux_amd64.zip" \ - && unzip consul.zip -d /usr/bin + && go install honnef.co/go/tools/cmd/staticcheck@latest + + +RUN export CONSUL_CHECKSUM=5370b0b5bf765530e28cb80f90dcb47bd7d6ba78176c1ab2430f56e460ed279c \ + && export archive=consul_${CONSUL_VERSION}_linux_amd64.zip \ + && curl -Lso /tmp/${archive} https://releases.hashicorp.com/consul/${CONSUL_VERSION}/${archive} \ + && echo "${CONSUL_CHECKSUM} /tmp/${archive}" | sha256sum -c \ + && cd /bin \ + && unzip /tmp/${archive} \ + && chmod +x /bin/consul \ + && rm /tmp/${archive} ENV CGO_ENABLED 0 -ENV GOPATH /go:/cp +ENV GOPATH /go diff --git a/README.md b/README.md index d2baf18f..a4ada547 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ *An init system for cloud-native distributed applications that automates the process of service discovery, configuration, and lifecycle management inside the container, so you can focus on your apps.* -[![Build Status](https://travis-ci.org/joyent/containerpilot.svg)](https://travis-ci.org/joyent/containerpilot) +[![Build Status](https://drone.greenbaum.cloud/api/badges/greenbaum.cloud/containerpilot/status.svg)](https://drone.greenbaum.cloud/greenbaum.cloud/containerpilot) [![MPL licensed](https://img.shields.io/badge/license-MPL_2.0-blue.svg)](https://github.com/joyent/containerpilot/blob/master/LICENSE) [![GoDoc](https://godoc.org/github.com/joyent/containerpilot?status.svg)](https://godoc.org/github.com/joyent/containerpilot) diff --git a/client/client.go b/client/client.go index 267257b9..12d61a73 100644 --- a/client/client.go +++ b/client/client.go @@ -14,7 +14,8 @@ import ( // requests out to a ContainerPilot process's control socket. type HTTPClient struct { http.Client - socketPath string + // staticcheck U1000 field is unused + //socketPath string } var socketType = "unix" diff --git a/commands/commands.go b/commands/commands.go index be83ede9..e368d458 100644 --- a/commands/commands.go +++ b/commands/commands.go @@ -30,6 +30,8 @@ type Command struct { logger log.Entry lock *sync.Mutex fields log.Fields + UID int + GID int } // NewCommand parses JSON config into a Command @@ -101,7 +103,20 @@ func (c *Command) Run(pctx context.Context, bus *events.EventBus) { cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr } + cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} + if os.Getuid() == 0 { + if c.UID != 0 && c.GID != 0 { + cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(c.UID), Gid: uint32(c.GID)} + } else if c.UID != 0 { + cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(c.UID)} + } else if c.GID != 0 { + cmd.SysProcAttr.Credential = &syscall.Credential{Gid: uint32(c.GID)} + } + } else { + log.Debugf("%s.Skipping uid and gid (ContainerPilot is not running as root)", c.Name) + } + c.Cmd = cmd ctx, cancel := getContext(pctx, c.Timeout) @@ -126,8 +141,8 @@ func (c *Command) Run(pctx context.Context, bus *events.EventBus) { defer log.Debugf("%s.Run end", c.Name) if err := c.Cmd.Start(); err != nil { log.Errorf("unable to start %s: %v", c.Name, err) - bus.Publish(events.Event{events.ExitFailed, c.Name}) - bus.Publish(events.Event{events.Error, err.Error()}) + bus.Publish(events.Event{Code: events.ExitFailed, Source: c.Name}) + bus.Publish(events.Event{Code: events.Error, Source: err.Error()}) return } @@ -150,12 +165,12 @@ func (c *Command) Run(pctx context.Context, bus *events.EventBus) { // we'll return from Wait() and publish events if err := c.Cmd.Wait(); err != nil { log.Errorf("%s exited with error: %v", c.Name, err) - bus.Publish(events.Event{events.ExitFailed, c.Name}) - bus.Publish(events.Event{events.Error, - fmt.Errorf("%s: %s", c.Name, err).Error()}) + bus.Publish(events.Event{Code: events.ExitFailed, Source: c.Name}) + bus.Publish(events.Event{Code: events.Error, + Source: fmt.Errorf("%s: %s", c.Name, err).Error()}) } else { log.Debugf("%s exited without error", c.Name) - bus.Publish(events.Event{events.ExitSuccess, c.Name}) + bus.Publish(events.Event{Code: events.ExitSuccess, Source: c.Name}) } }() } diff --git a/commands/commands_test.go b/commands/commands_test.go index 039134d2..b481a4f1 100644 --- a/commands/commands_test.go +++ b/commands/commands_test.go @@ -15,7 +15,7 @@ import ( func TestCommandRunWithTimeoutZero(t *testing.T) { cmd, _ := NewCommand("sleep 2", time.Duration(0), nil) got := runtestCommandRun(cmd) - timedout := events.Event{events.ExitFailed, "sleep"} + timedout := events.Event{Code: events.ExitFailed, Source: "sleep"} if got[timedout] != 1 { t.Fatalf("stopped command prior to test timeout, got events %v", got) } @@ -25,9 +25,9 @@ func TestCommandRunWithTimeoutKilled(t *testing.T) { cmd, _ := NewCommand("sleep 2", time.Duration(100*time.Millisecond), nil) cmd.Name = t.Name() got := runtestCommandRun(cmd) - testTimeout := events.Event{events.TimerExpired, "DebugSubscriberTimeout"} - expired := events.Event{events.ExitFailed, t.Name()} - errMsg := events.Event{events.Error, fmt.Sprintf("%s: signal: killed", cmd.Name)} + testTimeout := events.Event{Code: events.TimerExpired, Source: "DebugSubscriberTimeout"} + expired := events.Event{Code: events.ExitFailed, Source: t.Name()} + errMsg := events.Event{Code: events.Error, Source: fmt.Sprintf("%s: signal: killed", cmd.Name)} if got[testTimeout] > 0 || got[expired] != 1 || got[errMsg] != 1 { t.Fatalf("expected:\n%v\n%v\ngot events:\n%v", expired, errMsg, got) } @@ -38,9 +38,9 @@ func TestCommandRunChildrenKilled(t *testing.T) { time.Duration(100*time.Millisecond), nil) cmd.Name = t.Name() got := runtestCommandRun(cmd) - testTimeout := events.Event{events.TimerExpired, "DebugSubscriberTimeout"} - expired := events.Event{events.ExitFailed, t.Name()} - errMsg := events.Event{events.Error, fmt.Sprintf("%s: signal: killed", cmd.Name)} + testTimeout := events.Event{Code: events.TimerExpired, Source: "DebugSubscriberTimeout"} + expired := events.Event{Code: events.ExitFailed, Source: t.Name()} + errMsg := events.Event{Code: events.Error, Source: fmt.Sprintf("%s: signal: killed", cmd.Name)} if got[testTimeout] > 0 || got[expired] != 1 || got[errMsg] != 1 { t.Fatalf("expected:\n%v\n%v\ngot events:\n%v", expired, errMsg, got) } @@ -49,8 +49,8 @@ func TestCommandRunChildrenKilled(t *testing.T) { func TestCommandRunExecFailed(t *testing.T) { cmd, _ := NewCommand("./testdata/test.sh failStuff --debug", time.Duration(0), nil) got := runtestCommandRun(cmd) - failed := events.Event{events.ExitFailed, "./testdata/test.sh"} - errMsg := events.Event{events.Error, "./testdata/test.sh: exit status 255"} + failed := events.Event{Code: events.ExitFailed, Source: "./testdata/test.sh"} + errMsg := events.Event{Code: events.Error, Source: "./testdata/test.sh: exit status 255"} if got[failed] != 1 || got[errMsg] != 1 { t.Fatalf("expected:\n%v\n%v\ngot events:\n%v", failed, errMsg, got) } @@ -59,9 +59,9 @@ func TestCommandRunExecFailed(t *testing.T) { func TestCommandRunExecInvalid(t *testing.T) { cmd, _ := NewCommand("./testdata/invalidCommand", time.Duration(0), nil) got := runtestCommandRun(cmd) - failed := events.Event{events.ExitFailed, "./testdata/invalidCommand"} - errMsg := events.Event{events.Error, - "fork/exec ./testdata/invalidCommand: no such file or directory"} + failed := events.Event{Code: events.ExitFailed, Source: "./testdata/invalidCommand"} + errMsg := events.Event{Code: events.Error, + Source: "fork/exec ./testdata/invalidCommand: no such file or directory"} if got[failed] != 1 || got[errMsg] != 1 { t.Fatalf("expected:\n%v\n%v\ngot events:\n%v", failed, errMsg, got) } diff --git a/commands/testdata/test.sh b/commands/testdata/test.sh index 51c69222..3015caf9 100755 --- a/commands/testdata/test.sh +++ b/commands/testdata/test.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash trap 'exit 2' SIGTERM diff --git a/config/config.go b/config/config.go index 676b3ade..7e220567 100644 --- a/config/config.go +++ b/config/config.go @@ -6,7 +6,7 @@ import ( "bytes" "errors" "fmt" - "io/ioutil" + "os" "strings" "github.com/flynn/json5" @@ -79,7 +79,7 @@ func RenderConfig(configFlag, renderFlag string) error { fmt.Printf("%s", renderedConfig) } else { var err error - if err = ioutil.WriteFile(renderFlag, renderedConfig, 0644); err != nil { + if err = os.WriteFile(renderFlag, renderedConfig, 0644); err != nil { return fmt.Errorf("could not write config file: %s", err) } } @@ -108,7 +108,7 @@ func loadConfigFile(configFlag string) ([]byte, error) { if configFlag == "" { return nil, errors.New("-config flag is required") } - data, err := ioutil.ReadFile(configFlag) + data, err := os.ReadFile(configFlag) if err != nil { return nil, fmt.Errorf("could not read config file: %s", err) } diff --git a/config/config_test.go b/config/config_test.go index 89b352ae..f7f99472 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -1,7 +1,6 @@ package config import ( - "io/ioutil" "os" "path/filepath" "testing" @@ -33,6 +32,7 @@ func TestValidConfigJobs(t *testing.T) { assert.Equal(job0.Port, 8080, "config for job0.Port") assert.Equal(job0.Exec, "/bin/serviceA", "config for job0.Exec") assert.Equal(job0.Tags, []string{"tag1", "tag2"}, "config for job0.Tags") + assert.Equal(job0.Meta, map[string]string{"keyA": "A"}, "config for job0.Meta") assert.Equal(job0.Restarts, nil, "config for job1.Restarts") job1 := cfg.Jobs[1] @@ -184,8 +184,8 @@ func TestRenderConfigFileStdout(t *testing.T) { temp.Close() os.Stdout = old - renderedOut, _ := ioutil.ReadFile(fname) - renderedFile, _ := ioutil.ReadFile("testJSON.json") + renderedOut, _ := os.ReadFile(fname) + renderedFile, _ := os.ReadFile("testJSON.json") if string(renderedOut) != string(renderedFile) { t.Fatalf("expected the rendered file and stdout to be identical") } diff --git a/config/decode/decode.go b/config/decode/decode.go index f3a8fab4..572fea94 100644 --- a/config/decode/decode.go +++ b/config/decode/decode.go @@ -71,7 +71,7 @@ func interfaceToString(raw interface{}) string { } func interfaceToStringArray(rawArray []interface{}) []string { - if rawArray == nil || len(rawArray) == 0 { + if len(rawArray) == 0 { return nil } var stringArray []string diff --git a/config/logger/logging.go b/config/logger/logging.go index d593cc6c..0c3352b6 100644 --- a/config/logger/logging.go +++ b/config/logger/logging.go @@ -49,7 +49,7 @@ func (l *Config) Init() error { } level, err := logrus.ParseLevel(strings.ToLower(l.Level)) if err != nil { - return fmt.Errorf("Unknown log level '%s': %s", l.Level, err) + return fmt.Errorf("unknown log level '%s': %s", l.Level, err) } var formatter logrus.Formatter var output io.Writer @@ -65,7 +65,7 @@ func (l *Config) Init() error { TimestampFormat: time.RFC3339Nano, } default: - return fmt.Errorf("Unknown log format '%s'", l.Format) + return fmt.Errorf("unknown log format '%s'", l.Format) } switch strings.ToLower(l.Output) { case "stderr": @@ -73,11 +73,11 @@ func (l *Config) Init() error { case "stdout": output = os.Stdout case "": - return fmt.Errorf("Unknown output type '%s'", l.Output) + return fmt.Errorf("unknown output type '%s'", l.Output) default: f, err := reopen.NewFileWriter(l.Output) if err != nil { - return fmt.Errorf("Error initializing log file '%s': %s", l.Output, err) + return fmt.Errorf("error initializing log file '%s': %s", l.Output, err) } initializeSignal(f) output = f diff --git a/config/logger/logging_test.go b/config/logger/logging_test.go index 393fe475..c69372dc 100644 --- a/config/logger/logging_test.go +++ b/config/logger/logging_test.go @@ -1,7 +1,6 @@ package logger import ( - "io/ioutil" "os" "reflect" "strings" @@ -84,7 +83,7 @@ func TestFileLogger(t *testing.T) { // write a log message logMsg := "this is a test" logrus.Info(logMsg) - content, err := ioutil.ReadFile(filename) + content, err := os.ReadFile(filename) if err != nil { t.Errorf("Did not expect error: %v", err) } diff --git a/config/services/ips.go b/config/services/ips.go index 7c652e89..59854fa5 100644 --- a/config/services/ips.go +++ b/config/services/ips.go @@ -30,7 +30,7 @@ func IPFromInterfaces(raw interface{}) (string, error) { // GetIP determines the IP address of the container func GetIP(specList []string) (string, error) { - if specList == nil || len(specList) == 0 { + if len(specList) == 0 { // Use a sane default specList = []string{"eth0:inet", "inet"} } @@ -191,7 +191,7 @@ func parseInterfaceSpec(spec string) (interfaceSpec, error) { if _, err := strconv.Atoi(ip[1]); err != nil { nip := net.ParseIP(ip[1]) if nip == nil { - return nil, fmt.Errorf("Unable to parse static ip %s in %s", ip[0], spec) + return nil, fmt.Errorf("unable to parse static ip %s in %s", ip[0], spec) } return staticInterfaceSpec{Spec: spec, Name: "static", IP: nip}, nil } @@ -204,7 +204,7 @@ func parseInterfaceSpec(spec string) (interfaceSpec, error) { if index != "" { i, err := strconv.Atoi(index) if err != nil { - return nil, fmt.Errorf("Unable to parse index %s in %s", index, spec) + return nil, fmt.Errorf("unable to parse index %s in %s", index, spec) } return indexInterfaceSpec{Spec: spec, Name: name, Index: i}, nil } @@ -219,7 +219,7 @@ func parseInterfaceSpec(spec string) (interfaceSpec, error) { if _, net, err := net.ParseCIDR(spec); err == nil { return cidrInterfaceSpec{Spec: spec, Network: net}, nil } - return nil, fmt.Errorf("Unable to parse interface spec: %s", spec) + return nil, fmt.Errorf("unable to parse interface spec: %s", spec) } type interfaceIP struct { diff --git a/config/services/ips_test.go b/config/services/ips_test.go index 82b5f381..d5b53752 100644 --- a/config/services/ips_test.go +++ b/config/services/ips_test.go @@ -48,7 +48,7 @@ func TestGetIp(t *testing.T) { ip, _ := GetIP([]string{lo, "inet"}) assert.Equal(t, "127.0.0.1", ip, "expected to find loopback IP") - ip, err := GetIP([]string{"interface-does-not-exist"}) + _, err := GetIP([]string{"interface-does-not-exist"}) assert.Error(t, err, "expected interface not found, but instead got an IP") ip, _ = GetIP([]string{"static:192.168.1.100", lo}) diff --git a/config/template/template.go b/config/template/template.go index bad15d9b..1edeb742 100644 --- a/config/template/template.go +++ b/config/template/template.go @@ -65,9 +65,9 @@ func envFunc(env string) string { } func ensureInt(intv interface{}) (int, error) { - switch intv.(type) { + switch intv := intv.(type) { case string: - ret, err := strconv.Atoi(intv.(string)) + ret, err := strconv.Atoi(intv) if err != nil { return 0, err } diff --git a/config/testdata/test.json5 b/config/testdata/test.json5 index 70c912ae..b2b1bfce 100644 --- a/config/testdata/test.json5 +++ b/config/testdata/test.json5 @@ -18,7 +18,10 @@ interval: 19, ttl: 30, }, - tags: ["tag1","tag2"] + tags: ["tag1","tag2"], + meta: { + keyA: "A", + } }, { name: "serviceB", diff --git a/config/timing/duration_test.go b/config/timing/duration_test.go index 8bac52de..730ea3d1 100644 --- a/config/timing/duration_test.go +++ b/config/timing/duration_test.go @@ -20,7 +20,7 @@ func TestGetTimeout(t *testing.T) { dur, err = GetTimeout("x") expectDurationCompare(t, dur, time.Duration(0), - err, errors.New("time: invalid duration x")) + err, errors.New("time: invalid duration \"x\"")) dur, err = GetTimeout("0") expectDurationCompare(t, dur, time.Duration(0), err, nil) @@ -67,7 +67,7 @@ func TestParseDuration(t *testing.T) { // Some parse errors expectError(t, "asf", "invalid duration") - expectError(t, "20yy", "unknown unit yy") + expectError(t, "20yy", "time: unknown unit \"yy\" in duration \"20yy\"") // Fractional expectError(t, 10.10, "unexpected duration of type float") diff --git a/control/control.go b/control/control.go index 7379508b..66bab982 100644 --- a/control/control.go +++ b/control/control.go @@ -81,7 +81,6 @@ func (srv *HTTPServer) Run(pctx context.Context, bus *events.EventBus) { go func() { defer srv.Stop() <-ctx.Done() - return }() } diff --git a/control/endpoints.go b/control/endpoints.go index 7119318f..d56be395 100644 --- a/control/endpoints.go +++ b/control/endpoints.go @@ -5,7 +5,6 @@ import ( "encoding/json" "fmt" "io" - "io/ioutil" "net/http" "os" "strconv" @@ -56,7 +55,7 @@ func (pw PostHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { // process. Returns empty response or HTTP422. func (e Endpoints) PutEnviron(r *http.Request) (interface{}, int) { var postEnv map[string]string - jsonBlob, err := ioutil.ReadAll(r.Body) + jsonBlob, err := io.ReadAll(r.Body) defer r.Body.Close() if err != nil { return nil, http.StatusUnprocessableEntity @@ -110,7 +109,7 @@ func (e Endpoints) PostDisableMaintenanceMode(r *http.Request) (interface{}, int // Returns empty response or HTTP422. func (e Endpoints) PostMetric(r *http.Request) (interface{}, int) { var postMetrics map[string]interface{} - jsonBlob, err := ioutil.ReadAll(r.Body) + jsonBlob, err := io.ReadAll(r.Body) defer r.Body.Close() if err != nil { @@ -123,7 +122,7 @@ func (e Endpoints) PostMetric(r *http.Request) (interface{}, int) { } for metricKey, metricValue := range postMetrics { eventVal := fmt.Sprintf("%v|%v", metricKey, metricValue) - e.bus.Publish(events.Event{events.Metric, eventVal}) + e.bus.Publish(events.Event{Code: events.Metric, Source: eventVal}) } return nil, http.StatusOK } diff --git a/control/endpoints_test.go b/control/endpoints_test.go index 97b40102..e6444fab 100644 --- a/control/endpoints_test.go +++ b/control/endpoints_test.go @@ -3,7 +3,7 @@ package control import ( "context" "fmt" - "io/ioutil" + "io" "net/http" "net/http/httptest" "os" @@ -65,7 +65,7 @@ func TestPostHandler(t *testing.T) { ph.ServeHTTP(w, req) resp := w.Result() defer resp.Body.Close() - body, _ := ioutil.ReadAll(resp.Body) + body, _ := io.ReadAll(resp.Body) status := resp.StatusCode return status, string(body) } @@ -130,15 +130,15 @@ func TestPostMetric(t *testing.T) { }) t.Run("POST value", func(t *testing.T) { body := "{\"mymetric\": 1.0}" - expected := map[events.Event]int{{events.Metric, "mymetric|1"}: 1} + expected := map[events.Event]int{{Code: events.Metric, Source: "mymetric|1"}: 1} status := testFunc(t, expected, body) assert.Equal(t, http.StatusOK, status, "status was not 200OK") }) t.Run("POST multi-metric", func(t *testing.T) { body := "{\"mymetric\": 1.5, \"myothermetric\": 2}" status := testFunc(t, map[events.Event]int{ - {events.Metric, "mymetric|1.5"}: 1, - {events.Metric, "myothermetric|2"}: 1, + {Code: events.Metric, Source: "mymetric|1.5"}: 1, + {Code: events.Metric, Source: "myothermetric|2"}: 1, }, body) assert.Equal(t, http.StatusOK, status, "status was not 200OK") }) diff --git a/core/app_test.go b/core/app_test.go index 66111814..c8d18109 100644 --- a/core/app_test.go +++ b/core/app_test.go @@ -2,7 +2,6 @@ package core import ( "fmt" - "io/ioutil" "os" "strings" "testing" @@ -152,7 +151,7 @@ func TestReloadConfig(t *testing.T) { // write the configuration to a tempfile. caller is responsible // for calling 'defer os.Remove(f.Name())' when done func testCfgToTempFile(t *testing.T, text string) *os.File { - f, err := ioutil.TempFile(".", "test-") + f, err := os.CreateTemp(".", "test-") if err != nil { t.Fatal(err) } diff --git a/core/testdata/test.sh b/core/testdata/test.sh index c05a728d..68b2e59b 100755 --- a/core/testdata/test.sh +++ b/core/testdata/test.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash trap 'exit 2' SIGTERM diff --git a/devshell/.gitignore b/devshell/.gitignore new file mode 100644 index 00000000..04744984 --- /dev/null +++ b/devshell/.gitignore @@ -0,0 +1 @@ +/.direnv/ \ No newline at end of file diff --git a/devshell/devshell.toml b/devshell/devshell.toml new file mode 100644 index 00000000..4b8c8bff --- /dev/null +++ b/devshell/devshell.toml @@ -0,0 +1,4 @@ +# https://numtide.github.io/devshell +[[commands]] +package = "devshell.cli" +help = "Per project developer environments" diff --git a/devshell/flake.lock b/devshell/flake.lock new file mode 100644 index 00000000..4fd012a9 --- /dev/null +++ b/devshell/flake.lock @@ -0,0 +1,92 @@ +{ + "nodes": { + "devshell": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + }, + "locked": { + "lastModified": 1666548262, + "narHash": "sha256-4DyN4KXqQQsCw0vCXkMThw4b5Q4/q87ZZgRb4st8COc=", + "owner": "numtide", + "repo": "devshell", + "rev": "c8ce8ed81726079c398f5f29c4b68a7d6a3c2fa2", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "devshell", + "type": "github" + } + }, + "flake-utils": { + "locked": { + "lastModified": 1642700792, + "narHash": "sha256-XqHrk7hFb+zBvRg6Ghl+AZDq03ov6OshJLiSWOoX5es=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "846b2ae0fc4cc943637d3d1def4454213e203cba", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_2": { + "locked": { + "lastModified": 1659877975, + "narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1643381941, + "narHash": "sha256-pHTwvnN4tTsEKkWlXQ8JMY423epos8wUOhthpwJjtpc=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "5efc8ca954272c4376ac929f4c5ffefcc20551d5", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1666659249, + "narHash": "sha256-Qbyj7ya2ZTL5Vt5ONMUwgq/shNSqZVbxDInQbmDO5iw=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "d4f8e53720b554b6dac71c7ec62b3b51158905f9", + "type": "github" + }, + "original": { + "id": "nixpkgs", + "type": "indirect" + } + }, + "root": { + "inputs": { + "devshell": "devshell", + "flake-utils": "flake-utils_2", + "nixpkgs": "nixpkgs_2" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/devshell/flake.nix b/devshell/flake.nix new file mode 100644 index 00000000..f577c697 --- /dev/null +++ b/devshell/flake.nix @@ -0,0 +1,24 @@ +{ + description = "virtual environments"; + + inputs.devshell.url = "github:numtide/devshell"; + inputs.flake-utils.url = "github:numtide/flake-utils"; + + outputs = { self, flake-utils, devshell, nixpkgs }: + flake-utils.lib.eachDefaultSystem (system: { + devShell = + let pkgs = import nixpkgs { + inherit system; + + overlays = [ devshell.overlay ]; + }; + in + pkgs.devshell.mkShell { + imports = [ (pkgs.devshell.importTOML ./devshell.toml) ]; + devshell.packages = with pkgs; [ + go + gnumake + ]; + }; + }); +} diff --git a/discovery/consul.go b/discovery/consul.go index 3be6b0c5..f7069b74 100644 --- a/discovery/consul.go +++ b/discovery/consul.go @@ -24,7 +24,7 @@ func init() { // Consul wraps the service discovery backend for the Hashicorp Consul client // and tracks the state of all watched dependencies. type Consul struct { - api.Client + *api.Client lock sync.RWMutex watchedServices map[string][]*api.ServiceEntry } @@ -53,7 +53,7 @@ func NewConsul(config interface{}) (*Consul, error) { return nil, err } watchedServices := make(map[string][]*api.ServiceEntry) - consul := &Consul{*client, sync.RWMutex{}, watchedServices} + consul := &Consul{client, sync.RWMutex{}, watchedServices} return consul, nil } diff --git a/discovery/consul_test.go b/discovery/consul_test.go index e06161d5..ba8289db 100644 --- a/discovery/consul_test.go +++ b/discovery/consul_test.go @@ -84,12 +84,13 @@ func TestWithConsul(t *testing.T) { t.Run("TestConsulReregister", testConsulReregister(testServer)) t.Run("TestConsulCheckForChanges", testConsulCheckForChanges(testServer)) t.Run("TestConsulEnableTagOverride", testConsulEnableTagOverride(testServer)) + t.Run("testConsulTagsMeta", testConsulTagsMeta(testServer)) } func testConsulTTLPass(testServer *TestServer) func(*testing.T) { return func(t *testing.T) { consul, _ := NewConsul(testServer.HTTPAddr) - name := fmt.Sprintf("TestConsulTTLPass") + name := "TestConsulTTLPass" service := generateServiceDefinition(name, consul) checkID := fmt.Sprintf("service:%s", service.ID) @@ -105,7 +106,7 @@ func testConsulTTLPass(testServer *TestServer) func(*testing.T) { func testConsulRegisterWithInitialStatus(testServer *TestServer) func(*testing.T) { return func(t *testing.T) { consul, _ := NewConsul(testServer.HTTPAddr) - name := fmt.Sprintf("TestConsulRegisterWithInitialStatus") + name := "TestConsulRegisterWithInitialStatus" service := generateServiceDefinition(name, consul) checkID := fmt.Sprintf("service:%s", service.ID) @@ -121,7 +122,7 @@ func testConsulRegisterWithInitialStatus(testServer *TestServer) func(*testing.T func testConsulReregister(testServer *TestServer) func(*testing.T) { return func(t *testing.T) { consul, _ := NewConsul(testServer.HTTPAddr) - name := fmt.Sprintf("TestConsulReregister") + name := "TestConsulReregister" service := generateServiceDefinition(name, consul) id := service.ID @@ -146,9 +147,42 @@ func testConsulReregister(testServer *TestServer) func(*testing.T) { } } +func contains(s []string, str string) bool { + for _, v := range s { + if v == str { + return true + } + } + + return false +} + +func testConsulTagsMeta(testServer *TestServer) func(*testing.T) { + return func(t *testing.T) { + consul, _ := NewConsul(testServer.HTTPAddr) + name := "TestConsulReregister" + service := generateServiceDefinition(name, consul) + id := service.ID + + service.SendHeartbeat() // force registration and 1st heartbeat + services, _ := consul.Agent().Services() + svc := services[id] + if !contains(svc.Tags, "a") || !contains(svc.Tags, "b") { + t.Fatalf("first tag must containt a & b but is %s", svc.Tags) + } + if svc.Meta["keyA"] != "A" { + t.Fatalf("first meta must containt keyA:A but is %s", svc.Meta["keyA"]) + } + if svc.Meta["keyB"] != "B" { + t.Fatalf("first meta must containt keyB:B but is %s", svc.Meta["keyB"]) + } + + } +} + func testConsulCheckForChanges(testServer *TestServer) func(*testing.T) { return func(t *testing.T) { - backend := fmt.Sprintf("TestConsulCheckForChanges") + backend := "TestConsulCheckForChanges" consul, _ := NewConsul(testServer.HTTPAddr) service := generateServiceDefinition(backend, consul) id := service.ID @@ -163,7 +197,7 @@ func testConsulCheckForChanges(testServer *TestServer) func(*testing.T) { if changed, _ := consul.CheckForUpstreamChanges(backend, "", ""); changed { t.Errorf("%v should not have changed without TTL expiring", id) } - check := fmt.Sprintf("service:TestConsulCheckForChanges") + check := "service:TestConsulCheckForChanges" consul.Agent().UpdateTTL(check, "expired", "critical") if changed, _ := consul.CheckForUpstreamChanges(backend, "", ""); !changed { t.Errorf("%v should have changed after TTL expired.", id) @@ -173,7 +207,7 @@ func testConsulCheckForChanges(testServer *TestServer) func(*testing.T) { func testConsulEnableTagOverride(testServer *TestServer) func(*testing.T) { return func(t *testing.T) { - backend := fmt.Sprintf("TestConsulEnableTagOverride") + backend := "TestConsulEnableTagOverride" consul, _ := NewConsul(testServer.HTTPAddr) service := &ServiceDefinition{ ID: backend, @@ -204,12 +238,17 @@ func testConsulEnableTagOverride(testServer *TestServer) func(*testing.T) { func generateServiceDefinition(serviceName string, consul *Consul) *ServiceDefinition { return &ServiceDefinition{ - ID: serviceName, - Name: serviceName, - IPAddress: "192.168.1.1", + ID: serviceName, + Name: serviceName, + IPAddress: "192.168.1.1", InitialStatus: "warning", - TTL: 5, - Port: 9000, - Consul: consul, + TTL: 5, + Port: 9000, + Consul: consul, + Tags: []string{"a", "b"}, + Meta: map[string]string{ + "keyA": "A", + "keyB": "B", + }, } } diff --git a/discovery/service.go b/discovery/service.go index 6b3a4f79..8ce302f8 100644 --- a/discovery/service.go +++ b/discovery/service.go @@ -15,6 +15,7 @@ type ServiceDefinition struct { Port int TTL int Tags []string + Meta map[string]string InitialStatus string IPAddress string EnableTagOverride bool @@ -59,19 +60,16 @@ func (service *ServiceDefinition) RegisterWithInitialStatus() { status := "" switch service.InitialStatus { - case "passing": - status = api.HealthPassing - break - case "warning": - status = api.HealthWarning - break - case "critical": - status = api.HealthCritical - break + case "passing": + status = api.HealthPassing + case "warning": + status = api.HealthWarning + case "critical": + status = api.HealthCritical } log.Infof("Registering service %v with initial status set to %v", - service.Name, service.InitialStatus) + service.Name, service.InitialStatus) service.register(status) } @@ -96,13 +94,14 @@ func (service *ServiceDefinition) registerService(status string) error { ID: service.ID, Name: service.Name, Tags: service.Tags, + Meta: service.Meta, Port: service.Port, Address: service.IPAddress, EnableTagOverride: service.EnableTagOverride, Check: &api.AgentServiceCheck{ - TTL: fmt.Sprintf("%ds", service.TTL), - Status: status, - Notes: fmt.Sprintf("TTL for %s set by containerpilot", service.Name), + TTL: fmt.Sprintf("%ds", service.TTL), + Status: status, + Notes: fmt.Sprintf("TTL for %s set by containerpilot", service.Name), DeregisterCriticalServiceAfter: service.DeregisterCriticalServiceAfter, }, }, diff --git a/discovery/test_server.go b/discovery/test_server.go index cc32a8df..55623893 100644 --- a/discovery/test_server.go +++ b/discovery/test_server.go @@ -9,7 +9,7 @@ import ( "os/exec" "strconv" - "github.com/hashicorp/consul/testutil/retry" + "github.com/hashicorp/consul/sdk/testutil/retry" cleanhttp "github.com/hashicorp/go-cleanhttp" ) @@ -64,7 +64,8 @@ type failer struct { failed bool } -func (f *failer) Log(args ...interface{}) { fmt.Println(args) } +func (f *failer) Helper() {} +func (f *failer) Log(args ...interface{}) { fmt.Println(args...) } func (f *failer) FailNow() { f.failed = true } // WaitForAPI waits for only the agent HTTP endpoint to start responding. This diff --git a/docs/30-configuration/32-configuration-file.md b/docs/30-configuration/32-configuration-file.md index 2c3b21ab..75bbfc99 100644 --- a/docs/30-configuration/32-configuration-file.md +++ b/docs/30-configuration/32-configuration-file.md @@ -60,6 +60,10 @@ The following is a completed example of the JSON5 file configuration schema, wit "app", "prod" ], + meta: { + keyA: "A", + keyB: "B", + }, interfaces: [ "eth0", "eth1[1]", diff --git a/docs/30-configuration/34-jobs.md b/docs/30-configuration/34-jobs.md index 14e708bd..4e237145 100644 --- a/docs/30-configuration/34-jobs.md +++ b/docs/30-configuration/34-jobs.md @@ -81,6 +81,10 @@ jobs: [ "app", "prod" ], + meta: { + keyA: "A", + keyB: "B", + }, interfaces: [ "eth0", "eth1[1]", @@ -108,6 +112,14 @@ The `name` field is the name of the job as it will appear in logs and events. It The `exec` field is the executable (and its arguments) that is called when the job runs. This field can contain a string or an array of strings ([see below](#exec-arguments) for details on the format). The command to be run will have a process group set and this entire process group will be reaped by ContainerPilot when the process exits. The process will be run concurrently to all other work, so the process won't block the processing of other ContainerPilot events. +##### `uid` + +The `uid` field is the ID of the user that runs the command. + +##### `gid` + +The `gid` field is the ID of the group that runs the command. + ##### `logging` Jobs and health checks have a `logging` configuration block with a single option: `raw`. When the `raw`field is set to `false` (the default), ContainerPilot will wrap each line of output from an `exec` process's stdout/stderr in a log line. If set to `true`, ContainerPilot will attach the stdout/stderr of the process to the container's stdout/stderr and these streams will be unmodified by ContainerPilot. The latter option can be useful if the process emits structured logs in its own format. @@ -233,6 +245,10 @@ The `initial_status` field is optional and specifies which status to immediately The `tags` field is an optional array of tags to be used when the job is registered as a service in Consul. Other containers can use these tags in `watches` to filter a service by tag. +##### `meta` + +The `meta` field is an optional map key/value to be used when the job is registered as a service in Consul. Key names must be valid JSON5/Ecmascript identifierNames or be quoted and follow consul limitation , practical this means only [a-zA-Z0-9_-] can be used in key names and key names with '-' must be quoted + ##### `interfaces` The `interfaces` field is an optional single or array of interface specifications. If given, the IP of the service will be obtained from the first interface specification that matches. (Default value is `["eth0:inet"]`). The value that ContainerPilot uses for the IP address of the interface will be set as an environment variable with the name `CONTAINERPILOT_{JOB}_IP`. See the [environment variables](./32-configuration-file.md#environment-variables) section. diff --git a/docs/30-configuration/36-telemetry.md b/docs/30-configuration/36-telemetry.md index f1a09e1f..2cf46655 100644 --- a/docs/30-configuration/36-telemetry.md +++ b/docs/30-configuration/36-telemetry.md @@ -58,7 +58,7 @@ The `metrics` field is a list of user-defined metrics that the telemetry service The collectors can record metrics sent via the [HTTP control socket](./37-control-plane.md). If your application can't use this endpoint on its own, you can use a periodic job to record the metric value and call `containerpilot -putmetric`. An example of a good job script might be: ```bash -#!/bin/bash +#!/usr/bin/env bash # check free memory val=$(free | awk -F' +' '/Mem/{print $3}') ./containerpilot -putmetric "free_memory=$val" diff --git a/events/bus.go b/events/bus.go index f4e4879a..a1b8c049 100644 --- a/events/bus.go +++ b/events/bus.go @@ -115,9 +115,7 @@ func (bus *EventBus) Unsubscribe(subscriber EventSubscriber) { bus.lock.Lock() defer bus.lock.Unlock() sub := subscriber.(*Subscriber) - if _, ok := bus.registry[sub]; ok { - delete(bus.registry, sub) - } + delete(bus.registry, sub) bus.done.Done() } diff --git a/events/events_test.go b/events/events_test.go index 2488a746..fff12143 100644 --- a/events/events_test.go +++ b/events/events_test.go @@ -70,7 +70,7 @@ func TestPubSubInterfaces(t *testing.T) { ts.Run(ctx, bus) expected := []Event{ - Event{Startup, "serviceA"}, + {Startup, "serviceA"}, } for _, event := range expected { tp.Publish(event) diff --git a/glide.lock b/glide.lock deleted file mode 100644 index 2bd3f8e9..00000000 --- a/glide.lock +++ /dev/null @@ -1,73 +0,0 @@ -hash: c13286385a0d957eb3b4e47af1f85b6cd8d8b084c846737477f46ac04d2c44f2 -updated: 2017-11-13T21:22:43.229954534-05:00 -imports: -- name: github.com/beorn7/perks - version: 4c0e84591b9aa9e6dcfdf3e020114cd81f89d5f9 - subpackages: - - quantile -- name: github.com/client9/reopen - version: 1a6ccbeaae3f56aa0058f5491382cb21726e214e -- name: github.com/flynn/json5 - version: 7620272ed63390e979cf5882d2fa0506fe2a8db5 -- name: github.com/golang/protobuf - version: 6a1fa9404c0aebf36c879bc50152edcc953910d2 - subpackages: - - proto -- name: github.com/hashicorp/consul - version: 783a405d781ffc5edaf2d6b5eff41e22df96644f - subpackages: - - api - - testutil/retry -- name: github.com/hashicorp/go-cleanhttp - version: 3573b8b52aa7b37b9358d966a898feb387f62437 -- name: github.com/hashicorp/go-rootcerts - version: 6bb64b370b90e7ef1fa532be9e591a81c3493e00 -- name: github.com/hashicorp/serf - version: 91fd53b1d3e624389ed9a295a3fa380e5c7b9dfc - subpackages: - - coordinate -- name: github.com/matttproud/golang_protobuf_extensions - version: c12348ce28de40eed0136aa2b644d0ee0650e56c - subpackages: - - pbutil -- name: github.com/mitchellh/go-homedir - version: b8bc1bf767474819792c23f32d8286a45736f1c6 -- name: github.com/mitchellh/mapstructure - version: d2dd0262208475919e1a362f675cfc0e7c10e905 -- name: github.com/prometheus/client_golang - version: c5b7fccd204277076155f10851dad72b76a49317 - subpackages: - - prometheus -- name: github.com/prometheus/client_model - version: 6f3806018612930941127f2a7c6c453ba2c527d2 - subpackages: - - go -- name: github.com/prometheus/common - version: 0866df4b85a18d652b6965be022d007cdf076822 - subpackages: - - expfmt - - internal/bitbucket.org/ww/goautoneg - - model -- name: github.com/prometheus/procfs - version: e645f4e5aaa8506fc71d6edbc5c4ff02c04c46f2 - subpackages: - - xfs -- name: github.com/sirupsen/logrus - version: 202f25545ea4cf9b191ff7f846df5d87c9382c2b -- name: golang.org/x/sys - version: 94b76065f2d2081d0fef24a6e67c571f51a6408a - subpackages: - - unix -testImports: -- name: github.com/davecgh/go-spew - version: 6d212800a42e8ab5c146b8ace3490ee17e5225f9 - subpackages: - - spew -- name: github.com/pmezard/go-difflib - version: d8ed2627bdf02c080bf22230dbb337003b7aba2d - subpackages: - - difflib -- name: github.com/stretchr/testify - version: 69483b4bd14f5845b5a1e55bca19e954e827f1d0 - subpackages: - - assert diff --git a/glide.yaml b/glide.yaml deleted file mode 100644 index 4c0c1053..00000000 --- a/glide.yaml +++ /dev/null @@ -1,24 +0,0 @@ -package: github.com/joyent/containerpilot -homepage: https://www.joyent.com/containerpilot -license: MPL-2.0 -import: -- package: github.com/sirupsen/logrus - version: 1.0.0 -- package: github.com/hashicorp/consul - version: ~1.0.0 - subpackages: - - api -- package: github.com/mitchellh/mapstructure - version: d2dd0262208475919e1a362f675cfc0e7c10e905 -- package: github.com/prometheus/client_golang - version: 0.8.0 - subpackages: - - prometheus -- package: github.com/flynn/json5 - version: 7620272ed63390e979cf5882d2fa0506fe2a8db5 -testImport: -- package: github.com/stretchr/testify - version: v1.1.4 - subpackages: - - assert -- package: github.com/client9/reopen diff --git a/go.mod b/go.mod new file mode 100644 index 00000000..7383c192 --- /dev/null +++ b/go.mod @@ -0,0 +1,42 @@ +module github.com/joyent/containerpilot + +go 1.18 + +require ( + github.com/client9/reopen v1.0.0 + github.com/flynn/json5 v0.0.0-20160717195620-7620272ed633 + github.com/hashicorp/consul/api v1.15.3 + github.com/hashicorp/consul/sdk v0.11.0 + github.com/hashicorp/go-cleanhttp v0.5.2 + github.com/mitchellh/mapstructure v1.5.0 + github.com/prometheus/client_golang v1.13.0 + github.com/sirupsen/logrus v1.9.0 + github.com/stretchr/testify v1.8.1 +) + +require ( + github.com/armon/go-metrics v0.3.10 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/fatih/color v1.13.0 // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/hashicorp/go-hclog v1.2.0 // indirect + github.com/hashicorp/go-immutable-radix v1.3.1 // indirect + github.com/hashicorp/go-rootcerts v1.0.2 // indirect + github.com/hashicorp/golang-lru v0.5.4 // indirect + github.com/hashicorp/serf v0.9.7 // indirect + github.com/kylelemons/godebug v1.1.0 // indirect + github.com/mattn/go-colorable v0.1.12 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/prometheus/client_model v0.2.0 // indirect + github.com/prometheus/common v0.37.0 // indirect + github.com/prometheus/procfs v0.8.0 // indirect + github.com/robertkrimen/otto v0.0.0-20211024170158-b87d35c0b86f // indirect + golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect + google.golang.org/protobuf v1.28.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 00000000..ce56e16f --- /dev/null +++ b/go.sum @@ -0,0 +1,634 @@ +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/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/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +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-metrics v0.3.10 h1:FR+drcQStOe+32sYyJYyZ7FIdgoGGBnwLl+flodp8Uo= +github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/armon/go-radix v1.0.0/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/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= +github.com/cespare/xxhash/v2 v2.1.2/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/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= +github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/client9/reopen v1.0.0 h1:8tpLVR74DLpLObrn2KvsyxJY++2iORGR17WLUdSzUws= +github.com/client9/reopen v1.0.0/go.mod h1:caXVCEr+lUtoN1FlsRiOWdfQtdRHIYfcb0ai8qKWtkQ= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +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/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/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= +github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/flynn/json5 v0.0.0-20160717195620-7620272ed633 h1:xJMmr4GMYIbALX5edyoDIOQpc2bOQTeJiWMeCl9lX/8= +github.com/flynn/json5 v0.0.0-20160717195620-7620272ed633/go.mod h1:NJDK3/o7abx6PP54EOe0G0n0RLmhCo9xv61gUYpI0EY= +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-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= +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-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= +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/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +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 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= +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.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +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/hashicorp/consul/api v1.15.3 h1:WYONYL2rxTXtlekAqblR2SCdJsizMDIj/uXb5wNy9zU= +github.com/hashicorp/consul/api v1.15.3/go.mod h1:/g/qgcoBcEXALCNZgRRisyTW0nY86++L0KbeAMXYCeY= +github.com/hashicorp/consul/sdk v0.11.0 h1:HRzj8YSCln2yGgCumN5CL8lYlD3gBurnervJRJAZyC4= +github.com/hashicorp/consul/sdk v0.11.0/go.mod h1:yPkX5Q6CsxTFMjQQDJwzeNmUUF5NUGGbrDsv9wTb8cw= +github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v1.2.0 h1:La19f8d7WIlm4ogzNHB0JGqs5AUDAZ2UfCY4sJXcJdM= +github.com/hashicorp/go-hclog v1.2.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-immutable-radix v1.3.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= +github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI= +github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= +github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= +github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc= +github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= +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-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE= +github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +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/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= +github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= +github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= +github.com/hashicorp/memberlist v0.3.1 h1:MXgUXLqva1QvpVEDQW1IQLG0wivQAtmFlHRQ+1vWZfM= +github.com/hashicorp/memberlist v0.3.1/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= +github.com/hashicorp/serf v0.9.7 h1:hkdgbqizGQHuU5IPqYM1JdSMV8nKfpuOnZYXssk9muY= +github.com/hashicorp/serf v0.9.7/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +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.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +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/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +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/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +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/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +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/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +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/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= +github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +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.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= +github.com/miekg/dns v1.1.41 h1:WMszZWJG0XmzbK9FEmzH2TVcqYzFesusSIB41b8KHxY= +github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +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/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +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/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= +github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +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 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/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= +github.com/prometheus/client_golang v1.13.0 h1:b71QUfeo5M8gq2+evJdTPfZhYMAU0uKPkyPJ7TPsloU= +github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ= +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.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= +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.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= +github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= +github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= +github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= +github.com/robertkrimen/otto v0.0.0-20211024170158-b87d35c0b86f h1:a7clxaGmmqtdNTXyvrp/lVO/Gnkzlhc/+dLs5v965GM= +github.com/robertkrimen/otto v0.0.0-20211024170158-b87d35c0b86f/go.mod h1:/mK7FZ3mFYEn9zvNPhpngTyatyehSwte5bJZ4ehL5Xw= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +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/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= +github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= +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.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= +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-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-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= +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-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-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-20190923162816-aa69164e4478/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-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= +golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f h1:oA4XRj0qtSt8Yo1Zms0CUlsT3KG69V2UGQWPBxujDmc= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +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/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +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/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/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-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-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-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-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-20200116001909-b77594299b42/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-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +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/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +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-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-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-20190907020128-2ca718005c18/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-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/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/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +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 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +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/readline.v1 v1.0.0-20160726135117-62c6fe619375/go.mod h1:lNEQeAhU009zbRxng+XOj5ITVgY24WcbNnQopyfKoYQ= +gopkg.in/sourcemap.v1 v1.0.5 h1:inv58fC9f9J3TK2Y2R1NPntXEn3/wjWHkonhIUODNTI= +gopkg.in/sourcemap.v1 v1.0.5/go.mod h1:2RlvNNSMglmRrcvhfuzp4hQHwOtjxlbjX7UPY/GXb78= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +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= diff --git a/integration_tests/fixtures/app/Dockerfile b/integration_tests/fixtures/app/Dockerfile index 6839f601..a28ab634 100644 --- a/integration_tests/fixtures/app/Dockerfile +++ b/integration_tests/fixtures/app/Dockerfile @@ -1,8 +1,8 @@ -FROM node:slim +FROM node:16-slim RUN apt-get update && \ apt-get install -y \ - curl netcat-openbsd && \ + curl netcat-openbsd procps && \ rm -rf /var/lib/apt/lists/* RUN npm install -g json http-server diff --git a/integration_tests/fixtures/app/sensor.sh b/integration_tests/fixtures/app/sensor.sh index 00d3adb5..ce368d4e 100755 --- a/integration_tests/fixtures/app/sensor.sh +++ b/integration_tests/fixtures/app/sensor.sh @@ -1,3 +1,4 @@ -#!/bin/bash +#!/bin/bash -x +/bin/containerpilot -template /bin/containerpilot -putmetric 'containerpilot_app_some_counter=42' diff --git a/integration_tests/fixtures/consul/Dockerfile b/integration_tests/fixtures/consul/Dockerfile index 335683d7..cf6b92e1 100644 --- a/integration_tests/fixtures/consul/Dockerfile +++ b/integration_tests/fixtures/consul/Dockerfile @@ -1,3 +1,3 @@ -FROM consul:latest +FROM consul:1.11.4 RUN apk --no-cache add jq curl COPY assert /bin/assert diff --git a/integration_tests/fixtures/nginx/Dockerfile b/integration_tests/fixtures/nginx/Dockerfile index 34f04bfc..9c892c88 100644 --- a/integration_tests/fixtures/nginx/Dockerfile +++ b/integration_tests/fixtures/nginx/Dockerfile @@ -1,5 +1,5 @@ # Nginx container including ContainerPilot -FROM alpine:3.3 +FROM alpine:3.15 # install nginx and tooling we need RUN apk update && apk add \ @@ -9,8 +9,8 @@ RUN apk update && apk add \ && rm -rf /var/cache/apk/* # we use consul-template to re-write our Nginx virtualhost config -RUN curl -Lo /tmp/consul_template_0.14.0_linux_amd64.zip https://releases.hashicorp.com/consul-template/0.14.0/consul-template_0.14.0_linux_amd64.zip && \ - unzip /tmp/consul_template_0.14.0_linux_amd64.zip && \ +RUN curl -Lo /tmp/consul_template_0.28.1_linux_amd64.zip https://releases.hashicorp.com/consul-template/0.28.1/consul-template_0.28.1_linux_amd64.zip && \ + unzip /tmp/consul_template_0.28.1_linux_amd64.zip && \ mv consul-template /bin # add ContainerPilot build and configuration diff --git a/integration_tests/tests/test_config_reload/run.sh b/integration_tests/tests/test_config_reload/run.sh index d0ba1e61..7e5d7f02 100755 --- a/integration_tests/tests/test_config_reload/run.sh +++ b/integration_tests/tests/test_config_reload/run.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # test_config_reload: runs an application container and send it commands # to reload config diff --git a/integration_tests/tests/test_coprocess/run.sh b/integration_tests/tests/test_coprocess/run.sh index 84f28bb4..c715c280 100755 --- a/integration_tests/tests/test_coprocess/run.sh +++ b/integration_tests/tests/test_coprocess/run.sh @@ -1,10 +1,10 @@ -#!/bin/bash +#!/usr/bin/env bash set -e function finish { result=$? if [[ "$result" -ne 0 ]]; then - docker exec -it "$app" ps -ef + docker exec -i "$app" ps -ef docker logs "$app" | tee app.log fi exit $result @@ -13,30 +13,30 @@ trap finish EXIT # stand up app and consul, and wait for consul to elect a leader docker-compose up -d -docker exec -it "$(docker-compose ps -q consul)" assert ready +docker exec -i "$(docker-compose ps -q consul)" assert ready # verify the coprocess is running app=$(docker-compose ps -q app) -docker exec -it "$app" ps -ef | grep coprocess +docker exec -i "$app" ps -ef | grep coprocess # kill the coprocess and verify it restarts -docker exec -it "$app" pkill coprocess +docker exec -i "$app" pkill coprocess sleep 1 -docker exec -it "$app" ps -ef | grep coprocess +docker exec -i "$app" ps -ef | grep coprocess # kill the coprocess and verify it doesn't restart again -docker exec -it "$app" pkill coprocess +docker exec -i "$app" pkill coprocess sleep 1 -docker exec -it "$app" ps -ef | grep coprocess && exit 1 +docker exec -i "$app" ps -ef | grep coprocess && exit 1 # update the ContainerPilot config and verify the coprocess is running # with the new flags (this resets the restart limit) -docker exec -it "$app" sed -i 's/arg1/arg2/' /etc/containerpilot-with-coprocess.json5 -docker exec -it "$app" /reload-containerpilot.sh single +docker exec -i "$app" sed -i 's/arg1/arg2/' /etc/containerpilot-with-coprocess.json5 +docker exec -i "$app" /reload-containerpilot.sh single sleep 1 -docker exec -it "$app" ps -ef | grep coprocess | grep arg2 +docker exec -i "$app" ps -ef | grep coprocess | grep arg2 # kill the coprocess and verify it restarts -docker exec -it "$app" pkill coprocess +docker exec -i "$app" pkill coprocess sleep 1 -docker exec -it "$app" ps -ef | grep coprocess +docker exec -i "$app" ps -ef | grep coprocess diff --git a/integration_tests/tests/test_discovery_consul/run.sh b/integration_tests/tests/test_discovery_consul/run.sh index c19a7960..f39de099 100755 --- a/integration_tests/tests/test_discovery_consul/run.sh +++ b/integration_tests/tests/test_discovery_consul/run.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -e function finish { @@ -22,17 +22,17 @@ trap finish EXIT # start up consul and wait for leader election docker-compose up -d consul consul=$(docker-compose ps -q consul) -docker exec -it "$consul" assert ready +docker exec -i "$consul" assert ready # start app and nginx, wait for them to register docker-compose up -d app nginx -docker exec -it "$consul" assert service app 1 -docker exec -it "$consul" assert service nginx 1 +docker exec -i "$consul" assert service app 1 +docker exec -i "$consul" assert service nginx 1 # test that nginx config has been updated nginx=$(docker-compose ps -q nginx) for _ in $(seq 0 10); do - docker exec -it "$nginx" curl -s -o /dev/null --fail "http://localhost:80/app/" + docker exec -i "$nginx" curl -s -o /dev/null --fail "http://localhost:80/app/" [ $? -eq 0 ] && exit 0 sleep 1 done || (echo "no route for /app/" && exit 1) diff --git a/integration_tests/tests/test_envvars/run.sh b/integration_tests/tests/test_envvars/run.sh index f5099831..60bf5731 100755 --- a/integration_tests/tests/test_envvars/run.sh +++ b/integration_tests/tests/test_envvars/run.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -e ## run and make sure we get the env var out but print diff --git a/integration_tests/tests/test_logging/run.sh b/integration_tests/tests/test_logging/run.sh index 5a6285e6..b49d1034 100755 --- a/integration_tests/tests/test_logging/run.sh +++ b/integration_tests/tests/test_logging/run.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -e function finish { @@ -11,7 +11,7 @@ trap finish EXIT # start up app & consul and wait for leader election docker-compose up -d consul app -docker exec -it "$(docker-compose ps -q consul)" assert ready +docker exec -i "$(docker-compose ps -q consul)" assert ready app=$(docker-compose ps -q app) sleep 1 # need time for 1st health checks to fire diff --git a/integration_tests/tests/test_no_command/run.sh b/integration_tests/tests/test_no_command/run.sh index 1906b62c..ee500fba 100755 --- a/integration_tests/tests/test_no_command/run.sh +++ b/integration_tests/tests/test_no_command/run.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash logFail() { echo "${1}" diff --git a/integration_tests/tests/test_reap_zombies/docker-compose.yml b/integration_tests/tests/test_reap_zombies/docker-compose.yml index 45e2e122..46d7dbf6 100644 --- a/integration_tests/tests/test_reap_zombies/docker-compose.yml +++ b/integration_tests/tests/test_reap_zombies/docker-compose.yml @@ -9,7 +9,7 @@ services: command: agent -dev -client 0.0.0.0 -bind 0.0.0.0 zombies: - image: alpine:3.5 + image: alpine:3.15 mem_limit: 128m links: - consul:consul diff --git a/integration_tests/tests/test_reap_zombies/run.sh b/integration_tests/tests/test_reap_zombies/run.sh index 0c3a95a1..77094d9c 100755 --- a/integration_tests/tests/test_reap_zombies/run.sh +++ b/integration_tests/tests/test_reap_zombies/run.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -e # Test to verify that we're correctly reaping zombies. # At any given time we may have up to 1 zombie parented to PID1 (it has been @@ -8,7 +8,7 @@ set -e docker-compose up -d consul zombies consul=$(docker-compose ps -q consul) -docker exec -it "$consul" assert ready +docker exec -i "$consul" assert ready ID=$(docker-compose ps -q zombies) sleep 6 diff --git a/integration_tests/tests/test_reopen/run.sh b/integration_tests/tests/test_reopen/run.sh index b949a2f2..26dbbd33 100755 --- a/integration_tests/tests/test_reopen/run.sh +++ b/integration_tests/tests/test_reopen/run.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash function finish { result=$? diff --git a/integration_tests/tests/test_reopen/test_reopen.sh b/integration_tests/tests/test_reopen/test_reopen.sh index b2e3708e..82cc3a44 100755 --- a/integration_tests/tests/test_reopen/test_reopen.sh +++ b/integration_tests/tests/test_reopen/test_reopen.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash CONTAINERPILOT_LOGFILE="/tmp/containerpilot.log" CONTAINERPILOT_ROTATEDLOGFILE="/tmp/containerpilot.log.1" @@ -28,4 +28,4 @@ if [ ! -f $CONTAINERPILOT_LOGFILE ] || [[ $logs != *"hello world"* ]]; then exit 1 fi -exit 0 \ No newline at end of file +exit 0 diff --git a/integration_tests/tests/test_sighup/run.sh b/integration_tests/tests/test_sighup/run.sh index 58482942..324da27f 100755 --- a/integration_tests/tests/test_sighup/run.sh +++ b/integration_tests/tests/test_sighup/run.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -e @@ -14,12 +14,12 @@ trap finish EXIT # start up consul and wait for leader election docker-compose up -d consul consul=$(docker-compose ps -q consul) -docker exec -it "$consul" assert ready +docker exec -i "$consul" assert ready # start up app and wait for it to register docker-compose up -d app app=$(docker-compose ps -q app) -docker exec -it "$consul" assert service app 1 +docker exec -i "$consul" assert service app 1 # send SIGHUP into the app container docker kill -s HUP "$app" @@ -29,6 +29,6 @@ docker kill -s USR2 "$app" # verify that the app is still running and that both `on-sighup` and # `on-sigusr2` signal event based jobs have executed -docker exec -it "$consul" assert service app 1 +docker exec -i "$consul" assert service app 1 docker logs "$app" | grep "msg=\"'on-sighup job fired on SIGHUP" docker logs "$app" | grep "msg=\"'on-sigusr2 job fired on SIGUSR2" diff --git a/integration_tests/tests/test_sigterm/run.sh b/integration_tests/tests/test_sigterm/run.sh index fcaf8e94..d12bf648 100755 --- a/integration_tests/tests/test_sigterm/run.sh +++ b/integration_tests/tests/test_sigterm/run.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -e @@ -14,18 +14,18 @@ trap finish EXIT # start up consul and wait for leader election docker-compose up -d consul consul=$(docker-compose ps -q consul) -docker exec -it "$consul" assert ready +docker exec -i "$consul" assert ready # start up app and wait for it to register docker-compose up -d app app=$(docker-compose ps -q app) -docker exec -it "$consul" assert service app 1 +docker exec -i "$consul" assert service app 1 # sigterm app container docker stop "$app" # and verify it's exited gracefully from consul, and that # both preStop and postStop jobs have executed -docker exec -it "$consul" assert service app 1 +docker exec -i "$consul" assert service app 1 docker logs "$app" | grep "msg=\"'preStop fired on app stopping" docker logs "$app" | grep "msg=\"'postStop fired on app stopped" diff --git a/integration_tests/tests/test_tasks/run.sh b/integration_tests/tests/test_tasks/run.sh index f7ba6a95..f820c556 100755 --- a/integration_tests/tests/test_tasks/run.sh +++ b/integration_tests/tests/test_tasks/run.sh @@ -1,10 +1,10 @@ -#!/bin/bash +#!/usr/bin/env bash # test_tasks: runs multiple tasks with timeouts for a period of time and # then parses their logs to verify that they ran the expected number of times. # start up consul and wait for leader election docker-compose up -d consul -docker exec -it "$(docker-compose ps -q consul)" assert ready +docker exec -i "$(docker-compose ps -q consul)" assert ready docker-compose up -d app APP_ID="$(docker-compose ps -q app)" diff --git a/integration_tests/tests/test_tasks/task.sh b/integration_tests/tests/test_tasks/task.sh index eb0c7fad..511463b8 100755 --- a/integration_tests/tests/test_tasks/task.sh +++ b/integration_tests/tests/test_tasks/task.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash SLEEP=${1:-"1"} FILE=${2:-"/tmp/test"} diff --git a/integration_tests/tests/test_telemetry/check.sh b/integration_tests/tests/test_telemetry/check.sh index debe21fb..dac06a58 100755 --- a/integration_tests/tests/test_telemetry/check.sh +++ b/integration_tests/tests/test_telemetry/check.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eo pipefail IP="$1" diff --git a/integration_tests/tests/test_telemetry/run.sh b/integration_tests/tests/test_telemetry/run.sh index be73fb2f..9ba03c5b 100755 --- a/integration_tests/tests/test_telemetry/run.sh +++ b/integration_tests/tests/test_telemetry/run.sh @@ -1,22 +1,22 @@ -#!/bin/bash +#!/usr/bin/env bash set -e # start up consul and wait for leader election docker-compose up -d consul consul=$(docker-compose ps -q consul) -docker exec -it "$consul" assert ready +docker exec -i "$consul" assert ready # start up app docker-compose up -d app app="$(docker-compose ps -q app)" -IP=$(docker inspect -f '{{ .NetworkSettings.Networks.testtelemetry_default.IPAddress }}' "$app") +IP=$(docker inspect -f '{{ .NetworkSettings.Networks.test_telemetry_default.IPAddress }}' "$app") # This interface takes a while to converge for _ in $(seq 0 20); do sleep 1 - metrics=$(docker exec -it "$app" curl -s "${IP}:9090/metrics") + metrics=$(docker exec -i "$app" curl -s "${IP}:9090/metrics") echo "$metrics" | grep 'containerpilot_app_some_counter 42' && break done || (echo "did not get expected metrics output" && exit 1) @@ -29,9 +29,9 @@ echo "$metrics" | grep 'containerpilot_watch_instances' || \ ( echo 'no containerpilot_watch_instances metrics' && exit 1 ) # Check the status endpoint too -docker exec -it "$app" /check.sh "${IP}" +docker exec -i "$app" /check.sh "${IP}" # Make we register and tear down telemetry service in Consul -docker exec -it "$consul" assert service containerpilot 1 +docker exec -i "$consul" assert service containerpilot 1 docker-compose stop app -docker exec -it "$consul" curl -s --fail localhost:8500/v1/catalog/service/containerpilot | grep -v 'containerpilot' +docker exec -i "$consul" curl -s --fail localhost:8500/v1/catalog/service/containerpilot | grep -v 'containerpilot' diff --git a/integration_tests/tests/test_version_flag/run.sh b/integration_tests/tests/test_version_flag/run.sh index 54643aea..495b9323 100755 --- a/integration_tests/tests/test_version_flag/run.sh +++ b/integration_tests/tests/test_version_flag/run.sh @@ -1,7 +1,7 @@ -#!/bin/bash +#!/usr/bin/env bash docker-compose run app -TEST_ID=$(docker ps -a | awk -F' +' '/testversionflag/{print $1}') +TEST_ID=$(docker ps -a | awk -F' +' '/test_version_flag/{print $1}') docker logs "$TEST_ID" | grep dev-build-not-for-release result=$? diff --git a/jobs/config.go b/jobs/config.go index 4c2dd0ab..f0717192 100644 --- a/jobs/config.go +++ b/jobs/config.go @@ -21,13 +21,16 @@ const taskMinDuration = time.Millisecond type Config struct { Name string `mapstructure:"name"` Exec interface{} `mapstructure:"exec"` + UID int `mapstructure:"uid"` + GID int `mapstructure:"gid"` // service discovery - Port int `mapstructure:"port"` - InitialStatus string `mapstructure:"initial_status"` - Interfaces interface{} `mapstructure:"interfaces"` - Tags []string `mapstructure:"tags"` - ConsulExtras *ConsulExtras `mapstructure:"consul"` + Port int `mapstructure:"port"` + InitialStatus string `mapstructure:"initial_status"` + Interfaces interface{} `mapstructure:"interfaces"` + Tags []string `mapstructure:"tags"` + Meta map[string]string `mapstructure:"meta"` + ConsulExtras *ConsulExtras `mapstructure:"consul"` serviceDefinition *discovery.ServiceDefinition // health checking @@ -133,7 +136,7 @@ func (cfg *Config) Validate(disc discovery.Backend) error { } func (cfg *Config) setStopping(name string) { - cfg.stoppingWaitEvent = events.Event{events.Stopped, name} + cfg.stoppingWaitEvent = events.Event{Code: events.Stopped, Source: name} } func (cfg *Config) validateDiscovery(disc discovery.Backend) error { @@ -241,7 +244,7 @@ func (cfg *Config) validateWhenEvent() error { cfg.whenStartsLimit = unlimited } - cfg.whenEvent = events.Event{eventCode, cfg.When.Source} + cfg.whenEvent = events.Event{Code: eventCode, Source: cfg.When.Source} return nil } @@ -289,6 +292,8 @@ func (cfg *Config) validateExec() error { cfg.Name = cmd.Exec } cmd.Name = cfg.Name + cmd.UID = cfg.UID + cmd.GID = cfg.GID cfg.exec = cmd } return nil @@ -430,6 +435,7 @@ func (cfg *Config) addDiscoveryConfig(disc discovery.Backend) error { Port: cfg.Port, TTL: cfg.ttl, Tags: cfg.Tags, + Meta: cfg.Meta, InitialStatus: cfg.InitialStatus, IPAddress: ipAddress, DeregisterCriticalServiceAfter: deregAfter, diff --git a/jobs/config_test.go b/jobs/config_test.go index ddabdca5..ab527db6 100644 --- a/jobs/config_test.go +++ b/jobs/config_test.go @@ -2,7 +2,7 @@ package jobs import ( "fmt" - "io/ioutil" + "os" "testing" "time" @@ -23,6 +23,8 @@ func TestJobConfigServiceWithPreStart(t *testing.T) { // job0 is the main application job0 := jobs[0] assert.Equal(job0.Name, "serviceA", "config for job0.Name") + assert.Equal(job0.UID, 1, "config for job0.UID") + assert.Equal(job0.GID, 1, "config for job0.GID") assert.Equal(job0.Exec, "/bin/serviceA.sh", "config for job0.Exec") assert.Equal(job0.exec.Exec, "/bin/serviceA.sh", "config for job.0.Exec.exec") @@ -31,7 +33,7 @@ func TestJobConfigServiceWithPreStart(t *testing.T) { assert.Equal(job0.Tags, []string{"tag1", "tag2"}, "config for job0.Tags") assert.Equal(job0.restartLimit, 0, "config for job0.restartLimit") - assert.Equal(job0.whenEvent, events.Event{events.ExitSuccess, "preStart"}, + assert.Equal(job0.whenEvent, events.Event{Code: events.ExitSuccess, Source: "preStart"}, "config for serviceA.whenEvent") assert.Equal(job0.healthCheckExec.Exec, "/bin/healthCheckA.sh", "config for job0.healthCheckExec.Exec") @@ -58,12 +60,12 @@ func TestJobConfigHealthTimeout(t *testing.T) { job0 := jobs[0] assert.Equal(job0.Name, "serviceA", "config for job0.Name") - assert.Equal(job0.heartbeatInterval, time.Duration(10) * time.Second, "config for job0.Health") - assert.Equal(job0.healthCheckExec.Timeout, time.Duration(5) * time.Second, "config for job0.Name.Health") + assert.Equal(job0.heartbeatInterval, time.Duration(10)*time.Second, "config for job0.Health") + assert.Equal(job0.healthCheckExec.Timeout, time.Duration(5)*time.Second, "config for job0.Name.Health") job1 := jobs[1] assert.Equal(job1.Name, "serviceB", "config for job1.Name") - assert.Equal(job1.heartbeatInterval, time.Duration(10) * time.Second, "config for job1.Health") + assert.Equal(job1.heartbeatInterval, time.Duration(10)*time.Second, "config for job1.Health") assert.Equal(job1.healthCheckExec.Timeout, job1.heartbeatInterval, "config for job1.Health") } @@ -91,7 +93,7 @@ func TestJobConfigServiceWithStopping(t *testing.T) { // job0 is the main application job0 := jobs[0] assert.Equal(job0.Name, "serviceA", "config for job0.Name") - assert.Equal(job0.stoppingWaitEvent, events.Event{events.Stopped, "preStop"}, + assert.Equal(job0.stoppingWaitEvent, events.Event{Code: events.Stopped, Source: "preStop"}, "expected no stopping event for serviceA") // job1 is its preStart @@ -103,7 +105,7 @@ func TestJobConfigServiceWithStopping(t *testing.T) { assert.Equal(job2.Name, "preStop", "config for job2.Name") assert.Equal(job2.exec.Exec, "/bin/to/preStop.sh", "config for preStop.exec.Exec") - assert.Equal(job2.whenEvent, events.Event{events.Stopping, "serviceA"}, + assert.Equal(job2.whenEvent, events.Event{Code: events.Stopping, Source: "serviceA"}, "config for preStop.whenEvent") // job3 is its post-stop @@ -111,7 +113,7 @@ func TestJobConfigServiceWithStopping(t *testing.T) { assert.Equal(job3.Name, "postStop", "config for job3.Name") assert.Equal(job3.exec.Exec, "/bin/to/postStop.sh", "config for postStop.exec.Exec") - assert.Equal(job3.whenEvent, events.Event{events.Stopped, "serviceA"}, + assert.Equal(job3.whenEvent, events.Event{Code: events.Stopped, Source: "serviceA"}, "config for postStop.whenEvent") } @@ -165,7 +167,7 @@ func TestJobConfigConsulExtras(t *testing.T) { } func TestJobConfigSmokeTest(t *testing.T) { - data, _ := ioutil.ReadFile(fmt.Sprintf("./testdata/%s.json5", t.Name())) + data, _ := os.ReadFile(fmt.Sprintf("./testdata/%s.json5", t.Name())) testCfg := tests.DecodeRawToSlice(string(data)) assert := assert.New(t) @@ -285,7 +287,7 @@ func TestJobConfigValidateDiscovery(t *testing.T) { } func TestErrJobConfigConsulEnableTagOverride(t *testing.T) { - testCfg, _ := ioutil.ReadFile(fmt.Sprintf("./testdata/%s.json5", t.Name())) + testCfg, _ := os.ReadFile(fmt.Sprintf("./testdata/%s.json5", t.Name())) _, err := NewConfigs(tests.DecodeRawToSlice(string(testCfg)), noop) if err == nil { t.Errorf("ConsulExtras should have thrown error about EnableTagOverride being a string.") @@ -293,7 +295,7 @@ func TestErrJobConfigConsulEnableTagOverride(t *testing.T) { } func TestErrJobConfigConsulDeregisterCriticalServiceAfter(t *testing.T) { - testCfg, _ := ioutil.ReadFile(fmt.Sprintf("./testdata/%s.json5", t.Name())) + testCfg, _ := os.ReadFile(fmt.Sprintf("./testdata/%s.json5", t.Name())) _, err := NewConfigs(tests.DecodeRawToSlice(string(testCfg)), noop) if err == nil { t.Errorf("error should have been generated for duration 'nope'.") @@ -324,7 +326,7 @@ func TestJobConfigValidateFrequency(t *testing.T) { expectErr( `[{name: "E", exec: "/bin/taskE", timeout: "1ns", when: {interval: "xx"}}]`, - "unable to parse job[E].when.interval 'xx': time: invalid duration xx") + "unable to parse job[serviceC].timeout 'xx': time: invalid duration \"xx\"") testCfg := tests.DecodeRawToSlice( `[{name: "F", exec: "/bin/taskF", when: {interval: "1ms"}}]`) @@ -379,7 +381,7 @@ func TestJobConfigValidateExec(t *testing.T) { timeout: "xx" }]`) _, err = NewConfigs(testCfg, noop) - expected := "unable to parse job[serviceC].timeout 'xx': time: invalid duration xx" + expected := "unable to parse job[serviceC].timeout 'xx': time: invalid duration \"xx\"" if err == nil || err.Error() != expected { t.Fatalf("expected '%s', got '%v'", expected, err) } @@ -469,7 +471,7 @@ func TestHealthChecksConfigError(t *testing.T) { var noop = &mocks.NoopDiscoveryBackend{} func loadTestConfig(t *testing.T) []*Config { - data, _ := ioutil.ReadFile(fmt.Sprintf("./testdata/%s.json5", t.Name())) + data, _ := os.ReadFile(fmt.Sprintf("./testdata/%s.json5", t.Name())) testCfg := tests.DecodeRawToSlice(string(data)) jobs, err := NewConfigs(testCfg, noop) diff --git a/jobs/jobs.go b/jobs/jobs.go index 677e5dab..1a0d6bdd 100644 --- a/jobs/jobs.go +++ b/jobs/jobs.go @@ -33,7 +33,8 @@ type Job struct { statusLock *sync.RWMutex Service *discovery.ServiceDefinition healthCheckExec *commands.Command - healthCheckName string + // staticcheck U1000 field is unused + //healthCheckName string // starting events startEvent events.Event @@ -155,7 +156,7 @@ func (job *Job) Run(pctx context.Context, completedCh chan struct{}) { if job.startTimeout > 0 { timeoutName := fmt.Sprintf("%s.wait-timeout", job.Name) events.NewEventTimeout(ctx, job.Rx, job.startTimeout, timeoutName) - job.startTimeoutEvent = events.Event{events.TimerExpired, timeoutName} + job.startTimeoutEvent = events.Event{Code: events.TimerExpired, Source: timeoutName} } else { job.startTimeoutEvent = events.NonEvent } @@ -278,7 +279,7 @@ func (job *Job) onRunEveryTimerExpired(ctx context.Context) processEventStatus { func (job *Job) onHealthCheckFailed(ctx context.Context) processEventStatus { if job.GetStatus() != statusMaintenance { job.setStatus(statusUnhealthy) - job.Publish(events.Event{events.StatusUnhealthy, job.Name}) + job.Publish(events.Event{Code: events.StatusUnhealthy, Source: job.Name}) } return jobContinue } @@ -286,7 +287,7 @@ func (job *Job) onHealthCheckFailed(ctx context.Context) processEventStatus { func (job *Job) onHealthCheckPassed(ctx context.Context) processEventStatus { if job.GetStatus() != statusMaintenance { job.setStatus(statusHealthy) - job.Publish(events.Event{events.StatusHealthy, job.Name}) + job.Publish(events.Event{Code: events.StatusHealthy, Source: job.Name}) job.SendHeartbeat() } return jobContinue @@ -400,7 +401,7 @@ func (job *Job) cleanup(ctx context.Context, cancel context.CancelFunc) { switch event { case job.stoppingWaitEvent: break loop - case events.Event{events.Stopping, stoppingTimeout}: + case events.Event{Code: events.Stopping, Source: stoppingTimeout}: break loop } } diff --git a/jobs/jobs_test.go b/jobs/jobs_test.go index 9160106a..f0e48d4e 100644 --- a/jobs/jobs_test.go +++ b/jobs/jobs_test.go @@ -39,8 +39,8 @@ func TestJobRunSafeClose(t *testing.T) { expected := []events.Event{ events.GlobalStartup, - {events.Stopping, "myjob"}, - {events.Stopped, "myjob"}, + {Code: events.Stopping, Source: "myjob"}, + {Code: events.Stopped, Source: "myjob"}, } if !reflect.DeepEqual(expected, results) { t.Fatalf("expected: %v\ngot: %v", expected, results) @@ -241,7 +241,7 @@ func TestJobMaintenance(t *testing.T) { // in-flight health checks should not bump the Job out of maintenance t.Run("healthy no change", func(t *testing.T) { status := testFunc(t, statusMaintenance, - events.Event{events.ExitSuccess, "check.myjob"}) + events.Event{Code: events.ExitSuccess, Source: "check.myjob"}) assert.Equal(t, statusMaintenance, status, "job status after passing check while in maintenance") }) @@ -249,7 +249,7 @@ func TestJobMaintenance(t *testing.T) { // in-flight health checks should not bump the Job out of maintenance t.Run("unhealthy no change", func(t *testing.T) { status := testFunc(t, statusMaintenance, - events.Event{events.ExitFailed, "check.myjob"}) + events.Event{Code: events.ExitFailed, Source: "check.myjob"}) assert.Equal(t, statusMaintenance, status, "job status after failed check while in maintenance") }) @@ -262,7 +262,7 @@ func TestJobMaintenance(t *testing.T) { t.Run("now healthy", func(t *testing.T) { status := testFunc(t, statusUnknown, - events.Event{events.ExitSuccess, "check.myjob"}) + events.Event{Code: events.ExitSuccess, Source: "check.myjob"}) assert.Equal(t, statusHealthy, status, "job status after passing check out of maintenance") }) @@ -278,11 +278,11 @@ func TestJobProcessEvent(t *testing.T) { // } job := &Job{ Name: "testJob", - startEvent: events.Event{events.StatusChanged, "upstream"}, + startEvent: events.Event{Code: events.StatusChanged, Source: "upstream"}, startsRemain: 1, statusLock: &sync.RWMutex{}, } - got := job.processEvent(nil, events.Event{events.StatusChanged, "upstream"}) + got := job.processEvent(context.TODO(), events.Event{Code: events.StatusChanged, Source: "upstream"}) assert.Equal(t, jobContinue, got, "processEvent after 1st startEvent") assert.Equal(t, statusUnknown, job.Status) assert.Equal(t, 0, job.startsRemain) @@ -299,30 +299,30 @@ func TestJobProcessEvent(t *testing.T) { // restarts: 2 job := &Job{ Name: "testJob", - startEvent: events.Event{events.StatusChanged, "upstream"}, + startEvent: events.Event{Code: events.StatusChanged, Source: "upstream"}, startsRemain: 1, restartLimit: 2, restartsRemain: 2, statusLock: &sync.RWMutex{}, } - got := job.processEvent(nil, events.Event{events.StatusChanged, "upstream"}) + got := job.processEvent(context.TODO(), events.Event{Code: events.StatusChanged, Source: "upstream"}) assert.Equal(t, jobContinue, got, "processEvent after 1st startEvent") assert.Equal(t, statusUnknown, job.Status) assert.Equal(t, 0, job.startsRemain) assert.Equal(t, events.NonEvent, job.startEvent) - got = job.processEvent(nil, events.Event{events.StatusChanged, "upstream"}) + got = job.processEvent(context.TODO(), events.Event{Code: events.StatusChanged, Source: "upstream"}) assert.Equal(t, jobContinue, got, "processEvent after 2nd startEvent") - got = job.processEvent(nil, events.Event{events.ExitSuccess, "testJob"}) + got = job.processEvent(context.TODO(), events.Event{Code: events.ExitSuccess, Source: "testJob"}) assert.Equal(t, jobContinue, got, "processEvent after 1st exit") assert.Equal(t, 1, job.restartsRemain) - got = job.processEvent(nil, events.Event{events.ExitSuccess, "testJob"}) + got = job.processEvent(context.TODO(), events.Event{Code: events.ExitSuccess, Source: "testJob"}) assert.Equal(t, jobContinue, got, "processEvent after 2nd exit") assert.Equal(t, 0, job.restartsRemain) - got = job.processEvent(nil, events.Event{events.ExitSuccess, "testJob"}) + got = job.processEvent(context.TODO(), events.Event{Code: events.ExitSuccess, Source: "testJob"}) assert.Equal(t, jobHalt, got, "processEvent after 3rd exit") }) @@ -337,26 +337,26 @@ func TestJobProcessEvent(t *testing.T) { // restarts: "unlimited" job := &Job{ Name: "testJob", - startEvent: events.Event{events.StatusChanged, "upstream"}, + startEvent: events.Event{Code: events.StatusChanged, Source: "upstream"}, startsRemain: 1, restartLimit: -1, restartsRemain: -1, statusLock: &sync.RWMutex{}, } - got := job.processEvent(nil, events.Event{events.StatusChanged, "upstream"}) + got := job.processEvent(context.TODO(), events.Event{Code: events.StatusChanged, Source: "upstream"}) assert.Equal(t, jobContinue, got, "processEvent after 1st startEvent") assert.Equal(t, statusUnknown, job.Status) assert.Equal(t, 0, job.startsRemain) assert.Equal(t, events.NonEvent, job.startEvent) - got = job.processEvent(nil, events.Event{events.StatusChanged, "upstream"}) + got = job.processEvent(context.TODO(), events.Event{Code: events.StatusChanged, Source: "upstream"}) assert.Equal(t, jobContinue, got, "processEvent after 2nd startEvent") - got = job.processEvent(nil, events.Event{events.ExitSuccess, "testJob"}) + got = job.processEvent(context.TODO(), events.Event{Code: events.ExitSuccess, Source: "testJob"}) assert.Equal(t, jobContinue, got, "processEvent after 1st exit") assert.Equal(t, -2, job.restartsRemain) - got = job.processEvent(nil, events.Event{events.ExitSuccess, "testJob"}) + got = job.processEvent(context.TODO(), events.Event{Code: events.ExitSuccess, Source: "testJob"}) assert.Equal(t, jobContinue, got, "processEvent after 2nd exit") assert.Equal(t, -3, job.restartsRemain) }) @@ -369,26 +369,26 @@ func TestJobProcessEvent(t *testing.T) { // restarts: "none" job := &Job{ Name: "testJob", - startEvent: events.Event{events.StatusChanged, "upstream"}, + startEvent: events.Event{Code: events.StatusChanged, Source: "upstream"}, startsRemain: unlimited, statusLock: &sync.RWMutex{}, } - got := job.processEvent(nil, events.Event{events.StatusChanged, "upstream"}) + got := job.processEvent(context.TODO(), events.Event{Code: events.StatusChanged, Source: "upstream"}) assert.Equal(t, jobContinue, got, "processEvent after 1st startEvent") assert.Equal(t, statusUnknown, job.Status) assert.Equal(t, unlimited, job.startsRemain) - assert.Equal(t, events.Event{events.StatusChanged, "upstream"}, job.startEvent) + assert.Equal(t, events.Event{Code: events.StatusChanged, Source: "upstream"}, job.startEvent) - got = job.processEvent(nil, events.Event{events.StatusChanged, "upstream"}) + got = job.processEvent(context.TODO(), events.Event{Code: events.StatusChanged, Source: "upstream"}) assert.Equal(t, jobContinue, got, "processEvent after 2nd startEvent") - got = job.processEvent(nil, events.Event{events.ExitSuccess, "testJob"}) + got = job.processEvent(context.TODO(), events.Event{Code: events.ExitSuccess, Source: "testJob"}) assert.Equal(t, jobContinue, got, "processEvent after exit") assert.Equal(t, statusUnknown, job.Status) assert.Equal(t, unlimited, job.startsRemain) - assert.Equal(t, events.Event{events.StatusChanged, "upstream"}, job.startEvent) + assert.Equal(t, events.Event{Code: events.StatusChanged, Source: "upstream"}, job.startEvent) - got = job.processEvent(nil, events.Event{events.StatusChanged, "upstream"}) + got = job.processEvent(context.TODO(), events.Event{Code: events.StatusChanged, Source: "upstream"}) assert.Equal(t, jobContinue, got, "processEvent after 3rd startEvent") }) @@ -402,16 +402,16 @@ func TestJobProcessEvent(t *testing.T) { restartsRemain: unlimited, statusLock: &sync.RWMutex{}, } - got := job.processEvent(nil, events.GlobalStartup) + job.processEvent(context.TODO(), events.GlobalStartup) assert.Equal(t, statusUnknown, job.Status) assert.Equal(t, 0, job.startsRemain) assert.Equal(t, events.NonEvent, job.startEvent) // should return False after each exit which means we don't stop job - got = job.processEvent(nil, events.Event{events.ExitSuccess, "testJob"}) + got := job.processEvent(context.TODO(), events.Event{Code: events.ExitSuccess, Source: "testJob"}) assert.Equal(t, jobContinue, got, "processEvent after 1st exit") - got = job.processEvent(nil, events.Event{events.ExitSuccess, "testJob"}) + got = job.processEvent(context.TODO(), events.Event{Code: events.ExitSuccess, Source: "testJob"}) assert.Equal(t, jobContinue, got, "processEvent after 2nd exit") }) @@ -425,15 +425,15 @@ func TestJobProcessEvent(t *testing.T) { restartsRemain: 1, statusLock: &sync.RWMutex{}, } - got := job.processEvent(nil, events.GlobalStartup) + job.processEvent(context.TODO(), events.GlobalStartup) assert.Equal(t, statusUnknown, job.Status) assert.Equal(t, 0, job.startsRemain) assert.Equal(t, events.NonEvent, job.startEvent) - got = job.processEvent(nil, events.Event{events.ExitSuccess, "testJob"}) + got := job.processEvent(context.TODO(), events.Event{Code: events.ExitSuccess, Source: "testJob"}) assert.Equal(t, jobContinue, got, "processEvent after 1st exit") - got = job.processEvent(nil, events.Event{events.ExitSuccess, "testJob"}) + got = job.processEvent(context.TODO(), events.Event{Code: events.ExitSuccess, Source: "testJob"}) assert.Equal(t, jobHalt, got, "processEvent after 2nd exit") }) @@ -443,7 +443,7 @@ func TestJobProcessEvent(t *testing.T) { // each: "changed" // }, // restart: "unlimited" - startEvent := events.Event{events.StatusChanged, "upstream"} + startEvent := events.Event{Code: events.StatusChanged, Source: "upstream"} job := &Job{ Name: "testJob", startEvent: startEvent, @@ -451,19 +451,19 @@ func TestJobProcessEvent(t *testing.T) { restartsRemain: unlimited, statusLock: &sync.RWMutex{}, } - got := job.processEvent(nil, startEvent) + got := job.processEvent(context.TODO(), startEvent) assert.Equal(t, jobContinue, got, "processEvent after 1st startEvent") assert.Equal(t, statusUnknown, job.Status) assert.Equal(t, unlimited, job.startsRemain) assert.Equal(t, startEvent, job.startEvent) - got = job.processEvent(nil, startEvent) + got = job.processEvent(context.TODO(), startEvent) assert.Equal(t, jobContinue, got, "processEvent after 2nd startEvent") - got = job.processEvent(nil, events.Event{events.ExitSuccess, "testJob"}) + got = job.processEvent(context.TODO(), events.Event{Code: events.ExitSuccess, Source: "testJob"}) assert.Equal(t, jobContinue, got, "processEvent after exit") - got = job.processEvent(nil, startEvent) + got = job.processEvent(context.TODO(), startEvent) assert.Equal(t, jobContinue, got, "processEvent after 3rd startEvent") }) diff --git a/jobs/testdata/TestJobConfigServiceWithPreStart.json5 b/jobs/testdata/TestJobConfigServiceWithPreStart.json5 index 1d501f66..05822b3d 100644 --- a/jobs/testdata/TestJobConfigServiceWithPreStart.json5 +++ b/jobs/testdata/TestJobConfigServiceWithPreStart.json5 @@ -2,6 +2,8 @@ { name: "serviceA", port: 8080, + uid: 1, + gid: 1, interfaces: ["inet", "lo0"], exec: "/bin/serviceA.sh", when: { diff --git a/jobs/testdata/test.sh b/jobs/testdata/test.sh index c05a728d..68b2e59b 100755 --- a/jobs/testdata/test.sh +++ b/jobs/testdata/test.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash trap 'exit 2' SIGTERM diff --git a/jobs/testdata/test_coprocess.sh b/jobs/testdata/test_coprocess.sh index ab27a0b3..4bcf6e3d 100755 --- a/jobs/testdata/test_coprocess.sh +++ b/jobs/testdata/test_coprocess.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash trap 'exit 2' SIGTERM diff --git a/jobs/testdata/test_tasks.sh b/jobs/testdata/test_tasks.sh index ab27a0b3..4bcf6e3d 100755 --- a/jobs/testdata/test_tasks.sh +++ b/jobs/testdata/test_tasks.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash trap 'exit 2' SIGTERM diff --git a/makefile b/makefile index b95baf74..5f51aa82 100644 --- a/makefile +++ b/makefile @@ -1,5 +1,5 @@ MAKEFLAGS += --warn-undefined-variables -SHELL := /bin/bash +SHELL := bash .SHELLFLAGS := -o pipefail -euc .DEFAULT_GOAL := build @@ -11,7 +11,7 @@ LDFLAGS := -X ${IMPORT_PATH}/version.GitHash=$(shell git rev-parse --short HEAD) ROOT := $(shell pwd) RUNNER := -v ${ROOT}:/go/src/${IMPORT_PATH} -w /go/src/${IMPORT_PATH} containerpilot_build -docker := docker run --rm -e LDFLAGS="${LDFLAGS}" $(RUNNER) +docker := docker run --disable-content-trust --rm -e LDFLAGS="${LDFLAGS}" $(RUNNER) export PATH :=$(PATH):$(GOPATH)/bin # flags for local development @@ -21,8 +21,7 @@ GOARCH ?= $(shell go env GOARCH) CGO_ENABLED := 0 GOEXPERIMENT := framepointer -CONSUL_VERSION := 1.0.0 -GLIDE_VERSION := 0.12.3 +CONSUL_VERSION := 1.6.3 ## display this help message help: @@ -41,7 +40,7 @@ help: ## build the ContainerPilot binary build: build/containerpilot -build/containerpilot: build/containerpilot_build build/glide-installed */*/*.go */*.go */*/*.go *.go +build/containerpilot: build/containerpilot_build */*/*.go */*.go */*/*.go *.go $(docker) go build -o build/containerpilot -ldflags "$(LDFLAGS)" @rm -rf src || true @@ -66,7 +65,7 @@ release: build ## remove build/test artifacts, test fixtures, and vendor directories clean: - rm -rf build release cover vendor .glide + rm -rf build release cover vendor docker rmi -f containerpilot_build > /dev/null 2>&1 || true docker rm -f containerpilot_consul > /dev/null 2>&1 || true ./scripts/test.sh clean @@ -76,19 +75,18 @@ clean: # NOTE: glide will be replaced with `dep` when its production-ready # ref https://github.com/golang/dep -## install any changed packages in the glide.yaml -vendor: build/glide-installed -build/glide-installed: build/containerpilot_build glide.yaml - $(docker) glide install - mkdir -p vendor - @echo date > build/glide-installed +## install any changed packages in go.mod and go.sum +vendor: build/update +build/update: build/containerpilot_build go.mod + $(docker) go get -u ./... + @echo date > build/update -## install all vendored packages in the glide.yaml +## install all dependencies dep-install: - mkdir -p vendor - $(docker) glide install - @echo date > build/glide-installed + $(docker) go get -u ./... + @echo date > build/update +# Deprecated with go modules: # usage DEP=github.com/owner/package make dep-add ## fetch a dependency and vendor it via `glide` dep-add: build/containerpilot_build @@ -97,12 +95,9 @@ dep-add: build/containerpilot_build # run 'GOOS=darwin make tools' if you're installing on MacOS ## set up local dev environment tools: - @go version | grep 1.9 || (echo 'WARNING: go1.9 should be installed!') + @go version | grep 1.13 || (echo 'WARNING: go1.13 should be installed!') @$(if $(value GOPATH),, $(error 'GOPATH not set')) - go get github.com/golang/lint/golint - curl --fail -Lso glide.tgz "https://github.com/Masterminds/glide/releases/download/v$(GLIDE_VERSION)/glide-v$(GLIDE_VERSION)-$(GOOS)-$(GOARCH).tar.gz" - tar -C "$(GOPATH)/bin" -xzf glide.tgz --strip=1 $(GOOS)-$(GOARCH)/glide - rm glide.tgz + go get golang.org/x/lint/golint curl --fail -Lso consul.zip "https://releases.hashicorp.com/consul/$(CONSUL_VERSION)/consul_$(CONSUL_VERSION)_$(GOOS)_$(GOARCH).zip" unzip consul.zip -d "$(GOPATH)/bin" rm consul.zip @@ -135,7 +130,7 @@ local: | quiet quiet: # this is silly but shuts up 'Nothing to be done for `local`' @: -## run `go lint` and other code quality tools +## run `go vet`, `staticcheck` and other code quality tools lint: $(docker) bash ./scripts/lint.sh diff --git a/scripts/add_dep.sh b/scripts/add_dep.sh index 17833c13..e03a1489 100755 --- a/scripts/add_dep.sh +++ b/scripts/add_dep.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -x if [ -z "$DEP" ]; then echo "No dependency provided. Expected: DEP=" diff --git a/scripts/cover.sh b/scripts/cover.sh index 5c05484f..94be0357 100755 --- a/scripts/cover.sh +++ b/scripts/cover.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash OUT=${OUT:-cover/cover.out} TMP=${TMP:-cover/temp.out} echo "mode: set" > $OUT diff --git a/scripts/install_consul.sh b/scripts/install_consul.sh new file mode 100644 index 00000000..1b3bc08b --- /dev/null +++ b/scripts/install_consul.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash +# Helper bash script to install consul in drone CI environment, as it does not yet support +# building local docker images. See: https://github.com/drone/drone/issues/2462 + +CONSUL_VERSION=1.11.4 + +apt-get update \ + && apt-get install -y unzip \ + && go install honnef.co/go/tools/cmd/staticcheck@latest + +CONSUL_CHECKSUM=5155f6a3b7ff14d3671b0516f6b7310530b509a2b882b95b4fdf25f4219342c8 +archive=consul_${CONSUL_VERSION}_linux_amd64.zip +curl -Lso /tmp/${archive} https://releases.hashicorp.com/consul/${CONSUL_VERSION}/${archive} +echo "${CONSUL_CHECKSUM} /tmp/${archive}" | sha256sum -c +cd /bin +unzip /tmp/${archive} +chmod +x /bin/consul +rm /tmp/${archive} diff --git a/scripts/lint.sh b/scripts/lint.sh index 0e78dc99..aba0bd80 100755 --- a/scripts/lint.sh +++ b/scripts/lint.sh @@ -1,8 +1,8 @@ -#!/bin/bash +#!/usr/bin/env bash result=0 -for pkg in $(glide novendor) +for pkg in $(go list ./...) do - golint -set_exit_status "$pkg" || result=1 + staticcheck "$pkg" || result=1 go vet "$pkg" || result=1 done exit $result diff --git a/scripts/test.sh b/scripts/test.sh index 9c3d2901..3f23aff9 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )/.." && pwd )" DEBUG_LOG=${DEBUG_LOG:-"/dev/null"} diff --git a/scripts/unit_test.sh b/scripts/unit_test.sh index 226cac6c..c3638a87 100755 --- a/scripts/unit_test.sh +++ b/scripts/unit_test.sh @@ -1,3 +1,3 @@ -#!/bin/bash +#!/usr/bin/env bash go test -v $(go list ./... | grep -v '/vendor\|_test' | sed 's+_/'$(pwd)'+github.com/joyent/containerpilot+') -bench . diff --git a/shell.nix b/shell.nix new file mode 100644 index 00000000..365e7f2f --- /dev/null +++ b/shell.nix @@ -0,0 +1,11 @@ +{ pkgs ? import {} }: + +pkgs.mkShell { + buildInputs = with pkgs; [ + gnumake + go_1_17 + ]; + shellHook = '' + echo "Ready for go development!" + ''; +} diff --git a/subcommands/subcommands.go b/subcommands/subcommands.go index 4c5c7c72..8feecf0e 100644 --- a/subcommands/subcommands.go +++ b/subcommands/subcommands.go @@ -130,8 +130,10 @@ func initClient(configPath string) (*client.HTTPClient, error) { // we're using an interface{} for params for Handler but these should // never fail to type-assert. so this assert should be unreachable // unless we've screwed something up. -func assert(ok bool, msg string) { - if !ok { - panic("invalid parameter types for %v") - } -} +// +// staticcheck U1000 func unused +//func assert(ok bool, msg string) { +// if !ok { +// panic("invalid parameter types for %v") +// } +//} diff --git a/telemetry/metrics_test.go b/telemetry/metrics_test.go index c8942182..13ab0bef 100644 --- a/telemetry/metrics_test.go +++ b/telemetry/metrics_test.go @@ -3,7 +3,7 @@ package telemetry import ( "context" "fmt" - "io/ioutil" + "io" "net/http" "net/http/httptest" "reflect" @@ -12,6 +12,7 @@ import ( "testing" "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/stretchr/testify/assert" "github.com/joyent/containerpilot/events" @@ -25,7 +26,7 @@ the prometheus handler and then check the results of a GET. */ func TestMetricRun(t *testing.T) { - testServer := httptest.NewServer(prometheus.UninstrumentedHandler()) + testServer := httptest.NewServer(promhttp.Handler()) defer testServer.Close() cfg := &MetricConfig{ Namespace: "telemetry", @@ -41,7 +42,7 @@ func TestMetricRun(t *testing.T) { ctx := context.Background() metric.Run(ctx, bus) - record := events.Event{events.Metric, fmt.Sprintf("%s|84", metric.Name)} + record := events.Event{Code: events.Metric, Source: fmt.Sprintf("%s|84", metric.Name)} bus.Publish(record) metric.Receive(events.QuitByTest) @@ -61,7 +62,7 @@ func TestMetricRun(t *testing.T) { // TestMetricProcessMetric covers the same ground as the 4 collector- // specific tests below, but checks the unhappy path. func TestMetricProcessMetric(t *testing.T) { - testServer := httptest.NewServer(prometheus.UninstrumentedHandler()) + testServer := httptest.NewServer(promhttp.Handler()) defer testServer.Close() cfg := &MetricConfig{ Namespace: "telemetry", @@ -106,7 +107,7 @@ func TestMetricProcessMetric(t *testing.T) { } func TestMetricRecordCounter(t *testing.T) { - testServer := httptest.NewServer(prometheus.UninstrumentedHandler()) + testServer := httptest.NewServer(promhttp.Handler()) defer testServer.Close() metric := &Metric{ Type: Counter, @@ -135,7 +136,7 @@ func TestMetricRecordCounter(t *testing.T) { } func TestMetricRecordGauge(t *testing.T) { - testServer := httptest.NewServer(prometheus.UninstrumentedHandler()) + testServer := httptest.NewServer(promhttp.Handler()) defer testServer.Close() metric := &Metric{ Type: Gauge, @@ -165,7 +166,7 @@ func TestMetricRecordGauge(t *testing.T) { } func TestMetricRecordHistogram(t *testing.T) { - testServer := httptest.NewServer(prometheus.UninstrumentedHandler()) + testServer := httptest.NewServer(promhttp.Handler()) defer testServer.Close() metric := &Metric{ @@ -202,15 +203,18 @@ func TestMetricRecordHistogram(t *testing.T) { } func TestMetricRecordSummary(t *testing.T) { - testServer := httptest.NewServer(prometheus.UninstrumentedHandler()) + testServer := httptest.NewServer(promhttp.Handler()) defer testServer.Close() + objMap := map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001} + metric := &Metric{ Type: Summary, collector: prometheus.NewSummary(prometheus.SummaryOpts{ - Namespace: "telemetry", - Subsystem: "metrics", - Name: "TestMetricRecordSummary", - Help: "help", + Namespace: "telemetry", + Subsystem: "metrics", + Name: "TestMetricRecordSummary", + Help: "help", + Objectives: objMap, })} prometheus.MustRegister(metric.collector) patt := `telemetry_metrics_TestMetricRecordSummary{quantile="([\.0-9]*)"} ([0-9\.]*)` @@ -266,7 +270,7 @@ func getFromTestServer(t *testing.T, testServer *httptest.Server) string { t.Fatal(err) } else { defer res.Body.Close() - if resp, err := ioutil.ReadAll(res.Body); err != nil { + if resp, err := io.ReadAll(res.Body); err != nil { t.Fatal(err) } else { response := string(resp) diff --git a/telemetry/status.go b/telemetry/status.go index 8746887c..79ecc920 100644 --- a/telemetry/status.go +++ b/telemetry/status.go @@ -2,7 +2,6 @@ package telemetry import ( "encoding/json" - "fmt" "net/http" "strings" @@ -51,7 +50,7 @@ func (sh StatusHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { return } for _, job := range sh.telem.Status.jobs { - status := fmt.Sprintf("%s", job.GetStatus()) + status := job.GetStatus().String() for _, service := range sh.telem.Status.Services { if service.Name == job.Name { service.Status = status @@ -78,13 +77,13 @@ func (t *Telemetry) MonitorJobs(jobs []*jobs.Job) { Name: job.Name, Address: job.Service.IPAddress, Port: job.Service.Port, - Status: fmt.Sprintf("%s", job.GetStatus()), + Status: job.GetStatus().String(), } t.Status.Services = append(t.Status.Services, serviceResponse) } else { jobResponse := &jobStatusResponse{ Name: job.Name, - Status: fmt.Sprintf("%s", job.GetStatus()), + Status: job.GetStatus().String(), } t.Status.Jobs = append(t.Status.Jobs, jobResponse) } diff --git a/telemetry/telemetry.go b/telemetry/telemetry.go index a5ab52fa..1fcaf9e5 100644 --- a/telemetry/telemetry.go +++ b/telemetry/telemetry.go @@ -8,7 +8,7 @@ import ( "net/http" "time" - "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promhttp" log "github.com/sirupsen/logrus" "github.com/joyent/containerpilot/version" @@ -21,8 +21,10 @@ type Telemetry struct { Status *Status // supports '/status' endpoint fields // server - router *http.ServeMux - addr net.TCPAddr + + // staticcheck U1000 field is unused + //router *http.ServeMux + addr net.TCPAddr http.Server } @@ -39,7 +41,7 @@ func NewTelemetry(cfg *Config) *Telemetry { t.addr = cfg.addr router := http.NewServeMux() - router.Handle("/metrics", prometheus.Handler()) + router.Handle("/metrics", promhttp.Handler()) router.Handle("/status", NewStatusHandler(t)) t.Handler = router @@ -57,7 +59,6 @@ func (t *Telemetry) Run(ctx context.Context) { go func() { defer t.Stop(ctx) <-ctx.Done() - return }() } diff --git a/telemetry/telemetry_config_test.go b/telemetry/telemetry_config_test.go index 51ccf377..b2cb4dfc 100644 --- a/telemetry/telemetry_config_test.go +++ b/telemetry/telemetry_config_test.go @@ -2,7 +2,7 @@ package telemetry import ( "fmt" - "io/ioutil" + "os" "strings" "testing" @@ -13,7 +13,7 @@ import ( ) func TestTelemetryConfigParse(t *testing.T) { - data, _ := ioutil.ReadFile(fmt.Sprintf("./testdata/%s.json5", t.Name())) + data, _ := os.ReadFile(fmt.Sprintf("./testdata/%s.json5", t.Name())) testCfg := tests.DecodeRaw(string(data)) telem, err := NewConfig(testCfg, &mocks.NoopDiscoveryBackend{}) if err != nil { diff --git a/telemetry/testdata/test.sh b/telemetry/testdata/test.sh index b3696f3b..ace995b3 100755 --- a/telemetry/testdata/test.sh +++ b/telemetry/testdata/test.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash trap 'exit 2' SIGTERM diff --git a/watches/config_test.go b/watches/config_test.go index f034afc0..3e644c3b 100644 --- a/watches/config_test.go +++ b/watches/config_test.go @@ -2,7 +2,7 @@ package watches import ( "fmt" - "io/ioutil" + "os" "testing" "github.com/stretchr/testify/assert" @@ -11,7 +11,7 @@ import ( ) func TestWatchesParse(t *testing.T) { - data, _ := ioutil.ReadFile(fmt.Sprintf("./testdata/%s.json5", t.Name())) + data, _ := os.ReadFile(fmt.Sprintf("./testdata/%s.json5", t.Name())) testCfg := tests.DecodeRawToSlice(string(data)) watches, err := NewConfigs(testCfg, nil) if err != nil { diff --git a/watches/testdata/test.sh b/watches/testdata/test.sh index c05a728d..68b2e59b 100755 --- a/watches/testdata/test.sh +++ b/watches/testdata/test.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash trap 'exit 2' SIGTERM diff --git a/watches/watches.go b/watches/watches.go index b549b7c3..105576fa 100644 --- a/watches/watches.go +++ b/watches/watches.go @@ -82,16 +82,16 @@ func (watch *Watch) Run(pctx context.Context, bus *events.EventBus) { if !ok || event == events.QuitByTest { return } - if event == (events.Event{events.TimerExpired, timerSource}) { + if event == (events.Event{Code: events.TimerExpired, Source: timerSource}) { didChange, isHealthy := watch.CheckForUpstreamChanges() if didChange { - watch.Publish(events.Event{events.StatusChanged, watch.Name}) + watch.Publish(events.Event{Code: events.StatusChanged, Source: watch.Name}) // we only send the StatusHealthy and StatusUnhealthy // events if there was a change if isHealthy { - watch.Publish(events.Event{events.StatusHealthy, watch.Name}) + watch.Publish(events.Event{Code: events.StatusHealthy, Source: watch.Name}) } else { - watch.Publish(events.Event{events.StatusUnhealthy, watch.Name}) + watch.Publish(events.Event{Code: events.StatusUnhealthy, Source: watch.Name}) } } } diff --git a/watches/watches_test.go b/watches/watches_test.go index 1dcb42bc..9caf196c 100644 --- a/watches/watches_test.go +++ b/watches/watches_test.go @@ -18,8 +18,8 @@ func TestWatchPollOk(t *testing.T) { // this discovery backend will always return true when we check // it for changed got := runWatchTest(cfg, 5, &mocks.NoopDiscoveryBackend{Val: true}) - changed := events.Event{events.StatusChanged, "watch.mywatchOk"} - healthy := events.Event{events.StatusHealthy, "watch.mywatchOk"} + changed := events.Event{Code: events.StatusChanged, Source: "watch.mywatchOk"} + healthy := events.Event{Code: events.StatusHealthy, Source: "watch.mywatchOk"} if got[changed] != 1 || got[healthy] != 1 { t.Fatalf("expected 2 successful StatusHealthy events but got %v", got) } @@ -31,8 +31,8 @@ func TestWatchPollFail(t *testing.T) { Poll: 1, } got := runWatchTest(cfg, 3, &mocks.NoopDiscoveryBackend{Val: false}) - changed := events.Event{events.StatusChanged, "watch.mywatchFail"} - unhealthy := events.Event{events.StatusUnhealthy, "watch.mywatchFail"} + changed := events.Event{Code: events.StatusChanged, Source: "watch.mywatchFail"} + unhealthy := events.Event{Code: events.StatusUnhealthy, Source: "watch.mywatchFail"} if got[changed] != 0 || got[unhealthy] != 0 { t.Fatalf("expected 2 failed poll events without changes, but got %v", got) } @@ -44,7 +44,7 @@ func runWatchTest(cfg *Config, count int, disc discovery.Backend) map[events.Eve watch := NewWatch(cfg) ctx := context.Background() watch.Run(ctx, bus) - poll := events.Event{events.TimerExpired, fmt.Sprintf("%s.poll", cfg.Name)} + poll := events.Event{Code: events.TimerExpired, Source: fmt.Sprintf("%s.poll", cfg.Name)} watch.Receive(poll) watch.Receive(poll) // Ensure we can run it more than once watch.Receive(events.QuitByTest)