Compare commits
14 Commits
master
...
gitea-rele
Author | SHA1 | Date | |
---|---|---|---|
0b2078c6c5 | |||
0ed96f6c22 | |||
8c12e80ad2 | |||
f6a9199f25 | |||
3c2cae3011 | |||
f9c082d30f | |||
5bb4da6178 | |||
d810bcb61c | |||
e7353bb8df | |||
06ea8bea30 | |||
2e5ab23bd1 | |||
aa741eb49e | |||
96c9b7d74c | |||
31336280e6 |
52
.drone.yml
52
.drone.yml
@ -4,7 +4,7 @@ name: test
|
||||
|
||||
steps:
|
||||
- name: test
|
||||
image: golang:1.20
|
||||
image: golang:1.12
|
||||
environment:
|
||||
VERSION: ${DRONE_TAG:-${DRONE_COMMIT}}
|
||||
commands:
|
||||
@ -30,7 +30,7 @@ trigger:
|
||||
|
||||
steps:
|
||||
- name: build all binaries
|
||||
image: golang:1.20
|
||||
image: golang:1.12
|
||||
environment:
|
||||
VERSION: ${DRONE_TAG:-${DRONE_COMMIT}}
|
||||
commands:
|
||||
@ -60,15 +60,51 @@ steps:
|
||||
when:
|
||||
event: tag
|
||||
|
||||
- name: Build and publish docker images
|
||||
image: thegeeklab/drone-docker-buildx
|
||||
- name: push image - arm
|
||||
image: plugins/docker
|
||||
settings:
|
||||
repo: iamthefij/minitor-go
|
||||
auto_tag: true
|
||||
platforms:
|
||||
- linux/amd64
|
||||
- linux/arm64
|
||||
- linux/arm
|
||||
auto_tag_suffix: linux-arm
|
||||
username:
|
||||
from_secret: docker_username
|
||||
password:
|
||||
from_secret: docker_password
|
||||
build_args:
|
||||
- ARCH=arm
|
||||
- REPO=arm32v7
|
||||
|
||||
- name: push image - arm64
|
||||
image: plugins/docker
|
||||
settings:
|
||||
repo: iamthefij/minitor-go
|
||||
auto_tag: true
|
||||
auto_tag_suffix: linux-arm64
|
||||
username:
|
||||
from_secret: docker_username
|
||||
password:
|
||||
from_secret: docker_password
|
||||
build_args:
|
||||
- ARCH=arm64
|
||||
- REPO=arm64v8
|
||||
|
||||
- name: push image - amd64
|
||||
image: plugins/docker
|
||||
settings:
|
||||
repo: iamthefij/minitor-go
|
||||
auto_tag: true
|
||||
auto_tag_suffix: linux-amd64
|
||||
username:
|
||||
from_secret: docker_username
|
||||
password:
|
||||
from_secret: docker_password
|
||||
|
||||
- name: publish manifest
|
||||
image: plugins/manifest
|
||||
settings:
|
||||
spec: manifest.tmpl
|
||||
auto_tag: true
|
||||
ignore_missing: true
|
||||
username:
|
||||
from_secret: docker_username
|
||||
password:
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -17,5 +17,4 @@ config.yml
|
||||
|
||||
# Output binary
|
||||
minitor
|
||||
minitor-go
|
||||
dist/
|
||||
|
@ -1,36 +0,0 @@
|
||||
---
|
||||
linters:
|
||||
enable:
|
||||
- errname
|
||||
- errorlint
|
||||
- exhaustive
|
||||
- gofumpt
|
||||
- goimports
|
||||
- gomnd
|
||||
- goprintffuncname
|
||||
- misspell
|
||||
- tagliatelle
|
||||
- tenv
|
||||
- testpackage
|
||||
- thelper
|
||||
- tparallel
|
||||
- unconvert
|
||||
- wrapcheck
|
||||
- wsl
|
||||
disable:
|
||||
- gochecknoglobals
|
||||
|
||||
linters-settings:
|
||||
gosec:
|
||||
excludes:
|
||||
- G204
|
||||
tagliatelle:
|
||||
case:
|
||||
rules:
|
||||
yaml: snake
|
||||
|
||||
issues:
|
||||
exclude-rules:
|
||||
- path: _test\.go
|
||||
linters:
|
||||
- gosec
|
@ -1,7 +1,7 @@
|
||||
---
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.4.0
|
||||
rev: v2.4.0
|
||||
hooks:
|
||||
- id: check-added-large-files
|
||||
- id: check-yaml
|
||||
@ -10,11 +10,15 @@ repos:
|
||||
- id: trailing-whitespace
|
||||
- id: end-of-file-fixer
|
||||
- id: check-merge-conflict
|
||||
- repo: https://github.com/golangci/golangci-lint
|
||||
rev: v1.52.2
|
||||
- repo: git://github.com/dnephin/pre-commit-golang
|
||||
rev: v0.3.5
|
||||
hooks:
|
||||
- id: golangci-lint
|
||||
- repo: https://github.com/hadolint/hadolint
|
||||
rev: v2.12.1-beta
|
||||
hooks:
|
||||
- id: hadolint
|
||||
- id: go-fmt
|
||||
- id: go-imports
|
||||
# - id: gometalinter
|
||||
# - id: golangci-lint
|
||||
# - repo: https://github.com/IamTheFij/docker-pre-commit
|
||||
# rev: v2.0.0
|
||||
# hooks:
|
||||
# - id: docker-compose-check
|
||||
# - id: hadolint
|
||||
|
14
Dockerfile
14
Dockerfile
@ -1,10 +1,15 @@
|
||||
FROM alpine:3.18
|
||||
ARG REPO=library
|
||||
FROM multiarch/qemu-user-static:4.2.0-2 as qemu-user-static
|
||||
FROM ${REPO}/alpine:3.10
|
||||
|
||||
# Copying all qemu files because amd64 doesn't exist and cannot condional copy
|
||||
COPY --from=qemu-user-static /usr/bin/qemu-* /usr/bin/
|
||||
|
||||
RUN mkdir /app
|
||||
WORKDIR /app/
|
||||
|
||||
# Add common checking tools
|
||||
RUN apk --no-cache add bash=~5 curl=~8 jq=~1 bind-tools=~9 tzdata~=2024a
|
||||
RUN apk --no-cache add bash=~5.0 curl=~7.66 jq=~1.6
|
||||
|
||||
# Add minitor user for running as non-root
|
||||
RUN addgroup -S minitor && adduser -S minitor -G minitor
|
||||
@ -14,9 +19,8 @@ COPY ./scripts /app/scripts
|
||||
RUN chmod -R 755 /app/scripts
|
||||
|
||||
# Copy minitor in
|
||||
ARG TARGETOS
|
||||
ARG TARGETARCH
|
||||
COPY ./dist/minitor-${TARGETOS}-${TARGETARCH} ./minitor
|
||||
ARG ARCH=amd64
|
||||
COPY ./dist/minitor-linux-${ARCH} ./minitor
|
||||
|
||||
# Drop to non-root user
|
||||
USER minitor
|
||||
|
@ -1,4 +1,7 @@
|
||||
FROM golang:1.20 AS builder
|
||||
ARG REPO=library
|
||||
FROM golang:1.12-alpine AS builder
|
||||
|
||||
RUN apk add --no-cache git=~2
|
||||
|
||||
RUN mkdir /app
|
||||
WORKDIR /app
|
||||
@ -8,13 +11,12 @@ RUN go mod download
|
||||
|
||||
COPY ./*.go /app/
|
||||
|
||||
ARG TARGETOS
|
||||
ARG TARGETARCH
|
||||
ARG ARCH=amd64
|
||||
ARG VERSION=dev
|
||||
ENV CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=${TARGETARCH}
|
||||
ENV CGO_ENABLED=0 GOOS=linux GOARCH=${ARCH}
|
||||
RUN go build -ldflags "-X main.version=${VERSION}" -a -installsuffix nocgo -o minitor .
|
||||
|
||||
FROM alpine:3.18
|
||||
FROM ${REPO}/alpine:3.10
|
||||
RUN mkdir /app
|
||||
WORKDIR /app/
|
||||
|
||||
@ -22,7 +24,7 @@ WORKDIR /app/
|
||||
COPY --from=builder /app/minitor .
|
||||
|
||||
# Add common checking tools
|
||||
RUN apk --no-cache add bash=~5 curl=~8 jq=~1 bind-tools=~9 tzdata~=2024a
|
||||
RUN apk --no-cache add bash=~5.0 curl=~7.66 jq=~1.6
|
||||
|
||||
# Add minitor user for running as non-root
|
||||
RUN addgroup -S minitor && adduser -S minitor -G minitor
|
||||
|
6
Makefile
6
Makefile
@ -1,6 +1,6 @@
|
||||
DOCKER_TAG ?= minitor-go-${USER}
|
||||
VERSION ?= $(shell git describe --tags --dirty)
|
||||
GOFILES = *.go go.mod go.sum
|
||||
GOFILES = *.go
|
||||
# Multi-arch targets are generated from this
|
||||
TARGET_ALIAS = minitor-linux-amd64 minitor-linux-arm minitor-linux-arm64 minitor-darwin-amd64
|
||||
TARGETS = $(addprefix dist/,$(TARGET_ALIAS))
|
||||
@ -79,11 +79,11 @@ $(TARGET_ALIAS):
|
||||
# Arch specific docker build targets
|
||||
.PHONY: docker-build-arm
|
||||
docker-build-arm: dist/minitor-linux-arm
|
||||
docker build --platform linux/arm . -t ${DOCKER_TAG}-linux-arm
|
||||
docker build --build-arg REPO=arm32v7 --build-arg ARCH=arm . -t ${DOCKER_TAG}-linux-arm
|
||||
|
||||
.PHONY: docker-build-arm64
|
||||
docker-build-arm64: dist/minitor-linux-arm64
|
||||
docker build --platform linux/arm64 . -t ${DOCKER_TAG}-linux-arm64
|
||||
docker build --build-arg REPO=arm64v8 --build-arg ARCH=arm64 . -t ${DOCKER_TAG}-linux-arm64
|
||||
|
||||
# Cross run on host architechture
|
||||
.PHONY: docker-run-arm
|
||||
|
53
README.md
53
README.md
@ -46,8 +46,6 @@ docker run -v $PWD/config.yml:/app/config.yml iamthefij/minitor-go:latest
|
||||
|
||||
Images are provided for `amd64`, `arm`, and `arm64` architechtures.
|
||||
|
||||
Timezone configuration for the container is set by passing the `TZ` env variable. Eg. `TZ=America/Los_Angeles`.
|
||||
|
||||
## Configuring
|
||||
|
||||
In this repo, you can explore the `sample-config.yml` file for an example, but the general structure is as follows. It should be noted that environment variable interpolation happens on load of the YAML file.
|
||||
@ -56,10 +54,7 @@ The global configurations are:
|
||||
|
||||
|key|value|
|
||||
|---|---|
|
||||
|`check_interval`|Maximum frequency to run checks for each monitor as duration, eg. 1m2s.|
|
||||
|`default_alert_after`|A default value used as an `alert_after` value for a monitor if not specified or 0.|
|
||||
|`default_alert_down`|Default down alerts to used by a monitor in case none are provided.|
|
||||
|`default_alert_up`|Default up alerts to used by a monitor in case none are provided.|
|
||||
|`check_interval`|Maximum frequency to run checks for each monitor|
|
||||
|`monitors`|List of all monitors. Detailed description below|
|
||||
|`alerts`|List of all alerts. Detailed description below|
|
||||
|
||||
@ -96,38 +91,10 @@ Also, when alerts are executed, they will be passed through Go's format function
|
||||
|`{{.AlertCount}}`|Number of times this monitor has alerted|
|
||||
|`{{.FailureCount}}`|The total number of sequential failed checks for this monitor|
|
||||
|`{{.LastCheckOutput}}`|The last returned value from the check command to either stderr or stdout|
|
||||
|`{{.LastSuccess}}`|The datetime of the last successful check as a go Time struct|
|
||||
|`{{.LastSuccess}}`|The ISO datetime of the last successful check|
|
||||
|`{{.MonitorName}}`|The name of the monitor that failed and triggered the alert|
|
||||
|`{{.IsUp}}`|Indicates if the monitor that is alerting is up or not. Can be used in a conditional message template|
|
||||
|
||||
To provide flexible formatting, the following non-standard functions are available in templates:
|
||||
|
||||
|func|description|
|
||||
|---|---|
|
||||
|`ANSIC <Time>`|Formats provided time in ANSIC format|
|
||||
|`UnixDate <Time>`|Formats provided time in UnixDate format|
|
||||
|`RubyDate <Time>`|Formats provided time in RubyDate format|
|
||||
|`RFC822Z <Time>`|Formats provided time in RFC822Z format|
|
||||
|`RFC850 <Time>`|Formats provided time in RFC850 format|
|
||||
|`RFC1123 <Time>`|Formats provided time in RFC1123 format|
|
||||
|`RFC1123Z <Time>`|Formats provided time in RFC1123Z format|
|
||||
|`RFC3339 <Time>`|Formats provided time in RFC3339 format|
|
||||
|`RFC3339Nano <Time>`|Formats provided time in RFC3339Nano format|
|
||||
|`FormatTime <Time> <string template>`|Formats provided time according to provided template|
|
||||
|`InTZ <Time> <string timezone name>`|Converts provided time to parsed timezone from the provided name|
|
||||
|
||||
For more information, check out the [Go documentation for the time module](https://pkg.go.dev/time@go1.20.7#pkg-constants).
|
||||
|
||||
#### Running alerts on startup
|
||||
|
||||
It's not the best feeling to find out your alerts are broken when you're expecting to be alerted about another failure. To avoid this and provide early insight into broken alerts, it is possible to specify a list of alerts to run when Minitor starts up. This can be done using the command line flag `-startup-alerts`. This flag accepts a comma separated list of strings and will run a test of each of those alerts. Minitor will then respond as it typically does for any failed alert. This can be used to allow you time to correct when initially launching, and to allow schedulers to more easily detect a failed deployment of Minitor.
|
||||
|
||||
Eg.
|
||||
|
||||
```bash
|
||||
minitor -startup-alerts=log_down,log_up -config ./config.yml
|
||||
```
|
||||
|
||||
### Metrics
|
||||
|
||||
Minitor supports exporting metrics for [Prometheus](https://prometheus.io/). Prometheus is an open source tool for reading and querying metrics from different sources. Combined with another tool, [Grafana](https://grafana.com/), it allows building of charts and dashboards. You could also opt to just use Minitor to log check results, and instead do your alerting with Grafana.
|
||||
@ -144,7 +111,7 @@ minitor -metrics -metrics-port 3000
|
||||
|
||||
## Contributing
|
||||
|
||||
Whether you're looking to submit a patch or tell me I broke something, you can contribute through the Github mirror and I can merge PRs back to the source repository.
|
||||
Whether you're looking to submit a patch or just tell me I broke something, you can contribute through the Github mirror and I can merge PRs back to the source repository.
|
||||
|
||||
Primary Repo: https://git.iamthefij.com/iamthefij/minitor.git
|
||||
|
||||
@ -176,25 +143,15 @@ alerts:
|
||||
command: 'echo {{.MonitorName}}'
|
||||
```
|
||||
|
||||
Interval durations have changed from being an integer number of seconds to a duration string supported by Go, for example:
|
||||
|
||||
minitor-py:
|
||||
```yaml
|
||||
check_interval: 90
|
||||
```
|
||||
|
||||
minitor-go:
|
||||
```yaml
|
||||
check_interval: 1m30s
|
||||
```
|
||||
|
||||
For the time being, legacy configs for the Python version of Minitor should be compatible if you apply the `-py-compat` flag when running Minitor. Eventually, this flag will go away when later breaking changes are introduced.
|
||||
|
||||
## Future
|
||||
|
||||
Future, potentially breaking changes
|
||||
|
||||
- [ ] Implement leveled logging (maybe glog or logrus)
|
||||
- [ ] Consider value of templating vs injecting values into Env variables
|
||||
- [ ] Async checking
|
||||
- [ ] Revisit metrics and see if they all make sense
|
||||
- [ ] Consider dropping `alert_up` and `alert_down` in favor of using Go templates that offer more control of messaging (Breaking)
|
||||
- [ ] Use durations rather than seconds checked in event loop (Potentially breaking)
|
||||
|
99
alert.go
99
alert.go
@ -2,21 +2,12 @@ package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"git.iamthefij.com/iamthefij/slog"
|
||||
)
|
||||
|
||||
var (
|
||||
errNoTemplate = errors.New("no template")
|
||||
|
||||
// ErrAlertFailed indicates that an alert failed to send
|
||||
ErrAlertFailed = errors.New("alert failed")
|
||||
)
|
||||
|
||||
// Alert is a config driven mechanism for sending a notice
|
||||
@ -29,12 +20,12 @@ type Alert struct {
|
||||
|
||||
// AlertNotice captures the context for an alert to be sent
|
||||
type AlertNotice struct {
|
||||
MonitorName string
|
||||
AlertCount int16
|
||||
FailureCount int16
|
||||
IsUp bool
|
||||
LastSuccess time.Time
|
||||
MonitorName string
|
||||
LastCheckOutput string
|
||||
LastSuccess time.Time
|
||||
IsUp bool
|
||||
}
|
||||
|
||||
// IsValid returns a boolean indicating if the Alert has been correctly
|
||||
@ -54,64 +45,29 @@ func (alert *Alert) BuildTemplates() error {
|
||||
"{last_success}", "{{.LastSuccess}}",
|
||||
"{monitor_name}", "{{.MonitorName}}",
|
||||
)
|
||||
|
||||
slog.Debugf("Building template for alert %s", alert.Name)
|
||||
|
||||
// Time format func factory
|
||||
tff := func(formatString string) func(time.Time) string {
|
||||
return func(t time.Time) string {
|
||||
return t.Format(formatString)
|
||||
if LogDebug {
|
||||
log.Printf("DEBUG: Building template for alert %s", alert.Name)
|
||||
}
|
||||
}
|
||||
|
||||
// Create some functions for formatting datetimes in popular formats
|
||||
timeFormatFuncs := template.FuncMap{
|
||||
"ANSIC": tff(time.ANSIC),
|
||||
"UnixDate": tff(time.UnixDate),
|
||||
"RubyDate": tff(time.RubyDate),
|
||||
"RFC822Z": tff(time.RFC822Z),
|
||||
"RFC850": tff(time.RFC850),
|
||||
"RFC1123": tff(time.RFC1123),
|
||||
"RFC1123Z": tff(time.RFC1123Z),
|
||||
"RFC3339": tff(time.RFC3339),
|
||||
"RFC3339Nano": tff(time.RFC3339Nano),
|
||||
"FormatTime": func(t time.Time, timeFormat string) string {
|
||||
return t.Format(timeFormat)
|
||||
},
|
||||
"InTZ": func(t time.Time, tzName string) (time.Time, error) {
|
||||
tz, err := time.LoadLocation(tzName)
|
||||
if err != nil {
|
||||
return t, fmt.Errorf("failed to convert time to specified tz: %w", err)
|
||||
}
|
||||
|
||||
return t.In(tz), nil
|
||||
},
|
||||
}
|
||||
|
||||
switch {
|
||||
case alert.commandTemplate == nil && alert.Command.Command != nil:
|
||||
if alert.commandTemplate == nil && alert.Command.Command != nil {
|
||||
alert.commandTemplate = []*template.Template{}
|
||||
for i, cmdPart := range alert.Command.Command {
|
||||
if PyCompat {
|
||||
cmdPart = legacy.Replace(cmdPart)
|
||||
}
|
||||
|
||||
alert.commandTemplate = append(alert.commandTemplate, template.Must(
|
||||
template.New(alert.Name+fmt.Sprint(i)).Funcs(timeFormatFuncs).Parse(cmdPart),
|
||||
template.New(alert.Name+fmt.Sprint(i)).Parse(cmdPart),
|
||||
))
|
||||
}
|
||||
case alert.commandShellTemplate == nil && alert.Command.ShellCommand != "":
|
||||
} else if alert.commandShellTemplate == nil && alert.Command.ShellCommand != "" {
|
||||
shellCmd := alert.Command.ShellCommand
|
||||
|
||||
if PyCompat {
|
||||
shellCmd = legacy.Replace(shellCmd)
|
||||
}
|
||||
|
||||
alert.commandShellTemplate = template.Must(
|
||||
template.New(alert.Name).Funcs(timeFormatFuncs).Parse(shellCmd),
|
||||
template.New(alert.Name).Parse(shellCmd),
|
||||
)
|
||||
default:
|
||||
return fmt.Errorf("No template provided for alert %s: %w", alert.Name, errNoTemplate)
|
||||
} else {
|
||||
return fmt.Errorf("No template provided for alert %s", alert.Name)
|
||||
}
|
||||
|
||||
return nil
|
||||
@ -119,40 +75,30 @@ func (alert *Alert) BuildTemplates() error {
|
||||
|
||||
// Send will send an alert notice by executing the command template
|
||||
func (alert Alert) Send(notice AlertNotice) (outputStr string, err error) {
|
||||
slog.Infof("Sending alert %s for %s", alert.Name, notice.MonitorName)
|
||||
|
||||
log.Printf("INFO: Sending alert %s for %s", alert.Name, notice.MonitorName)
|
||||
var cmd *exec.Cmd
|
||||
|
||||
switch {
|
||||
case alert.commandTemplate != nil:
|
||||
if alert.commandTemplate != nil {
|
||||
command := []string{}
|
||||
|
||||
for _, cmdTmp := range alert.commandTemplate {
|
||||
var commandBuffer bytes.Buffer
|
||||
|
||||
err = cmdTmp.Execute(&commandBuffer, notice)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
command = append(command, commandBuffer.String())
|
||||
}
|
||||
|
||||
cmd = exec.Command(command[0], command[1:]...)
|
||||
case alert.commandShellTemplate != nil:
|
||||
} else if alert.commandShellTemplate != nil {
|
||||
var commandBuffer bytes.Buffer
|
||||
|
||||
err = alert.commandShellTemplate.Execute(&commandBuffer, notice)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
shellCommand := commandBuffer.String()
|
||||
|
||||
cmd = ShellCommand(shellCommand)
|
||||
default:
|
||||
err = fmt.Errorf("No templates compiled for alert %s: %w", alert.Name, errNoTemplate)
|
||||
|
||||
} else {
|
||||
err = fmt.Errorf("No templates compiled for alert %v", alert.Name)
|
||||
return
|
||||
}
|
||||
|
||||
@ -164,15 +110,8 @@ func (alert Alert) Send(notice AlertNotice) (outputStr string, err error) {
|
||||
var output []byte
|
||||
output, err = cmd.CombinedOutput()
|
||||
outputStr = string(output)
|
||||
slog.Debugf("Alert output for: %s\n---\n%s\n---", alert.Name, outputStr)
|
||||
|
||||
if err != nil {
|
||||
err = fmt.Errorf(
|
||||
"Alert %s failed to send. Returned %w: %w",
|
||||
alert.Name,
|
||||
err,
|
||||
ErrAlertFailed,
|
||||
)
|
||||
if LogDebug {
|
||||
log.Printf("DEBUG: Alert output for: %s\n---\n%s\n---", alert.Name, outputStr)
|
||||
}
|
||||
|
||||
return outputStr, err
|
||||
|
@ -18,13 +18,11 @@ func TestAlertIsValid(t *testing.T) {
|
||||
|
||||
for _, c := range cases {
|
||||
log.Printf("Testing case %s", c.name)
|
||||
|
||||
actual := c.alert.IsValid()
|
||||
if actual != c.expected {
|
||||
t.Errorf("IsValid(%v), expected=%t actual=%t", c.name, c.expected, actual)
|
||||
log.Printf("Case failed: %s", c.name)
|
||||
}
|
||||
|
||||
log.Println("-----")
|
||||
}
|
||||
}
|
||||
@ -102,28 +100,19 @@ func TestAlertSend(t *testing.T) {
|
||||
log.Printf("Testing case %s", c.name)
|
||||
// Set PyCompat to value of compat flag
|
||||
PyCompat = c.pyCompat
|
||||
|
||||
err := c.alert.BuildTemplates()
|
||||
if err != nil {
|
||||
t.Errorf("Send(%v output), error building templates: %v", c.name, err)
|
||||
}
|
||||
|
||||
c.alert.BuildTemplates()
|
||||
output, err := c.alert.Send(c.notice)
|
||||
hasErr := (err != nil)
|
||||
|
||||
if output != c.expectedOutput {
|
||||
t.Errorf("Send(%v output), expected=%v actual=%v", c.name, c.expectedOutput, output)
|
||||
log.Printf("Case failed: %s", c.name)
|
||||
}
|
||||
|
||||
if hasErr != c.expectErr {
|
||||
t.Errorf("Send(%v err), expected=%v actual=%v", c.name, "Err", err)
|
||||
log.Printf("Case failed: %s", c.name)
|
||||
}
|
||||
|
||||
// Set PyCompat back to default value
|
||||
PyCompat = false
|
||||
|
||||
log.Println("-----")
|
||||
}
|
||||
}
|
||||
@ -131,12 +120,10 @@ func TestAlertSend(t *testing.T) {
|
||||
func TestAlertSendNoTemplates(t *testing.T) {
|
||||
alert := Alert{}
|
||||
notice := AlertNotice{}
|
||||
|
||||
output, err := alert.Send(notice)
|
||||
if err == nil {
|
||||
t.Errorf("Send(no template), expected=%v actual=%v", "Err", output)
|
||||
}
|
||||
|
||||
log.Println("-----")
|
||||
}
|
||||
|
||||
@ -155,12 +142,10 @@ func TestAlertBuildTemplate(t *testing.T) {
|
||||
log.Printf("Testing case %s", c.name)
|
||||
err := c.alert.BuildTemplates()
|
||||
hasErr := (err != nil)
|
||||
|
||||
if hasErr != c.expectErr {
|
||||
t.Errorf("IsValid(%v), expected=%t actual=%t", c.name, c.expectErr, err)
|
||||
log.Printf("Case failed: %s", c.name)
|
||||
}
|
||||
|
||||
log.Println("-----")
|
||||
}
|
||||
}
|
||||
|
108
config.go
108
config.go
@ -2,22 +2,15 @@ package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"time"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
|
||||
"git.iamthefij.com/iamthefij/slog"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
var errInvalidConfig = errors.New("Invalid configuration")
|
||||
|
||||
// Config type is contains all provided user configuration
|
||||
type Config struct {
|
||||
CheckInterval SecondsOrDuration `yaml:"check_interval"`
|
||||
DefaultAlertAfter int16 `yaml:"default_alert_after"`
|
||||
DefaultAlertEvery *int16 `yaml:"default_alert_every"`
|
||||
DefaultAlertDown []string `yaml:"default_alert_down"`
|
||||
DefaultAlertUp []string `yaml:"default_alert_up"`
|
||||
CheckInterval int64 `yaml:"check_interval"`
|
||||
Monitors []*Monitor
|
||||
Alerts map[string]*Alert
|
||||
}
|
||||
@ -42,48 +35,17 @@ func (cos *CommandOrShell) UnmarshalYAML(unmarshal func(interface{}) error) erro
|
||||
// Error indicates this is shell command
|
||||
if err != nil {
|
||||
var shellCmd string
|
||||
|
||||
err := unmarshal(&shellCmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cos.ShellCommand = shellCmd
|
||||
} else {
|
||||
cos.Command = cmd
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SecondsOrDuration wraps a duration value for parsing a duration or seconds from YAML
|
||||
// NOTE: This should be removed in favor of only parsing durations once compatibility is broken
|
||||
type SecondsOrDuration struct {
|
||||
value time.Duration
|
||||
}
|
||||
|
||||
// Value returns a duration value
|
||||
func (sod SecondsOrDuration) Value() time.Duration {
|
||||
return sod.value
|
||||
}
|
||||
|
||||
// UnmarshalYAML allows unmarshalling a duration value or seconds if an int was provided
|
||||
func (sod *SecondsOrDuration) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
var seconds int64
|
||||
err := unmarshal(&seconds)
|
||||
|
||||
if err == nil {
|
||||
sod.value = time.Second * time.Duration(seconds)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Error indicates that we don't have an int
|
||||
err = unmarshal(&sod.value)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// IsValid checks config validity and returns true if valid
|
||||
func (config Config) IsValid() (isValid bool) {
|
||||
isValid = true
|
||||
@ -91,75 +53,47 @@ func (config Config) IsValid() (isValid bool) {
|
||||
// Validate alerts
|
||||
if config.Alerts == nil || len(config.Alerts) == 0 {
|
||||
// This should never happen because there is a default alert named 'log' for now
|
||||
slog.Errorf("Invalid alert configuration: Must provide at least one alert")
|
||||
|
||||
log.Printf("ERROR: Invalid alert configuration: Must provide at least one alert")
|
||||
isValid = false
|
||||
}
|
||||
|
||||
for _, alert := range config.Alerts {
|
||||
if !alert.IsValid() {
|
||||
slog.Errorf("Invalid alert configuration: %+v", alert.Name)
|
||||
|
||||
log.Printf("ERROR: Invalid alert configuration: %s", alert.Name)
|
||||
isValid = false
|
||||
} else {
|
||||
slog.Debugf("Loaded alert %s", alert.Name)
|
||||
}
|
||||
}
|
||||
|
||||
// Validate monitors
|
||||
if config.Monitors == nil || len(config.Monitors) == 0 {
|
||||
slog.Errorf("Invalid monitor configuration: Must provide at least one monitor")
|
||||
|
||||
log.Printf("ERROR: Invalid monitor configuration: Must provide at least one monitor")
|
||||
isValid = false
|
||||
}
|
||||
|
||||
for _, monitor := range config.Monitors {
|
||||
if !monitor.IsValid() {
|
||||
slog.Errorf("Invalid monitor configuration: %s", monitor.Name)
|
||||
|
||||
log.Printf("ERROR: Invalid monitor configuration: %s", monitor.Name)
|
||||
isValid = false
|
||||
}
|
||||
// Check that all Monitor alerts actually exist
|
||||
for _, isUp := range []bool{true, false} {
|
||||
for _, alertName := range monitor.GetAlertNames(isUp) {
|
||||
if _, ok := config.Alerts[alertName]; !ok {
|
||||
slog.Errorf(
|
||||
"Invalid monitor configuration: %s. Unknown alert %s",
|
||||
log.Printf(
|
||||
"ERROR: Invalid monitor configuration: %s. Unknown alert %s",
|
||||
monitor.Name, alertName,
|
||||
)
|
||||
|
||||
isValid = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return isValid
|
||||
return
|
||||
}
|
||||
|
||||
// Init performs extra initialization on top of loading the config from file
|
||||
func (config *Config) Init() (err error) {
|
||||
for _, monitor := range config.Monitors {
|
||||
if monitor.AlertAfter == 0 && config.DefaultAlertAfter > 0 {
|
||||
monitor.AlertAfter = config.DefaultAlertAfter
|
||||
}
|
||||
|
||||
if monitor.AlertEvery == nil && config.DefaultAlertEvery != nil {
|
||||
monitor.AlertEvery = config.DefaultAlertEvery
|
||||
}
|
||||
|
||||
if len(monitor.AlertDown) == 0 && len(config.DefaultAlertDown) > 0 {
|
||||
monitor.AlertDown = config.DefaultAlertDown
|
||||
}
|
||||
|
||||
if len(monitor.AlertUp) == 0 && len(config.DefaultAlertUp) > 0 {
|
||||
monitor.AlertUp = config.DefaultAlertUp
|
||||
}
|
||||
}
|
||||
|
||||
for name, alert := range config.Alerts {
|
||||
alert.Name = name
|
||||
|
||||
if err = alert.BuildTemplates(); err != nil {
|
||||
return
|
||||
}
|
||||
@ -170,7 +104,7 @@ func (config *Config) Init() (err error) {
|
||||
|
||||
// LoadConfig will read config from the given path and parse it
|
||||
func LoadConfig(filePath string) (config Config, err error) {
|
||||
data, err := os.ReadFile(filePath)
|
||||
data, err := ioutil.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@ -180,30 +114,28 @@ func LoadConfig(filePath string) (config Config, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
slog.Debugf("Config values:\n%v\n", config)
|
||||
if LogDebug {
|
||||
log.Printf("DEBUG: Config values:\n%v\n", config)
|
||||
}
|
||||
|
||||
// Add log alert if not present
|
||||
if PyCompat {
|
||||
// Initialize alerts list if not present
|
||||
// Intialize alerts list if not present
|
||||
if config.Alerts == nil {
|
||||
config.Alerts = map[string]*Alert{}
|
||||
}
|
||||
|
||||
if _, ok := config.Alerts["log"]; !ok {
|
||||
config.Alerts["log"] = NewLogAlert()
|
||||
}
|
||||
}
|
||||
|
||||
// Finish initializing configuration
|
||||
if err = config.Init(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if !config.IsValid() {
|
||||
err = errInvalidConfig
|
||||
|
||||
err = errors.New("Invalid configuration")
|
||||
return
|
||||
}
|
||||
|
||||
return config, err
|
||||
// Finish initializing configuration
|
||||
err = config.Init()
|
||||
|
||||
return
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ package main
|
||||
import (
|
||||
"log"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestLoadConfig(t *testing.T) {
|
||||
@ -14,7 +13,6 @@ func TestLoadConfig(t *testing.T) {
|
||||
pyCompat bool
|
||||
}{
|
||||
{"./test/valid-config.yml", false, "Valid config file", false},
|
||||
{"./test/valid-config-default-values.yml", false, "Valid config file with default values", false},
|
||||
{"./test/valid-default-log-alert.yml", false, "Valid config file with default log alert PyCompat", true},
|
||||
{"./test/valid-default-log-alert.yml", true, "Invalid config file no log alert", false},
|
||||
{"./test/does-not-exist", true, "Invalid config path", false},
|
||||
@ -29,50 +27,20 @@ func TestLoadConfig(t *testing.T) {
|
||||
PyCompat = c.pyCompat
|
||||
_, err := LoadConfig(c.configPath)
|
||||
hasErr := (err != nil)
|
||||
|
||||
if hasErr != c.expectErr {
|
||||
t.Errorf("LoadConfig(%v), expected_error=%v actual=%v", c.name, c.expectErr, err)
|
||||
log.Printf("Case failed: %s", c.name)
|
||||
}
|
||||
|
||||
// Set PyCompat to default value
|
||||
PyCompat = false
|
||||
}
|
||||
}
|
||||
|
||||
func TestIntervalParsing(t *testing.T) {
|
||||
log.Printf("Testing case TestIntervalParsing")
|
||||
|
||||
config, err := LoadConfig("./test/valid-config.yml")
|
||||
if err != nil {
|
||||
t.Errorf("Failed loading config: %v", err)
|
||||
}
|
||||
|
||||
oneSecond := time.Second
|
||||
tenSeconds := 10 * time.Second
|
||||
oneMinute := time.Minute
|
||||
|
||||
// validate top level interval seconds represented as an int
|
||||
if config.CheckInterval.Value() != oneSecond {
|
||||
t.Errorf("Incorrectly parsed int seconds. expected=%v actual=%v", oneSecond, config.CheckInterval)
|
||||
}
|
||||
|
||||
if config.Monitors[0].CheckInterval.Value() != tenSeconds {
|
||||
t.Errorf("Incorrectly parsed seconds duration. expected=%v actual=%v", oneSecond, config.CheckInterval)
|
||||
}
|
||||
|
||||
if config.Monitors[1].CheckInterval.Value() != oneMinute {
|
||||
t.Errorf("Incorrectly parsed seconds duration. expected=%v actual=%v", oneSecond, config.CheckInterval)
|
||||
}
|
||||
|
||||
log.Println("-----")
|
||||
}
|
||||
}
|
||||
|
||||
// TestMultiLineConfig is a more complicated test stepping through the parsing
|
||||
// and execution of mutli-line strings presented in YAML
|
||||
func TestMultiLineConfig(t *testing.T) {
|
||||
log.Println("Testing multi-line string config")
|
||||
|
||||
config, err := LoadConfig("./test/valid-verify-multi-line.yml")
|
||||
if err != nil {
|
||||
t.Fatalf("TestMultiLineConfig(load), expected=no_error actual=%v", err)
|
||||
@ -80,10 +48,8 @@ func TestMultiLineConfig(t *testing.T) {
|
||||
|
||||
log.Println("-----")
|
||||
log.Println("TestMultiLineConfig(parse > string)")
|
||||
|
||||
expected := "echo 'Some string with stuff'; echo \"<angle brackets>\"; exit 1\n"
|
||||
actual := config.Monitors[0].Command.ShellCommand
|
||||
|
||||
if expected != actual {
|
||||
t.Errorf("TestMultiLineConfig(>) failed")
|
||||
t.Logf("string expected=`%v`", expected)
|
||||
@ -94,15 +60,12 @@ func TestMultiLineConfig(t *testing.T) {
|
||||
|
||||
log.Println("-----")
|
||||
log.Println("TestMultiLineConfig(execute > string)")
|
||||
|
||||
_, notice := config.Monitors[0].Check()
|
||||
if notice == nil {
|
||||
t.Fatalf("Did not receive an alert notice")
|
||||
}
|
||||
|
||||
expected = "Some string with stuff\n<angle brackets>\n"
|
||||
actual = notice.LastCheckOutput
|
||||
|
||||
if expected != actual {
|
||||
t.Errorf("TestMultiLineConfig(execute > string) check failed")
|
||||
t.Logf("string expected=`%v`", expected)
|
||||
@ -113,10 +76,8 @@ func TestMultiLineConfig(t *testing.T) {
|
||||
|
||||
log.Println("-----")
|
||||
log.Println("TestMultiLineConfig(parse | string)")
|
||||
|
||||
expected = "echo 'Some string with stuff'\necho '<angle brackets>'\n"
|
||||
actual = config.Alerts["log_shell"].Command.ShellCommand
|
||||
|
||||
if expected != actual {
|
||||
t.Errorf("TestMultiLineConfig(|) failed")
|
||||
t.Logf("string expected=`%v`", expected)
|
||||
@ -127,12 +88,10 @@ func TestMultiLineConfig(t *testing.T) {
|
||||
|
||||
log.Println("-----")
|
||||
log.Println("TestMultiLineConfig(execute | string)")
|
||||
|
||||
actual, err = config.Alerts["log_shell"].Send(AlertNotice{})
|
||||
if err != nil {
|
||||
t.Errorf("Execution of alert failed")
|
||||
}
|
||||
|
||||
expected = "Some string with stuff\n<angle brackets>\n"
|
||||
if expected != actual {
|
||||
t.Errorf("TestMultiLineConfig(execute | string) check failed")
|
||||
|
21
go.mod
21
go.mod
@ -1,23 +1,8 @@
|
||||
module git.iamthefij.com/iamthefij/minitor-go
|
||||
|
||||
go 1.20
|
||||
go 1.12
|
||||
|
||||
require (
|
||||
git.iamthefij.com/iamthefij/slog v1.3.0
|
||||
github.com/prometheus/client_golang v1.19.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||
github.com/prometheus/client_model v0.5.0 // indirect
|
||||
github.com/prometheus/common v0.48.0 // indirect
|
||||
github.com/prometheus/procfs v0.12.0 // indirect
|
||||
github.com/rogpeppe/go-internal v1.10.0 // indirect
|
||||
golang.org/x/sys v0.16.0 // indirect
|
||||
google.golang.org/protobuf v1.33.0 // indirect
|
||||
github.com/prometheus/client_golang v1.2.1
|
||||
gopkg.in/yaml.v2 v2.2.4
|
||||
)
|
||||
|
496
go.sum
496
go.sum
@ -1,546 +1,78 @@
|
||||
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/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
|
||||
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=
|
||||
git.iamthefij.com/iamthefij/slog v1.3.0 h1:4Hu5PQvDrW5e3FrTS3q2iIXW0iPvhNY/9qJsqDR3K3I=
|
||||
git.iamthefij.com/iamthefij/slog v1.3.0/go.mod h1:1RUj4hcCompZkAxXCRfUX786tb3cM/Zpkn97dGfUfbg=
|
||||
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/alecthomas/kingpin/v2 v2.3.1/go.mod h1:oYL5vtsvEHZGHxU7DMp32Dvx+qL+ptGn6lWaot2vCNE=
|
||||
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/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=
|
||||
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/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/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/cespare/xxhash/v2 v2.1.0 h1:yTUvW7Vhb89inJ+8irsUqiWjh8iT6sQPZiQzI6ReGkA=
|
||||
github.com/cespare/xxhash/v2 v2.1.0/go.mod h1:dgIUBU3pDso/gPgZ1osOZ0iQf77oPR28Tjxl5dIMyVM=
|
||||
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/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-kit/log v0.2.1/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 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
|
||||
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/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/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/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/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.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/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/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.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
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/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/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
||||
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/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/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.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.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y=
|
||||
github.com/prometheus/client_golang v1.15.0 h1:5fCgGYogn0hFdhyhLbw7hEsWxufKtY9klyvdNfFlFhM=
|
||||
github.com/prometheus/client_golang v1.15.0/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk=
|
||||
github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU=
|
||||
github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k=
|
||||
github.com/prometheus/client_golang v1.2.1 h1:JnMpQc6ppsNgw9QPAGF6Dod479itz7lvlsMzzNayLOI=
|
||||
github.com/prometheus/client_golang v1.2.1/go.mod h1:XMU6Z2MjaRKVu/dC1qupJI9SiNkDYzz3xecMgSW/F+U=
|
||||
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 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4=
|
||||
github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
|
||||
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
|
||||
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
||||
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
|
||||
github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
|
||||
github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
|
||||
github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM=
|
||||
github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc=
|
||||
github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
|
||||
github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
|
||||
github.com/prometheus/common v0.7.0 h1:L+1lyG48J1zAQXA3RBX/nG/B3gjlHq0zTt2tlbJLyCY=
|
||||
github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
|
||||
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.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/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
|
||||
github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI=
|
||||
github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY=
|
||||
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
|
||||
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||
github.com/prometheus/procfs v0.0.5 h1:3+auTFlqw+ZaQYJARz6ArODtkaIwtvBTx3N2NehQlL8=
|
||||
github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
|
||||
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/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/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.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/xhit/go-str2duration v1.2.0/go.mod h1:3cPSlfZlUHVlneIVfePFWcJZsuwf+P1v2SRTV4cUmp4=
|
||||
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=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
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-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
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/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
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-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-20210525063256-abc453219eb5/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/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
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/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I=
|
||||
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-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/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-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
|
||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
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/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.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/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
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-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/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
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/appengine v1.6.7/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.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
|
||||
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
|
||||
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
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 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.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=
|
||||
|
142
main.go
142
main.go
@ -1,16 +1,16 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"strings"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"git.iamthefij.com/iamthefij/slog"
|
||||
)
|
||||
|
||||
var (
|
||||
// LogDebug will control whether debug messsages should be logged
|
||||
LogDebug = false
|
||||
|
||||
// ExportMetrics will track whether or not we want to export metrics to prometheus
|
||||
ExportMetrics = false
|
||||
// MetricsPort is the port to expose metrics on
|
||||
@ -23,149 +23,101 @@ var (
|
||||
|
||||
// version of minitor being run
|
||||
version = "dev"
|
||||
|
||||
errUnknownAlert = errors.New("unknown alert")
|
||||
)
|
||||
|
||||
func sendAlerts(config *Config, monitor *Monitor, alertNotice *AlertNotice) error {
|
||||
slog.Debugf("Received an alert notice from %s", alertNotice.MonitorName)
|
||||
alertNames := monitor.GetAlertNames(alertNotice.IsUp)
|
||||
func checkMonitors(config *Config) error {
|
||||
for _, monitor := range config.Monitors {
|
||||
if monitor.ShouldCheck() {
|
||||
success, alertNotice := monitor.Check()
|
||||
|
||||
hasAlert := alertNotice != nil
|
||||
|
||||
// Track status metrics
|
||||
Metrics.SetMonitorStatus(monitor.Name, monitor.IsUp())
|
||||
Metrics.CountCheck(monitor.Name, success, hasAlert)
|
||||
|
||||
// Should probably consider refactoring everything below here
|
||||
if alertNotice != nil {
|
||||
if LogDebug {
|
||||
log.Printf("DEBUG: Recieved an alert notice from %s", alertNotice.MonitorName)
|
||||
}
|
||||
alertNames := monitor.GetAlertNames(alertNotice.IsUp)
|
||||
if alertNames == nil {
|
||||
// This should only happen for a recovery alert. AlertDown is validated not empty
|
||||
slog.Warningf(
|
||||
"Received alert, but no alert mechanisms exist. MonitorName=%s IsUp=%t",
|
||||
log.Printf(
|
||||
"WARNING: Recieved alert, but no alert mechanisms exist. MonitorName=%s IsUp=%t",
|
||||
alertNotice.MonitorName, alertNotice.IsUp,
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, alertName := range alertNames {
|
||||
if alert, ok := config.Alerts[alertName]; ok {
|
||||
output, err := alert.Send(*alertNotice)
|
||||
if err != nil {
|
||||
slog.Errorf(
|
||||
"Alert '%s' failed. result=%v: output=%s",
|
||||
log.Printf(
|
||||
"ERROR: Alert '%s' failed. result=%v: output=%s",
|
||||
alert.Name,
|
||||
err,
|
||||
output,
|
||||
)
|
||||
|
||||
return err
|
||||
return fmt.Errorf(
|
||||
"Unsuccessfully triggered alert '%s'. "+
|
||||
"Crashing to avoid false negatives: %v",
|
||||
alert.Name,
|
||||
err,
|
||||
)
|
||||
}
|
||||
|
||||
// Count alert metrics
|
||||
Metrics.CountAlert(monitor.Name, alert.Name)
|
||||
} else {
|
||||
// This case should never actually happen since we validate against it
|
||||
slog.Errorf("Unknown alert for monitor %s: %s", alertNotice.MonitorName, alertName)
|
||||
|
||||
return fmt.Errorf("unknown alert for monitor %s: %s: %w", alertNotice.MonitorName, alertName, errUnknownAlert)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkMonitors(config *Config) error {
|
||||
// TODO: Run this in goroutines and capture exceptions
|
||||
for _, monitor := range config.Monitors {
|
||||
if monitor.ShouldCheck() {
|
||||
success, alertNotice := monitor.Check()
|
||||
hasAlert := alertNotice != nil
|
||||
|
||||
// Track status metrics
|
||||
Metrics.SetMonitorStatus(monitor.Name, monitor.IsUp())
|
||||
Metrics.CountCheck(monitor.Name, success, monitor.LastCheckMilliseconds(), hasAlert)
|
||||
|
||||
if alertNotice != nil {
|
||||
err := sendAlerts(config, monitor, alertNotice)
|
||||
// If there was an error in sending an alert, exit early and bubble it up
|
||||
if err != nil {
|
||||
return err
|
||||
log.Printf("ERROR: Unknown alert for monitor %s: %s", alertNotice.MonitorName, alertName)
|
||||
return fmt.Errorf("Unknown alert for monitor %s: %s", alertNotice.MonitorName, alertName)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func sendStartupAlerts(config *Config, alertNames []string) error {
|
||||
for _, alertName := range alertNames {
|
||||
var err error
|
||||
|
||||
alert, ok := config.Alerts[alertName]
|
||||
if !ok {
|
||||
err = fmt.Errorf("unknown alert %s: %w", alertName, errUnknownAlert)
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
_, err = alert.Send(AlertNotice{
|
||||
AlertCount: 0,
|
||||
FailureCount: 0,
|
||||
IsUp: true,
|
||||
LastSuccess: time.Now(),
|
||||
MonitorName: fmt.Sprintf("First Run Alert Test: %s", alert.Name),
|
||||
LastCheckOutput: "",
|
||||
})
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
showVersion := flag.Bool("version", false, "Display the version of minitor and exit")
|
||||
configPath := flag.String("config", "config.yml", "Alternate configuration path (default: config.yml)")
|
||||
startupAlerts := flag.String("startup-alerts", "", "List of alerts to run on startup. This can help determine unhealthy alerts early on. (default \"\")")
|
||||
|
||||
flag.BoolVar(&slog.DebugLevel, "debug", false, "Enables debug logs (default: false)")
|
||||
// Get debug flag
|
||||
flag.BoolVar(&LogDebug, "debug", false, "Enables debug logs (default: false)")
|
||||
flag.BoolVar(&ExportMetrics, "metrics", false, "Enables prometheus metrics exporting (default: false)")
|
||||
flag.BoolVar(&PyCompat, "py-compat", false, "Enables support for legacy Python Minitor config. Will eventually be removed. (default: false)")
|
||||
flag.IntVar(&MetricsPort, "metrics-port", MetricsPort, "The port that Prometheus metrics should be exported on, if enabled. (default: 8080)")
|
||||
flag.IntVar(&MetricsPort, "metrics-port", 8080, "The port that Prometheus metrics should be exported on, if enabled. (default: 8080)")
|
||||
var showVersion = flag.Bool("version", false, "Display the version of minitor and exit")
|
||||
var configPath = flag.String("config", "config.yml", "Alternate configuration path (default: config.yml)")
|
||||
flag.Parse()
|
||||
|
||||
// Print version if flag is provided
|
||||
if *showVersion {
|
||||
fmt.Println("Minitor version:", version)
|
||||
|
||||
log.Println("Minitor version:", version)
|
||||
return
|
||||
}
|
||||
|
||||
if PyCompat {
|
||||
slog.Warningf("Python compatibility mode is enabled. This will be removed in the next major release. Please update your configuration.")
|
||||
}
|
||||
|
||||
// Load configuration
|
||||
config, err := LoadConfig(*configPath)
|
||||
slog.OnErrFatalf(err, "Error loading config: %v", err)
|
||||
if err != nil {
|
||||
log.Fatalf("Error loading config: %v", err)
|
||||
}
|
||||
|
||||
// Serve metrics exporter, if specified
|
||||
if ExportMetrics {
|
||||
slog.Infof("Exporting metrics to Prometheus on port %d", MetricsPort)
|
||||
|
||||
log.Println("INFO: Exporting metrics to Prometheus")
|
||||
go ServeMetrics()
|
||||
}
|
||||
|
||||
if *startupAlerts != "" {
|
||||
alertNames := strings.Split(*startupAlerts, ",")
|
||||
|
||||
err = sendStartupAlerts(&config, alertNames)
|
||||
|
||||
slog.OnErrPanicf(err, "Error running startup alerts")
|
||||
}
|
||||
|
||||
// Start main loop
|
||||
for {
|
||||
err = checkMonitors(&config)
|
||||
slog.OnErrPanicf(err, "Error checking monitors")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
time.Sleep(config.CheckInterval.Value())
|
||||
sleepTime := time.Duration(config.CheckInterval) * time.Second
|
||||
time.Sleep(sleepTime)
|
||||
}
|
||||
}
|
||||
|
127
main_test.go
127
main_test.go
@ -16,7 +16,7 @@ func TestCheckMonitors(t *testing.T) {
|
||||
{
|
||||
config: Config{
|
||||
Monitors: []*Monitor{
|
||||
{
|
||||
&Monitor{
|
||||
Name: "Success",
|
||||
Command: CommandOrShell{Command: []string{"true"}},
|
||||
},
|
||||
@ -28,24 +28,36 @@ func TestCheckMonitors(t *testing.T) {
|
||||
{
|
||||
config: Config{
|
||||
Monitors: []*Monitor{
|
||||
{
|
||||
&Monitor{
|
||||
Name: "Failure",
|
||||
Command: CommandOrShell{Command: []string{"false"}},
|
||||
AlertAfter: 1,
|
||||
},
|
||||
&Monitor{
|
||||
Name: "Failure",
|
||||
Command: CommandOrShell{Command: []string{"false"}},
|
||||
AlertDown: []string{"unknown"},
|
||||
AlertAfter: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: false,
|
||||
name: "Monitor failure, no alerts",
|
||||
name: "Monitor failure, no and unknown alerts",
|
||||
},
|
||||
{
|
||||
config: Config{
|
||||
Monitors: []*Monitor{
|
||||
{
|
||||
&Monitor{
|
||||
Name: "Success",
|
||||
Command: CommandOrShell{Command: []string{"ls"}},
|
||||
alertCount: 1,
|
||||
},
|
||||
&Monitor{
|
||||
Name: "Success",
|
||||
Command: CommandOrShell{Command: []string{"true"}},
|
||||
AlertUp: []string{"unknown"},
|
||||
alertCount: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: false,
|
||||
@ -54,35 +66,7 @@ func TestCheckMonitors(t *testing.T) {
|
||||
{
|
||||
config: Config{
|
||||
Monitors: []*Monitor{
|
||||
{
|
||||
Name: "Failure",
|
||||
Command: CommandOrShell{Command: []string{"false"}},
|
||||
AlertDown: []string{"unknown"},
|
||||
AlertAfter: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: true,
|
||||
name: "Monitor failure, unknown alerts",
|
||||
},
|
||||
{
|
||||
config: Config{
|
||||
Monitors: []*Monitor{
|
||||
{
|
||||
Name: "Success",
|
||||
Command: CommandOrShell{Command: []string{"true"}},
|
||||
AlertUp: []string{"unknown"},
|
||||
alertCount: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: true,
|
||||
name: "Monitor recovery, unknown alerts",
|
||||
},
|
||||
{
|
||||
config: Config{
|
||||
Monitors: []*Monitor{
|
||||
{
|
||||
&Monitor{
|
||||
Name: "Failure",
|
||||
Command: CommandOrShell{Command: []string{"false"}},
|
||||
AlertDown: []string{"good"},
|
||||
@ -90,7 +74,7 @@ func TestCheckMonitors(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Alerts: map[string]*Alert{
|
||||
"good": {
|
||||
"good": &Alert{
|
||||
Command: CommandOrShell{Command: []string{"true"}},
|
||||
},
|
||||
},
|
||||
@ -101,7 +85,7 @@ func TestCheckMonitors(t *testing.T) {
|
||||
{
|
||||
config: Config{
|
||||
Monitors: []*Monitor{
|
||||
{
|
||||
&Monitor{
|
||||
Name: "Failure",
|
||||
Command: CommandOrShell{Command: []string{"false"}},
|
||||
AlertDown: []string{"bad"},
|
||||
@ -109,7 +93,7 @@ func TestCheckMonitors(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Alerts: map[string]*Alert{
|
||||
"bad": {
|
||||
"bad": &Alert{
|
||||
Name: "bad",
|
||||
Command: CommandOrShell{Command: []string{"false"}},
|
||||
},
|
||||
@ -121,77 +105,10 @@ func TestCheckMonitors(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
err := c.config.Init()
|
||||
if err != nil {
|
||||
t.Errorf("checkMonitors(%s): unexpected error reading config: %v", c.name, err)
|
||||
}
|
||||
|
||||
err = checkMonitors(&c.config)
|
||||
c.config.Init()
|
||||
err := checkMonitors(&c.config)
|
||||
if err == nil && c.expectErr {
|
||||
t.Errorf("checkMonitors(%s): Expected panic, the code did not panic", c.name)
|
||||
} else if err != nil && !c.expectErr {
|
||||
t.Errorf("checkMonitors(%s): Did not expect an error, but we got one anyway: %v", c.name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFirstRunAlerts(t *testing.T) {
|
||||
cases := []struct {
|
||||
config Config
|
||||
expectErr bool
|
||||
startupAlerts []string
|
||||
name string
|
||||
}{
|
||||
{
|
||||
config: Config{},
|
||||
expectErr: false,
|
||||
startupAlerts: []string{},
|
||||
name: "Empty",
|
||||
},
|
||||
{
|
||||
config: Config{},
|
||||
expectErr: true,
|
||||
startupAlerts: []string{"missing"},
|
||||
name: "Unknown",
|
||||
},
|
||||
{
|
||||
config: Config{
|
||||
Alerts: map[string]*Alert{
|
||||
"good": {
|
||||
Command: CommandOrShell{Command: []string{"true"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: false,
|
||||
startupAlerts: []string{"good"},
|
||||
name: "Successful alert",
|
||||
},
|
||||
{
|
||||
config: Config{
|
||||
Alerts: map[string]*Alert{
|
||||
"bad": {
|
||||
Name: "bad",
|
||||
Command: CommandOrShell{Command: []string{"false"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: true,
|
||||
startupAlerts: []string{"bad"},
|
||||
name: "Failed alert",
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
err := c.config.Init()
|
||||
if err != nil {
|
||||
t.Errorf("sendFirstRunAlerts(%s): unexpected error reading config: %v", c.name, err)
|
||||
}
|
||||
|
||||
err = sendStartupAlerts(&c.config, c.startupAlerts)
|
||||
if err == nil && c.expectErr {
|
||||
t.Errorf("sendFirstRunAlerts(%s): Expected error, the code did not error", c.name)
|
||||
} else if err != nil && !c.expectErr {
|
||||
t.Errorf("sendFirstRunAlerts(%s): Did not expect an error, but we got one anyway: %v", c.name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
25
manifest.tmpl
Normal file
25
manifest.tmpl
Normal file
@ -0,0 +1,25 @@
|
||||
image: iamthefij/minitor-go:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}}
|
||||
{{#if build.tags}}
|
||||
tags:
|
||||
{{#each build.tags}}
|
||||
- {{this}}
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
manifests:
|
||||
-
|
||||
image: iamthefij/minitor-go:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64
|
||||
platform:
|
||||
architecture: amd64
|
||||
os: linux
|
||||
-
|
||||
image: iamthefij/minitor-go:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm64
|
||||
platform:
|
||||
architecture: arm64
|
||||
os: linux
|
||||
variant: v8
|
||||
-
|
||||
image: iamthefij/minitor-go:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm
|
||||
platform:
|
||||
architecture: arm
|
||||
os: linux
|
||||
variant: v7
|
18
metrics.go
18
metrics.go
@ -19,7 +19,6 @@ import (
|
||||
type MinitorMetrics struct {
|
||||
alertCount *prometheus.CounterVec
|
||||
checkCount *prometheus.CounterVec
|
||||
checkTime *prometheus.GaugeVec
|
||||
monitorStatus *prometheus.GaugeVec
|
||||
}
|
||||
|
||||
@ -41,13 +40,6 @@ func NewMetrics() *MinitorMetrics {
|
||||
},
|
||||
[]string{"monitor", "status", "is_alert"},
|
||||
),
|
||||
checkTime: prometheus.NewGaugeVec(
|
||||
prometheus.GaugeOpts{
|
||||
Name: "minitor_check_milliseconds",
|
||||
Help: "Time in miliseconds that a check ran for",
|
||||
},
|
||||
[]string{"monitor", "status"},
|
||||
),
|
||||
monitorStatus: prometheus.NewGaugeVec(
|
||||
prometheus.GaugeOpts{
|
||||
Name: "minitor_monitor_up_count",
|
||||
@ -60,7 +52,6 @@ func NewMetrics() *MinitorMetrics {
|
||||
// Register newly created metrics
|
||||
prometheus.MustRegister(metrics.alertCount)
|
||||
prometheus.MustRegister(metrics.checkCount)
|
||||
prometheus.MustRegister(metrics.checkTime)
|
||||
prometheus.MustRegister(metrics.monitorStatus)
|
||||
|
||||
return metrics
|
||||
@ -72,12 +63,11 @@ func (metrics *MinitorMetrics) SetMonitorStatus(monitor string, isUp bool) {
|
||||
if isUp {
|
||||
val = 1.0
|
||||
}
|
||||
|
||||
metrics.monitorStatus.With(prometheus.Labels{"monitor": monitor}).Set(val)
|
||||
}
|
||||
|
||||
// CountCheck counts the result of a particular Monitor check
|
||||
func (metrics *MinitorMetrics) CountCheck(monitor string, isSuccess bool, ms int64, isAlert bool) {
|
||||
func (metrics *MinitorMetrics) CountCheck(monitor string, isSuccess bool, isAlert bool) {
|
||||
status := "failure"
|
||||
if isSuccess {
|
||||
status = "success"
|
||||
@ -91,10 +81,6 @@ func (metrics *MinitorMetrics) CountCheck(monitor string, isSuccess bool, ms int
|
||||
metrics.checkCount.With(
|
||||
prometheus.Labels{"monitor": monitor, "status": status, "is_alert": alertVal},
|
||||
).Inc()
|
||||
|
||||
metrics.checkTime.With(
|
||||
prometheus.Labels{"monitor": monitor, "status": status},
|
||||
).Set(float64(ms))
|
||||
}
|
||||
|
||||
// CountAlert counts an alert
|
||||
@ -110,8 +96,6 @@ func (metrics *MinitorMetrics) CountAlert(monitor string, alert string) {
|
||||
// ServeMetrics starts an http server with a Prometheus metrics handler
|
||||
func ServeMetrics() {
|
||||
http.Handle("/metrics", promhttp.Handler())
|
||||
|
||||
host := fmt.Sprintf(":%d", MetricsPort)
|
||||
|
||||
_ = http.ListenAndServe(host, nil)
|
||||
}
|
||||
|
75
monitor.go
75
monitor.go
@ -1,31 +1,28 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"math"
|
||||
"os/exec"
|
||||
"time"
|
||||
|
||||
"git.iamthefij.com/iamthefij/slog"
|
||||
)
|
||||
|
||||
// Monitor represents a particular periodic check of a command
|
||||
type Monitor struct { //nolint:maligned
|
||||
type Monitor struct {
|
||||
// Config values
|
||||
AlertAfter int16 `yaml:"alert_after"`
|
||||
AlertEvery *int16 `yaml:"alert_every"`
|
||||
CheckInterval SecondsOrDuration `yaml:"check_interval"`
|
||||
Name string
|
||||
Command CommandOrShell
|
||||
AlertDown []string `yaml:"alert_down"`
|
||||
AlertUp []string `yaml:"alert_up"`
|
||||
Command CommandOrShell
|
||||
|
||||
CheckInterval float64 `yaml:"check_interval"`
|
||||
AlertAfter int16 `yaml:"alert_after"`
|
||||
AlertEvery int16 `yaml:"alert_every"`
|
||||
// Other values
|
||||
lastCheck time.Time
|
||||
lastOutput string
|
||||
alertCount int16
|
||||
failureCount int16
|
||||
lastCheck time.Time
|
||||
lastSuccess time.Time
|
||||
lastOutput string
|
||||
lastCheckDuration time.Duration
|
||||
}
|
||||
|
||||
// IsValid returns a boolean indicating if the Monitor has been correctly
|
||||
@ -43,9 +40,8 @@ func (monitor Monitor) ShouldCheck() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
sinceLastCheck := time.Since(monitor.lastCheck)
|
||||
|
||||
return sinceLastCheck >= monitor.CheckInterval.Value()
|
||||
sinceLastCheck := time.Now().Sub(monitor.lastCheck).Seconds()
|
||||
return sinceLastCheck >= monitor.CheckInterval
|
||||
}
|
||||
|
||||
// Check will run the command configured by the Monitor and return a status
|
||||
@ -58,14 +54,11 @@ func (monitor *Monitor) Check() (bool, *AlertNotice) {
|
||||
cmd = ShellCommand(monitor.Command.ShellCommand)
|
||||
}
|
||||
|
||||
checkStartTime := time.Now()
|
||||
output, err := cmd.CombinedOutput()
|
||||
monitor.lastCheck = time.Now()
|
||||
monitor.lastOutput = string(output)
|
||||
monitor.lastCheckDuration = monitor.lastCheck.Sub(checkStartTime)
|
||||
|
||||
var alertNotice *AlertNotice
|
||||
|
||||
isSuccess := (err == nil)
|
||||
if isSuccess {
|
||||
alertNotice = monitor.success()
|
||||
@ -73,11 +66,17 @@ func (monitor *Monitor) Check() (bool, *AlertNotice) {
|
||||
alertNotice = monitor.failure()
|
||||
}
|
||||
|
||||
slog.Debugf("Command output: %s", monitor.lastOutput)
|
||||
slog.OnErrWarnf(err, "Command result: %v", err)
|
||||
if LogDebug {
|
||||
log.Printf("DEBUG: Command output: %s", monitor.lastOutput)
|
||||
}
|
||||
if err != nil {
|
||||
if LogDebug {
|
||||
log.Printf("DEBUG: Command result: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
slog.Infof(
|
||||
"%s success=%t, alert=%t",
|
||||
log.Printf(
|
||||
"INFO: %s success=%t, alert=%t",
|
||||
monitor.Name,
|
||||
isSuccess,
|
||||
alertNotice != nil,
|
||||
@ -91,17 +90,11 @@ func (monitor Monitor) IsUp() bool {
|
||||
return monitor.alertCount == 0
|
||||
}
|
||||
|
||||
// LastCheckMilliseconds gives number of miliseconds the last check ran for
|
||||
func (monitor Monitor) LastCheckMilliseconds() int64 {
|
||||
return monitor.lastCheckDuration.Milliseconds()
|
||||
}
|
||||
|
||||
func (monitor *Monitor) success() (notice *AlertNotice) {
|
||||
if !monitor.IsUp() {
|
||||
// Alert that we have recovered
|
||||
notice = monitor.createAlertNotice(true)
|
||||
}
|
||||
|
||||
monitor.failureCount = 0
|
||||
monitor.alertCount = 0
|
||||
monitor.lastSuccess = time.Now()
|
||||
@ -113,14 +106,15 @@ func (monitor *Monitor) failure() (notice *AlertNotice) {
|
||||
monitor.failureCount++
|
||||
// If we haven't hit the minimum failures, we can exit
|
||||
if monitor.failureCount < monitor.getAlertAfter() {
|
||||
slog.Debugf(
|
||||
"%s failed but did not hit minimum failures. "+
|
||||
if LogDebug {
|
||||
log.Printf(
|
||||
"DEBUG: %s failed but did not hit minimum failures. "+
|
||||
"Count: %v alert after: %v",
|
||||
monitor.Name,
|
||||
monitor.failureCount,
|
||||
monitor.getAlertAfter(),
|
||||
)
|
||||
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@ -128,20 +122,19 @@ func (monitor *Monitor) failure() (notice *AlertNotice) {
|
||||
failureCount := (monitor.failureCount - monitor.getAlertAfter())
|
||||
|
||||
// Use alert cadence to determine if we should alert
|
||||
switch {
|
||||
case monitor.AlertEvery == nil, *monitor.AlertEvery == 0:
|
||||
if monitor.AlertEvery > 0 {
|
||||
// Handle integer number of failures before alerting
|
||||
if failureCount%monitor.AlertEvery == 0 {
|
||||
notice = monitor.createAlertNotice(false)
|
||||
}
|
||||
} else if monitor.AlertEvery == 0 {
|
||||
// Handle alerting on first failure only
|
||||
if failureCount == 0 {
|
||||
notice = monitor.createAlertNotice(false)
|
||||
}
|
||||
case *monitor.AlertEvery > 0:
|
||||
// Handle integer number of failures before alerting
|
||||
if failureCount%*monitor.AlertEvery == 0 {
|
||||
notice = monitor.createAlertNotice(false)
|
||||
}
|
||||
default:
|
||||
} else {
|
||||
// Handle negative numbers indicating an exponential backoff
|
||||
if failureCount >= int16(math.Pow(2, float64(monitor.alertCount))-1) { //nolint:gomnd
|
||||
if failureCount >= int16(math.Pow(2, float64(monitor.alertCount))-1) {
|
||||
notice = monitor.createAlertNotice(false)
|
||||
}
|
||||
}
|
||||
@ -151,7 +144,7 @@ func (monitor *Monitor) failure() (notice *AlertNotice) {
|
||||
monitor.alertCount++
|
||||
}
|
||||
|
||||
return notice
|
||||
return
|
||||
}
|
||||
|
||||
func (monitor Monitor) getAlertAfter() int16 {
|
||||
@ -160,7 +153,6 @@ func (monitor Monitor) getAlertAfter() int16 {
|
||||
if monitor.AlertAfter == 0 {
|
||||
return 1
|
||||
}
|
||||
|
||||
return monitor.AlertAfter
|
||||
}
|
||||
|
||||
@ -169,7 +161,6 @@ func (monitor Monitor) GetAlertNames(up bool) []string {
|
||||
if up {
|
||||
return monitor.AlertUp
|
||||
}
|
||||
|
||||
return monitor.AlertDown
|
||||
}
|
||||
|
||||
|
@ -22,13 +22,11 @@ func TestMonitorIsValid(t *testing.T) {
|
||||
|
||||
for _, c := range cases {
|
||||
log.Printf("Testing case %s", c.name)
|
||||
|
||||
actual := c.monitor.IsValid()
|
||||
if actual != c.expected {
|
||||
t.Errorf("IsValid(%v), expected=%t actual=%t", c.name, c.expected, actual)
|
||||
log.Printf("Case failed: %s", c.name)
|
||||
}
|
||||
|
||||
log.Println("-----")
|
||||
}
|
||||
}
|
||||
@ -45,9 +43,9 @@ func TestMonitorShouldCheck(t *testing.T) {
|
||||
name string
|
||||
}{
|
||||
{Monitor{}, true, "Empty"},
|
||||
{Monitor{lastCheck: timeNow, CheckInterval: SecondsOrDuration{time.Second * 15}}, false, "Just checked"},
|
||||
{Monitor{lastCheck: timeTenSecAgo, CheckInterval: SecondsOrDuration{time.Second * 15}}, false, "-10s"},
|
||||
{Monitor{lastCheck: timeTwentySecAgo, CheckInterval: SecondsOrDuration{time.Second * 15}}, true, "-20s"},
|
||||
{Monitor{lastCheck: timeNow, CheckInterval: 15}, false, "Just checked"},
|
||||
{Monitor{lastCheck: timeTenSecAgo, CheckInterval: 15}, false, "-10s"},
|
||||
{Monitor{lastCheck: timeTwentySecAgo, CheckInterval: 15}, true, "-20s"},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
@ -73,13 +71,11 @@ func TestMonitorIsUp(t *testing.T) {
|
||||
|
||||
for _, c := range cases {
|
||||
log.Printf("Testing case %s", c.name)
|
||||
|
||||
actual := c.monitor.IsUp()
|
||||
if actual != c.expected {
|
||||
t.Errorf("IsUp(%v), expected=%t actual=%t", c.name, c.expected, actual)
|
||||
log.Printf("Case failed: %s", c.name)
|
||||
}
|
||||
|
||||
log.Println("-----")
|
||||
}
|
||||
}
|
||||
@ -100,13 +96,11 @@ func TestMonitorGetAlertNames(t *testing.T) {
|
||||
|
||||
for _, c := range cases {
|
||||
log.Printf("Testing case %s", c.name)
|
||||
|
||||
actual := c.monitor.GetAlertNames(c.up)
|
||||
if !EqualSliceString(actual, c.expected) {
|
||||
t.Errorf("GetAlertNames(%v), expected=%v actual=%v", c.name, c.expected, actual)
|
||||
log.Printf("Case failed: %s", c.name)
|
||||
}
|
||||
|
||||
log.Println("-----")
|
||||
}
|
||||
}
|
||||
@ -125,15 +119,12 @@ func TestMonitorSuccess(t *testing.T) {
|
||||
|
||||
for _, c := range cases {
|
||||
log.Printf("Testing case %s", c.name)
|
||||
|
||||
notice := c.monitor.success()
|
||||
hasNotice := (notice != nil)
|
||||
|
||||
if hasNotice != c.expectNotice {
|
||||
t.Errorf("success(%v), expected=%t actual=%t", c.name, c.expectNotice, hasNotice)
|
||||
log.Printf("Case failed: %s", c.name)
|
||||
}
|
||||
|
||||
log.Println("-----")
|
||||
}
|
||||
}
|
||||
@ -141,32 +132,27 @@ func TestMonitorSuccess(t *testing.T) {
|
||||
// TestMonitorFailureAlertAfter tests that alerts will not trigger until
|
||||
// hitting the threshold provided by AlertAfter
|
||||
func TestMonitorFailureAlertAfter(t *testing.T) {
|
||||
var alertEvery int16 = 1
|
||||
|
||||
cases := []struct {
|
||||
monitor Monitor
|
||||
expectNotice bool
|
||||
name string
|
||||
}{
|
||||
{Monitor{AlertAfter: 1}, true, "Empty"}, // Defaults to true because and AlertEvery default to 0
|
||||
{Monitor{failureCount: 0, AlertAfter: 1, AlertEvery: &alertEvery}, true, "Alert after 1: first failure"},
|
||||
{Monitor{failureCount: 1, AlertAfter: 1, AlertEvery: &alertEvery}, true, "Alert after 1: second failure"},
|
||||
{Monitor{failureCount: 0, AlertAfter: 20, AlertEvery: &alertEvery}, false, "Alert after 20: first failure"},
|
||||
{Monitor{failureCount: 19, AlertAfter: 20, AlertEvery: &alertEvery}, true, "Alert after 20: 20th failure"},
|
||||
{Monitor{failureCount: 20, AlertAfter: 20, AlertEvery: &alertEvery}, true, "Alert after 20: 21st failure"},
|
||||
{Monitor{failureCount: 0, AlertAfter: 1, AlertEvery: 1}, true, "Alert after 1: first failure"},
|
||||
{Monitor{failureCount: 1, AlertAfter: 1, AlertEvery: 1}, true, "Alert after 1: second failure"},
|
||||
{Monitor{failureCount: 0, AlertAfter: 20, AlertEvery: 1}, false, "Alert after 20: first failure"},
|
||||
{Monitor{failureCount: 19, AlertAfter: 20, AlertEvery: 1}, true, "Alert after 20: 20th failure"},
|
||||
{Monitor{failureCount: 20, AlertAfter: 20, AlertEvery: 1}, true, "Alert after 20: 21st failure"},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
log.Printf("Testing case %s", c.name)
|
||||
|
||||
notice := c.monitor.failure()
|
||||
hasNotice := (notice != nil)
|
||||
|
||||
if hasNotice != c.expectNotice {
|
||||
t.Errorf("failure(%v), expected=%t actual=%t", c.name, c.expectNotice, hasNotice)
|
||||
log.Printf("Case failed: %s", c.name)
|
||||
}
|
||||
|
||||
log.Println("-----")
|
||||
}
|
||||
}
|
||||
@ -174,11 +160,6 @@ func TestMonitorFailureAlertAfter(t *testing.T) {
|
||||
// TestMonitorFailureAlertEvery tests that alerts will trigger
|
||||
// on the expected intervals
|
||||
func TestMonitorFailureAlertEvery(t *testing.T) {
|
||||
var alertEvery0, alertEvery1, alertEvery2 int16
|
||||
alertEvery0 = 0
|
||||
alertEvery1 = 1
|
||||
alertEvery2 = 2
|
||||
|
||||
cases := []struct {
|
||||
monitor Monitor
|
||||
expectNotice bool
|
||||
@ -193,20 +174,20 @@ func TestMonitorFailureAlertEvery(t *testing.T) {
|
||||
|
||||
For usabilty, this should be consistent. Consistent with what though? minitor-py? Or itself? Dun dun duuuunnnnn!
|
||||
*/
|
||||
{Monitor{AlertAfter: 1}, true, "Empty"}, // Defaults to true because AlertAfter and AlertEvery default to nil
|
||||
{Monitor{AlertAfter: 1}, true, "Empty"}, // Defaults to true because AlertAfter and AlertEvery default to 0
|
||||
// Alert first time only, after 1
|
||||
{Monitor{failureCount: 0, AlertAfter: 1, AlertEvery: &alertEvery0}, true, "Alert first time only after 1: first failure"},
|
||||
{Monitor{failureCount: 1, AlertAfter: 1, AlertEvery: &alertEvery0}, false, "Alert first time only after 1: second failure"},
|
||||
{Monitor{failureCount: 2, AlertAfter: 1, AlertEvery: &alertEvery0}, false, "Alert first time only after 1: third failure"},
|
||||
{Monitor{failureCount: 0, AlertAfter: 1, AlertEvery: 0}, true, "Alert first time only after 1: first failure"},
|
||||
{Monitor{failureCount: 1, AlertAfter: 1, AlertEvery: 0}, false, "Alert first time only after 1: second failure"},
|
||||
{Monitor{failureCount: 2, AlertAfter: 1, AlertEvery: 0}, false, "Alert first time only after 1: third failure"},
|
||||
// Alert every time, after 1
|
||||
{Monitor{failureCount: 0, AlertAfter: 1, AlertEvery: &alertEvery1}, true, "Alert every time after 1: first failure"},
|
||||
{Monitor{failureCount: 1, AlertAfter: 1, AlertEvery: &alertEvery1}, true, "Alert every time after 1: second failure"},
|
||||
{Monitor{failureCount: 2, AlertAfter: 1, AlertEvery: &alertEvery1}, true, "Alert every time after 1: third failure"},
|
||||
{Monitor{failureCount: 0, AlertAfter: 1, AlertEvery: 1}, true, "Alert every time after 1: first failure"},
|
||||
{Monitor{failureCount: 1, AlertAfter: 1, AlertEvery: 1}, true, "Alert every time after 1: second failure"},
|
||||
{Monitor{failureCount: 1, AlertAfter: 1, AlertEvery: 1}, true, "Alert every time after 1: third failure"},
|
||||
// Alert every other time, after 1
|
||||
{Monitor{failureCount: 0, AlertAfter: 1, AlertEvery: &alertEvery2}, true, "Alert every other time after 1: first failure"},
|
||||
{Monitor{failureCount: 1, AlertAfter: 1, AlertEvery: &alertEvery2}, false, "Alert every other time after 1: second failure"},
|
||||
{Monitor{failureCount: 2, AlertAfter: 1, AlertEvery: &alertEvery2}, true, "Alert every other time after 1: third failure"},
|
||||
{Monitor{failureCount: 3, AlertAfter: 1, AlertEvery: &alertEvery2}, false, "Alert every other time after 1: fourth failure"},
|
||||
{Monitor{failureCount: 0, AlertAfter: 1, AlertEvery: 2}, true, "Alert every other time after 1: first failure"},
|
||||
{Monitor{failureCount: 1, AlertAfter: 1, AlertEvery: 2}, false, "Alert every other time after 1: second failure"},
|
||||
{Monitor{failureCount: 2, AlertAfter: 1, AlertEvery: 2}, true, "Alert every other time after 1: third failure"},
|
||||
{Monitor{failureCount: 3, AlertAfter: 1, AlertEvery: 2}, false, "Alert every other time after 1: fourth failure"},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
@ -214,12 +195,10 @@ func TestMonitorFailureAlertEvery(t *testing.T) {
|
||||
|
||||
notice := c.monitor.failure()
|
||||
hasNotice := (notice != nil)
|
||||
|
||||
if hasNotice != c.expectNotice {
|
||||
t.Errorf("failure(%v), expected=%t actual=%t", c.name, c.expectNotice, hasNotice)
|
||||
log.Printf("Case failed: %s", c.name)
|
||||
}
|
||||
|
||||
log.Println("-----")
|
||||
}
|
||||
}
|
||||
@ -227,8 +206,6 @@ func TestMonitorFailureAlertEvery(t *testing.T) {
|
||||
// TestMonitorFailureExponential tests that alerts will trigger
|
||||
// with an exponential backoff after repeated failures
|
||||
func TestMonitorFailureExponential(t *testing.T) {
|
||||
var alertEveryExp int16 = -1
|
||||
|
||||
cases := []struct {
|
||||
expectNotice bool
|
||||
name string
|
||||
@ -245,19 +222,16 @@ func TestMonitorFailureExponential(t *testing.T) {
|
||||
|
||||
// Unlike previous tests, this one requires a static Monitor with repeated
|
||||
// calls to the failure method
|
||||
monitor := Monitor{failureCount: 0, AlertAfter: 1, AlertEvery: &alertEveryExp}
|
||||
|
||||
monitor := Monitor{failureCount: 0, AlertAfter: 1, AlertEvery: -1}
|
||||
for _, c := range cases {
|
||||
log.Printf("Testing case %s", c.name)
|
||||
|
||||
notice := monitor.failure()
|
||||
hasNotice := (notice != nil)
|
||||
|
||||
if hasNotice != c.expectNotice {
|
||||
t.Errorf("failure(%v), expected=%t actual=%t", c.name, c.expectNotice, hasNotice)
|
||||
log.Printf("Case failed: %s", c.name)
|
||||
}
|
||||
|
||||
log.Println("-----")
|
||||
}
|
||||
}
|
||||
@ -269,7 +243,6 @@ func TestMonitorCheck(t *testing.T) {
|
||||
hasNotice bool
|
||||
lastOutput string
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
monitor Monitor
|
||||
expect expected
|
||||
@ -317,7 +290,6 @@ func TestMonitorCheck(t *testing.T) {
|
||||
t.Errorf("Check(%v) (output), expected=%v actual=%v", c.name, c.expect.lastOutput, lastOutput)
|
||||
log.Printf("Case failed: %s", c.name)
|
||||
}
|
||||
|
||||
log.Println("-----")
|
||||
}
|
||||
}
|
||||
|
@ -3,14 +3,14 @@ check_interval: 5
|
||||
|
||||
monitors:
|
||||
- name: Fake Website
|
||||
command: ["curl", "-s", "-o", "/dev/null", "https://minitor.mon"]
|
||||
command: ['curl', '-s', '-o', '/dev/null', 'https://minitor.mon']
|
||||
alert_down: [log_down, mailgun_down, sms_down]
|
||||
alert_up: [log_up, email_up]
|
||||
check_interval: 10 # Must be at minimum the global `check_interval`
|
||||
alert_after: 3
|
||||
alert_every: -1 # Defaults to -1 for exponential backoff. 0 to disable repeating
|
||||
- name: Real Website
|
||||
command: ["curl", "-s", "-o", "/dev/null", "https://google.com"]
|
||||
command: ['curl', '-s', '-o', '/dev/null', 'https://google.com']
|
||||
alert_down: [log_down, mailgun_down, sms_down]
|
||||
alert_up: [log_up, email_up]
|
||||
check_interval: 5
|
||||
@ -23,8 +23,7 @@ alerts:
|
||||
log_up:
|
||||
command: ["echo", "Minitor recovery for {{.MonitorName}}"]
|
||||
email_up:
|
||||
command:
|
||||
[sendmail, "me@minitor.mon", "Recovered: {monitor_name}", "We're back!"]
|
||||
command: [sendmail, "me@minitor.mon", "Recovered: {monitor_name}", "We're back!"]
|
||||
mailgun_down:
|
||||
command: >
|
||||
curl -s -X POST
|
||||
|
@ -1,6 +0,0 @@
|
||||
#! /bin/sh
|
||||
|
||||
# Used for a basic HTTP health check
|
||||
# Avoids output from non-errors and will fail if the HTTP response is unsuccessful
|
||||
|
||||
curl --silent --show-error --fail -o /dev/null "$@"
|
@ -1,12 +0,0 @@
|
||||
---
|
||||
check_interval: 1
|
||||
default_alert_down: ["log_command"]
|
||||
default_alert_after: 1
|
||||
|
||||
monitors:
|
||||
- name: Command
|
||||
command: ["echo", "$PATH"]
|
||||
|
||||
alerts:
|
||||
log_command:
|
||||
command: ["echo", "regular", '"command!!!"', "{{.MonitorName}}"]
|
@ -3,23 +3,21 @@ check_interval: 1
|
||||
|
||||
monitors:
|
||||
- name: Command
|
||||
command: ["echo", "$PATH"]
|
||||
alert_down: ["log_command", "log_shell"]
|
||||
command: ['echo', '$PATH']
|
||||
alert_down: ['log_command', 'log_shell']
|
||||
alert_every: 0
|
||||
check_interval: 10s
|
||||
- name: Shell
|
||||
command: >
|
||||
echo 'Some string with stuff';
|
||||
echo 'another line';
|
||||
echo $PATH;
|
||||
exit 1
|
||||
alert_down: ["log_command", "log_shell"]
|
||||
alert_down: ['log_command', 'log_shell']
|
||||
alert_after: 5
|
||||
alert_every: 0
|
||||
check_interval: 1m
|
||||
|
||||
alerts:
|
||||
log_command:
|
||||
command: ["echo", "regular", '"command!!!"', "{{.MonitorName}}"]
|
||||
command: ['echo', 'regular', '"command!!!"', "{{.MonitorName}}"]
|
||||
log_shell:
|
||||
command: echo "Failure on {{.MonitorName}} User is $USER"
|
||||
|
4
util.go
4
util.go
@ -8,7 +8,7 @@ import (
|
||||
// ShellCommand takes a string and executes it as a command using `sh`
|
||||
func ShellCommand(command string) *exec.Cmd {
|
||||
shellCommand := []string{"sh", "-c", strings.TrimSpace(command)}
|
||||
|
||||
//log.Printf("Shell command: %v", shellCommand)
|
||||
return exec.Command(shellCommand[0], shellCommand[1:]...)
|
||||
}
|
||||
|
||||
@ -17,12 +17,10 @@ func EqualSliceString(a, b []string) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i, val := range a {
|
||||
if val != b[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user