Compare commits

..

No commits in common. "master" and "v0.8.0" have entirely different histories.

44 changed files with 1996 additions and 622 deletions

View File

@ -1,13 +0,0 @@
version: 2
updates:
- package-ecosystem: "gomod"
directory: "/"
schedule:
interval: "daily"
ignore:
- dependency-name: k8s.io/*
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"

View File

@ -1,28 +0,0 @@
# https://docs.github.com/en/code-security/dependabot/working-with-dependabot/automating-dependabot-with-github-actions
name: Approve and enable auto-merge for dependabot
on: pull_request
permissions:
pull-requests: write
contents: write
jobs:
review:
runs-on: ubuntu-latest
if: ${{ github.actor == 'dependabot[bot]' }}
steps:
- name: Dependabot metadata
id: metadata
uses: dependabot/fetch-metadata@v2.4.0
with:
github-token: "${{ secrets.GITHUB_TOKEN }}"
- name: Approve PR
run: gh pr review --approve "$PR_URL"
env:
PR_URL: ${{github.event.pull_request.html_url}}
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
- name: Enable auto-merge for Dependabot PRs
run: gh pr merge --auto --squash "$PR_URL"
env:
PR_URL: ${{github.event.pull_request.html_url}}
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}

View File

@ -1,28 +0,0 @@
name: Go
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: 1.23
- name: Build
run: |
GOOS=linux go build -v ./...
GOOS=windows go build -v ./...
GOOS=darwin go build -v ./...
- name: Test
run: go test -v ./...

View File

@ -1,41 +0,0 @@
name: golangci-lint
on:
push:
tags:
- v*
branches:
- master
- main
pull_request:
jobs:
golangci:
name: lint
runs-on: ubuntu-latest
steps:
- uses: actions/setup-go@v5
with:
go-version: 1.23
- uses: actions/checkout@v4
- name: golangci-lint
uses: golangci/golangci-lint-action@v7
with:
# Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version
version: v2.0.2
# Optional: working directory, useful for monorepos
# working-directory: somedir
# Optional: golangci-lint command line arguments.
# args: --issues-exit-code=0
# Optional: show only new issues if it's a pull request. The default value is `false`.
# only-new-issues: true
# Optional: if set to true then the action will use pre-installed Go.
# skip-go-installation: true
# Optional: if set to true then the action don't cache or restore ~/go/pkg.
# skip-pkg-cache: true
# Optional: if set to true then the action don't cache or restore ~/.cache/go-build.
# skip-build-cache: true

View File

@ -1,32 +0,0 @@
version: "2"
run:
concurrency: 4
linters:
enable:
- testifylint
settings:
testifylint:
enable-all: true
disable:
- float-compare
exclusions:
presets:
- comments
- common-false-positives
- legacy
- std-error-handling
paths:
- vendor
- third_party$
- builtin$
- examples$
formatters:
settings:
goimports:
local-prefixes:
- github.com/argoproj/pkg/v2
exclusions:
paths:
- third_party$
- builtin$
- examples$

View File

@ -24,9 +24,11 @@ darwin-build:
GOOS=darwin go build ./...
.PHONY: lint
lint:
lint: build
go mod tidy
GOGC=$(LINT_GOGC) golangci-lint run --fix --verbose --concurrency $(LINT_CONCURRENCY) --timeout $(LINT_DEADLINE)
# golangci-lint does not do a good job of formatting imports
goimports -local github.com/argoproj/pkg -w `find . ! -path './vendor/*' -type f -name '*.go'`
GOGC=$(LINT_GOGC) golangci-lint run --fix --verbose --concurrency $(LINT_CONCURRENCY) --deadline $(LINT_DEADLINE)
.PHONY: test
test: lint

View File

@ -1,6 +0,0 @@
# argoproj/pkg
This repository contains shared libraries and utilities for the Argo project. The packages are primarily maintained for
use by the Argo projects, but they are open for use by anyone.
If your project uses `argoproj/pkg`, please add it to the [USERS.md](USERS.md) file.

View File

@ -1,9 +0,0 @@
## Who uses argoproj/pkg?
These are the projects that use `argoproj/pkg`. If you use one or more of these packages, add your project here!
| Project | grpc/http | kubeclientmetrics | stats | sync | time |
|--------------------------------------------------------------|--------------------|--------------------|--------------------|--------------------|--------------------|
| [Argo CD](https://github.com/argoproj/argo-cd) | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| [Argo Rollouts](https://github.com/argoproj/argo-rollouts) | :white_check_mark: | :white_check_mark: | | | |
| [Argo Workflows](https://github.com/argoproj/argo-workflows) | :white_check_mark: | | :white_check_mark: | :white_check_mark: | :white_check_mark: |

25
cli/cli.go Normal file
View File

@ -0,0 +1,25 @@
package cli
import (
"flag"
"strconv"
log "github.com/sirupsen/logrus"
"k8s.io/klog/v2"
"github.com/argoproj/pkg/errors"
)
// SetLogLevel parses and sets a logrus log level
func SetLogLevel(logLevel string) {
level, err := log.ParseLevel(logLevel)
errors.CheckError(err)
log.SetLevel(level)
}
// SetGLogLevel set the glog level for the k8s go-client
func SetGLogLevel(glogLevel int) {
klog.InitFlags(nil)
_ = flag.Set("logtostderr", "true")
_ = flag.Set("v", strconv.Itoa(glogLevel))
}

12
errors/errors.go Normal file
View File

@ -0,0 +1,12 @@
package errors
import (
log "github.com/sirupsen/logrus"
)
// CheckError is a convenience function to fatally log an exit if the supplied error is non-nil
func CheckError(err error) {
if err != nil {
log.Fatal(err)
}
}

175
exec/exec.go Normal file
View File

@ -0,0 +1,175 @@
package exec
import (
"bytes"
"fmt"
"os"
"os/exec"
"runtime"
"strings"
"time"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
"github.com/argoproj/pkg/rand"
)
var ErrWaitPIDTimeout = fmt.Errorf("Timed out waiting for PID to complete")
var Unredacted = Redact(nil)
type CmdError struct {
Args string
Stderr string
Cause error
}
func (ce *CmdError) Error() string {
res := fmt.Sprintf("`%v` failed %v", ce.Args, ce.Cause)
if ce.Stderr != "" {
res = fmt.Sprintf("%s: %s", res, ce.Stderr)
}
return res
}
func (ce *CmdError) String() string {
return ce.Error()
}
func newCmdError(args string, cause error, stderr string) *CmdError {
return &CmdError{Args: args, Stderr: stderr, Cause: cause}
}
type CmdOpts struct {
Timeout time.Duration
Redactor func(text string) string
}
var DefaultCmdOpts = CmdOpts{
Timeout: time.Duration(0),
Redactor: Unredacted,
}
func Redact(items []string) func(text string) string {
return func(text string) string {
for _, item := range items {
text = strings.Replace(text, item, "******", -1)
}
return text
}
}
// RunCommandExt is a convenience function to run/log a command and return/log stderr in an error upon
// failure.
func RunCommandExt(cmd *exec.Cmd, opts CmdOpts) (string, error) {
logCtx := log.WithFields(log.Fields{"execID": rand.RandString(5)})
redactor := DefaultCmdOpts.Redactor
if opts.Redactor != nil {
redactor = opts.Redactor
}
// log in a way we can copy-and-paste into a terminal
args := strings.Join(cmd.Args, " ")
logCtx.WithFields(log.Fields{"dir": cmd.Dir}).Info(redactor(args))
var stdout bytes.Buffer
var stderr bytes.Buffer
cmd.Stdout = &stdout
cmd.Stderr = &stderr
start := time.Now()
err := cmd.Start()
if err != nil {
return "", err
}
done := make(chan error)
go func() { done <- cmd.Wait() }()
// Start a timer
timeout := DefaultCmdOpts.Timeout
if opts.Timeout != time.Duration(0) {
timeout = opts.Timeout
}
var timoutCh <-chan time.Time
if timeout != 0 {
timoutCh = time.NewTimer(timeout).C
}
select {
//noinspection ALL
case <-timoutCh:
_ = cmd.Process.Kill()
output := stdout.String()
logCtx.WithFields(log.Fields{"duration": time.Since(start)}).Debug(redactor(output))
err = newCmdError(redactor(args), fmt.Errorf("timeout after %v", timeout), "")
logCtx.Error(err.Error())
return strings.TrimSuffix(output, "\n"), err
case err := <-done:
if err != nil {
output := stdout.String()
logCtx.WithFields(log.Fields{"duration": time.Since(start)}).Debug(redactor(output))
err := newCmdError(redactor(args), errors.New(redactor(err.Error())), strings.TrimSpace(redactor(stderr.String())))
logCtx.Error(err.Error())
return strings.TrimSuffix(output, "\n"), err
}
}
output := stdout.String()
logCtx.WithFields(log.Fields{"duration": time.Since(start)}).Debug(redactor(output))
return strings.TrimSuffix(output, "\n"), nil
}
func RunCommand(name string, opts CmdOpts, arg ...string) (string, error) {
return RunCommandExt(exec.Command(name, arg...), opts)
}
// WaitPIDOpts are options to WaitPID
type WaitPIDOpts struct {
PollInterval time.Duration
Timeout time.Duration
}
// WaitPID waits for a non-child process id to exit
func WaitPID(pid int, opts ...WaitPIDOpts) error {
if runtime.GOOS != "linux" {
return errors.Errorf("Platform '%s' unsupported", runtime.GOOS)
}
var timeout time.Duration
var pollInterval = time.Second
if len(opts) > 0 {
if opts[0].PollInterval != 0 {
pollInterval = opts[0].PollInterval
}
if opts[0].Timeout != 0 {
timeout = opts[0].Timeout
}
}
path := fmt.Sprintf("/proc/%d", pid)
ticker := time.NewTicker(pollInterval)
defer ticker.Stop()
var timoutCh <-chan time.Time
if timeout != 0 {
timoutCh = time.NewTimer(timeout).C
}
for {
select {
case <-ticker.C:
_, err := os.Stat(path)
if err != nil {
if os.IsNotExist(err) {
return nil
}
return errors.WithStack(err)
}
case <-timoutCh:
return ErrWaitPIDTimeout
}
}
}

128
exec/exec_test.go Normal file
View File

@ -0,0 +1,128 @@
package exec
import (
"os/exec"
"testing"
"time"
log "github.com/sirupsen/logrus"
"github.com/sirupsen/logrus/hooks/test"
"github.com/stretchr/testify/assert"
)
func TestRunCommand(t *testing.T) {
hook := test.NewGlobal()
log.SetLevel(log.DebugLevel)
defer log.SetLevel(log.InfoLevel)
message, err := RunCommand("echo", CmdOpts{Redactor: Redact([]string{"world"})}, "hello world")
assert.NoError(t, err)
assert.Equal(t, "hello world", message)
assert.Len(t, hook.Entries, 2)
entry := hook.Entries[0]
assert.Equal(t, log.InfoLevel, entry.Level)
assert.Equal(t, "echo hello ******", entry.Message)
assert.Contains(t, entry.Data, "dir")
assert.Contains(t, entry.Data, "execID")
entry = hook.Entries[1]
assert.Equal(t, log.DebugLevel, entry.Level)
assert.Equal(t, "hello ******\n", entry.Message)
assert.Contains(t, entry.Data, "duration")
assert.Contains(t, entry.Data, "execID")
}
func TestRunCommandTimeout(t *testing.T) {
hook := test.NewGlobal()
log.SetLevel(log.DebugLevel)
defer log.SetLevel(log.InfoLevel)
output, err := RunCommand("sh", CmdOpts{Timeout: 500 * time.Millisecond}, "-c", "echo hello world && echo my-error >&2 && sleep 2")
assert.Equal(t, output, "hello world")
assert.EqualError(t, err, "`sh -c echo hello world && echo my-error >&2 && sleep 2` failed timeout after 500ms")
assert.Len(t, hook.Entries, 3)
entry := hook.Entries[0]
assert.Equal(t, log.InfoLevel, entry.Level)
assert.Equal(t, "sh -c echo hello world && echo my-error >&2 && sleep 2", entry.Message)
assert.Contains(t, entry.Data, "dir")
assert.Contains(t, entry.Data, "execID")
entry = hook.Entries[1]
assert.Equal(t, log.DebugLevel, entry.Level)
assert.Equal(t, "hello world\n", entry.Message)
assert.Contains(t, entry.Data, "duration")
assert.Contains(t, entry.Data, "execID")
entry = hook.Entries[2]
assert.Equal(t, log.ErrorLevel, entry.Level)
assert.Equal(t, "`sh -c echo hello world && echo my-error >&2 && sleep 2` failed timeout after 500ms", entry.Message)
assert.Contains(t, entry.Data, "execID")
}
func TestTrimmedOutput(t *testing.T) {
message, err := RunCommand("printf", CmdOpts{}, "hello world")
assert.NoError(t, err)
assert.Equal(t, "hello world", message)
}
func TestRunCommandExitErr(t *testing.T) {
hook := test.NewGlobal()
log.SetLevel(log.DebugLevel)
defer log.SetLevel(log.InfoLevel)
output, err := RunCommand("sh", CmdOpts{Redactor: Redact([]string{"world"})}, "-c", "echo hello world && echo my-error >&2 && exit 1")
assert.Equal(t, "hello world", output)
assert.EqualError(t, err, "`sh -c echo hello ****** && echo my-error >&2 && exit 1` failed exit status 1: my-error")
assert.Len(t, hook.Entries, 3)
entry := hook.Entries[0]
assert.Equal(t, log.InfoLevel, entry.Level)
assert.Equal(t, "sh -c echo hello ****** && echo my-error >&2 && exit 1", entry.Message)
assert.Contains(t, entry.Data, "dir")
assert.Contains(t, entry.Data, "execID")
entry = hook.Entries[1]
assert.Equal(t, log.DebugLevel, entry.Level)
assert.Equal(t, "hello ******\n", entry.Message)
assert.Contains(t, entry.Data, "duration")
assert.Contains(t, entry.Data, "execID")
entry = hook.Entries[2]
assert.Equal(t, log.ErrorLevel, entry.Level)
assert.Equal(t, "`sh -c echo hello ****** && echo my-error >&2 && exit 1` failed exit status 1: my-error", entry.Message)
assert.Contains(t, entry.Data, "execID")
}
func TestRunCommandErr(t *testing.T) {
//_ := test.NewGlobal()
log.SetLevel(log.DebugLevel)
defer log.SetLevel(log.InfoLevel)
// this would create a panic
output, err := RunCommand("", CmdOpts{Redactor: Redact([]string{"world"})})
assert.Empty(t, output)
assert.EqualError(t, err, "fork/exec : no such file or directory")
}
func TestRunInDir(t *testing.T) {
cmd := exec.Command("pwd")
cmd.Dir = "/"
message, err := RunCommandExt(cmd, CmdOpts{})
assert.Nil(t, err)
assert.Equal(t, "/", message)
}
func TestRedact(t *testing.T) {
assert.Equal(t, "", Redact(nil)(""))
assert.Equal(t, "", Redact([]string{})(""))
assert.Equal(t, "", Redact([]string{"foo"})(""))
assert.Equal(t, "foo", Redact([]string{})("foo"))
assert.Equal(t, "******", Redact([]string{"foo"})("foo"))
assert.Equal(t, "****** ******", Redact([]string{"foo", "bar"})("foo bar"))
assert.Equal(t, "****** ******", Redact([]string{"foo"})("foo foo"))
}

108
expr/function.go Normal file
View File

@ -0,0 +1,108 @@
package expr
import (
"encoding/json"
"fmt"
"reflect"
"strconv"
"github.com/oliveagle/jsonpath"
)
func GetExprEnvFunctionMap() map[string]interface{} {
return map[string]interface{}{
"asInt": AsInt,
"asFloat": AsFloat,
"string": AsStr,
"jsonpath": JsonPath,
}
}
func AsStr(val interface{}) interface{} {
return fmt.Sprintf("%v", val)
}
func JsonPath(jsonStr string, path string) interface{} {
var jsonMap interface{}
err := json.Unmarshal([]byte(jsonStr), &jsonMap)
if err != nil {
panic(err)
}
value, err := jsonpath.JsonPathLookup(jsonMap, path)
if err != nil {
panic(err)
}
return value
}
func AsInt(in interface{}) int64 {
switch i := in.(type) {
case float64:
return int64(i)
case float32:
return int64(i)
case int64:
return i
case int32:
return int64(i)
case int16:
return int64(i)
case int8:
return int64(i)
case int:
return int64(i)
case uint64:
return int64(i)
case uint32:
return int64(i)
case uint16:
return int64(i)
case uint8:
return int64(i)
case uint:
return int64(i)
case string:
inAsInt, err := strconv.ParseInt(i, 10, 64)
if err == nil {
return inAsInt
}
panic(err)
}
panic(fmt.Sprintf("asInt() not supported on %v %v", reflect.TypeOf(in), in))
}
func AsFloat(in interface{}) float64 {
switch i := in.(type) {
case float64:
return i
case float32:
return float64(i)
case int64:
return float64(i)
case int32:
return float64(i)
case int16:
return float64(i)
case int8:
return float64(i)
case int:
return float64(i)
case uint64:
return float64(i)
case uint32:
return float64(i)
case uint16:
return float64(i)
case uint8:
return float64(i)
case uint:
return float64(i)
case string:
inAsFloat, err := strconv.ParseFloat(i, 64)
if err == nil {
return inAsFloat
}
panic(err)
}
panic(fmt.Sprintf("asFloat() not supported on %v %v", reflect.TypeOf(in), in))
}

31
expr/function_test.go Normal file
View File

@ -0,0 +1,31 @@
package expr
import (
"github.com/stretchr/testify/assert"
"testing"
)
func TestAsFloat(t *testing.T) {
assert.Equal(t, int64(1), AsInt(int32(1)))
assert.Equal(t, int64(1), AsInt(int64(1)))
assert.Equal(t, int64(1), AsInt("1"))
assert.Panics(t, func() { AsInt("1.56") })
}
func TestAsFloat2(t *testing.T) {
assert.Equal(t, 1.24, AsFloat(1.24))
assert.Equal(t, 1.65, AsFloat("1.65"))
}
func TestAsStr(t *testing.T) {
assert.Equal(t, "1.24", AsStr(1.24))
assert.Equal(t, "1", AsStr(1))
}
func TestJsonPath(t *testing.T) {
simpleJson := "{\"employee\":{\"name\":\"sonoo\",\"salary\":56000,\"married\":true}}"
arrayJson := "{\"employees\":[{\"name\":\"Shyam\",\"email\":\"shyamjaiswal@gmail.com\"},{\"name\":\"Bob\",\"email\":\"bob32@gmail.com\"}," +
"{\"name\":\"Jai\",\"email\":\"jai87@gmail.com\"}]}"
assert.Equal(t, "sonoo", JsonPath(simpleJson, "$.employee.name"))
assert.Equal(t, "Bob", JsonPath(arrayJson, "$.employees[1].name"))
}

27
file/file.go Normal file
View File

@ -0,0 +1,27 @@
package file
import (
"os"
"github.com/pkg/errors"
)
// IsDirectory returns whether or not the given file is a directory
func IsDirectory(path string) (bool, error) {
fileOrDir, err := os.Open(path)
if err != nil {
return false, errors.WithStack(err)
}
defer func() { _ = fileOrDir.Close() }()
stat, err := fileOrDir.Stat()
if err != nil {
return false, errors.WithStack(err)
}
return stat.IsDir(), nil
}
// Exists returns whether or not a path exists
func Exists(path string) bool {
_, err := os.Stat(path)
return !os.IsNotExist(err)
}

39
file/file_test.go Normal file
View File

@ -0,0 +1,39 @@
package file
import (
"fmt"
"path/filepath"
"runtime"
"testing"
"github.com/stretchr/testify/assert"
"github.com/argoproj/pkg/rand"
)
// TestIsDirectory tests if a path is a directory. Errors if directory doesn't exist
func TestIsDirectory(t *testing.T) {
_, filename, _, ok := runtime.Caller(0)
if !ok {
panic("could not determine test directory")
}
testDir := filepath.Dir(filename)
isDir, err := IsDirectory(testDir)
assert.Nil(t, err)
assert.True(t, isDir)
isDir, err = IsDirectory(filename)
assert.Nil(t, err)
assert.False(t, isDir)
isDir, err = IsDirectory("doesnt-exist")
assert.NotNil(t, err)
assert.False(t, isDir)
}
func TestExists(t *testing.T) {
assert.True(t, Exists("/"))
randFilePath := fmt.Sprintf("/%s", rand.RandString(10))
assert.False(t, Exists(randFilePath))
}

117
go.mod
View File

@ -1,83 +1,48 @@
module github.com/argoproj/pkg/v2
module github.com/argoproj/pkg
go 1.23.5
toolchain go1.24.1
go 1.14
require (
github.com/felixge/httpsnoop v1.0.4
github.com/golang/protobuf v1.5.4
github.com/grpc-ecosystem/grpc-gateway v1.16.0
github.com/sirupsen/logrus v1.9.3
github.com/stretchr/testify v1.10.0
golang.org/x/net v0.42.0
k8s.io/api v0.32.2
k8s.io/apimachinery v0.32.2
k8s.io/client-go v0.32.2
)
require (
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/jsonreference v0.20.2 // indirect
github.com/go-openapi/swag v0.23.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/google/gnostic-models v0.6.8 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/spf13/pflag v1.0.6 // indirect
github.com/x448/float16 v0.8.4 // indirect
golang.org/x/oauth2 v0.23.0 // indirect
golang.org/x/sys v0.34.0 // indirect
golang.org/x/term v0.33.0 // indirect
golang.org/x/text v0.27.0 // indirect
golang.org/x/time v0.7.0 // indirect
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
google.golang.org/grpc v1.56.3 // indirect
google.golang.org/protobuf v1.35.1 // indirect
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/klog/v2 v2.130.1 // indirect
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect
sigs.k8s.io/yaml v1.4.0 // indirect
github.com/aws/aws-sdk-go v1.33.16
github.com/dustin/go-humanize v1.0.0
github.com/felixge/httpsnoop v1.0.1
github.com/golang/protobuf v1.3.3
github.com/grpc-ecosystem/grpc-gateway v1.14.6
github.com/minio/minio-go/v7 v7.0.2
github.com/oliveagle/jsonpath v0.0.0-20180606110733-2e52cf6e6852
github.com/pkg/errors v0.9.1
github.com/sirupsen/logrus v1.6.0
github.com/smartystreets/goconvey v1.6.4 // indirect
github.com/spf13/cobra v1.0.0
github.com/stretchr/testify v1.6.1
golang.org/x/net v0.0.0-20200202094626-16171245cfb2
k8s.io/api v0.17.8
k8s.io/apimachinery v0.17.8
k8s.io/client-go v0.17.8
k8s.io/klog/v2 v2.5.0
)
replace (
k8s.io/api => k8s.io/api v0.32.2
k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.32.2
k8s.io/apimachinery => k8s.io/apimachinery v0.32.2
k8s.io/apiserver => k8s.io/apiserver v0.32.2
k8s.io/cli-runtime => k8s.io/cli-runtime v0.32.2
k8s.io/client-go => k8s.io/client-go v0.32.2
k8s.io/cloud-provider => k8s.io/cloud-provider v0.32.2
k8s.io/cluster-bootstrap => k8s.io/cluster-bootstrap v0.32.2
k8s.io/code-generator => k8s.io/code-generator v0.32.2
k8s.io/component-base => k8s.io/component-base v0.32.2
k8s.io/cri-api => k8s.io/cri-api v0.32.2
k8s.io/csi-translation-lib => k8s.io/csi-translation-lib v0.32.2
k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.32.2
k8s.io/kube-controller-manager => k8s.io/kube-controller-manager v0.32.2
k8s.io/kube-proxy => k8s.io/kube-proxy v0.32.2
k8s.io/kube-scheduler => k8s.io/kube-scheduler v0.32.2
k8s.io/kubectl => k8s.io/kubectl v0.32.2
k8s.io/kubelet => k8s.io/kubelet v0.32.2
k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.32.2
k8s.io/metrics => k8s.io/metrics v0.32.2
k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.32.2
// https://github.com/kubernetes/kubernetes/issues/79384#issuecomment-505627280
k8s.io/api => k8s.io/api v0.17.8
k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.17.8 // indirect
k8s.io/apimachinery => k8s.io/apimachinery v0.17.8 // indirect
k8s.io/apiserver => k8s.io/apiserver v0.17.8
k8s.io/cli-runtime => k8s.io/cli-runtime v0.17.8
k8s.io/client-go => k8s.io/client-go v0.17.8
k8s.io/cloud-provider => k8s.io/cloud-provider v0.17.8
k8s.io/cluster-bootstrap => k8s.io/cluster-bootstrap v0.17.8
k8s.io/code-generator => k8s.io/code-generator v0.17.8
k8s.io/component-base => k8s.io/component-base v0.17.8
k8s.io/cri-api => k8s.io/cri-api v0.17.8
k8s.io/csi-translation-lib => k8s.io/csi-translation-lib v0.17.8
k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.17.8
k8s.io/kube-controller-manager => k8s.io/kube-controller-manager v0.17.8
k8s.io/kube-proxy => k8s.io/kube-proxy v0.17.8
k8s.io/kube-scheduler => k8s.io/kube-scheduler v0.17.8
k8s.io/kubectl => k8s.io/kubectl v0.17.8
k8s.io/kubelet => k8s.io/kubelet v0.17.8
k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.17.8
k8s.io/metrics => k8s.io/metrics v0.17.8
k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.17.8
)

423
go.sum
View File

@ -1,222 +1,353 @@
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=
github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/aws/aws-sdk-go v1.33.16 h1:h/3BL2BQMEbS67BPoEo/5jD8IPGVrKBmoa4S9mBBntw=
github.com/aws/aws-sdk-go v1.33.16/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
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/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
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/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ=
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
github.com/go-logr/logr v0.4.0 h1:K7/B1jt6fIBQVd4Owv2MqGQClcgf0R266+7C/QjRcLc=
github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d h1:3PaI8p3seN09VjbTYC/QWlUZdZ1qS1zGjy7LH2Wt07I=
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef h1:veQD95Isof8w9/WXiA+pa3tz3fJXkt5B7QaRBrM62gk=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
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.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo=
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d h1:7XGaL1e6bYS1yIonGp9761ExpPPV1ui0SAC59Yube9k=
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.14.6 h1:8ERzHx8aj1Sc47mu9n/AksaKCSWrMchFtkdrS4BIj5o=
github.com/grpc-ecosystem/grpc-gateway v1.14.6/go.mod h1:zdiPV4Yse/1gnckTHtghG4GkDEdKCRJduHpTxT3/jcw=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/imdario/mergo v0.3.5 h1:JboBksRwiiAJWvIYJVo46AfV+IAIKZpfrSzVKj42R4Q=
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jmespath/go-jmespath v0.3.0 h1:OS12ieG61fsCg5+qLJ+SsW9NicxNkg3b25OyT2yCeUc=
github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
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/klauspost/cpuid v1.2.3 h1:CCtW0xUnWGVINKvE/WWOYKdsPV6mawAtvQuSl8guwQs=
github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
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 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=
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 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/minio/md5-simd v1.1.0 h1:QPfiOqlZH+Cj9teu0t9b1nTBfPbyTl16Of5MeuShdK4=
github.com/minio/md5-simd v1.1.0/go.mod h1:XpBqgZULrMYD3R+M28PcmP0CkI7PEMzB3U77ZrKZ0Gw=
github.com/minio/minio-go/v7 v7.0.2 h1:P/7wFd4KrRBHVo7AKdcqO+9ReoS+XpMjfRFoE5quH0E=
github.com/minio/minio-go/v7 v7.0.2/go.mod h1:dJ80Mv2HeGkYLH1sqS/ksz07ON6csH3S6JUMSQ2zAns=
github.com/minio/sha256-simd v0.1.1 h1:5QHSlgo3nt5yKOJrC7W8w7X+NFl8cMPZm96iu8kKUJU=
github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM=
github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4=
github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/oliveagle/jsonpath v0.0.0-20180606110733-2e52cf6e6852 h1:Yl0tPBa8QPjGmesFh1D0rDy+q1Twx6FyU7VWHi8wZbI=
github.com/oliveagle/jsonpath v0.0.0-20180606110733-2e52cf6e6852/go.mod h1:eqOVx5Vwu4gd2mmMZvVZsgIqNSaW3xxRThUJ0k/TPk4=
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8=
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975 h1:/Tl7pH94bvbAAHBdZJT947M/+gp0+CqQXDtMRC0fseo=
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
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/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-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-20190620200207-3b0461eec859/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-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-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-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
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-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190209173611-3b5209105503/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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg=
golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-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-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
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/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 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
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-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884 h1:fiNLklpBwWK1mth30Hlwk+fcdBmIALlgF5iy77O37Ig=
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A=
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.21.0/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.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
google.golang.org/grpc v1.56.3 h1:8I4C0Yq1EjstUzUJzpcRVbuYA2mODtEmpWiQoN/b2nc=
google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s=
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
google.golang.org/grpc v1.29.1 h1:EC2SB8S04d2r73uptxphDSUG+kTKVgjRPF+N3xpxRB4=
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
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-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4=
gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/ini.v1 v1.57.0 h1:9unxIsFcTt4I55uWluz+UmL95q4kdJ0buvQ1ZIqVQww=
gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
k8s.io/api v0.32.2 h1:bZrMLEkgizC24G9eViHGOPbW+aRo9duEISRIJKfdJuw=
k8s.io/api v0.32.2/go.mod h1:hKlhk4x1sJyYnHENsrdCWw31FEmCijNGPJO5WzHiJ6Y=
k8s.io/apimachinery v0.32.2 h1:yoQBR9ZGkA6Rgmhbp/yuT9/g+4lxtsGYwW6dR6BDPLQ=
k8s.io/apimachinery v0.32.2/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
k8s.io/client-go v0.32.2 h1:4dYCD4Nz+9RApM2b/3BtVvBHw54QjMFUl1OLcJG5yOA=
k8s.io/client-go v0.32.2/go.mod h1:fpZ4oJXclZ3r2nDOv+Ux3XcJutfrwjKTCHz2H3sww94=
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y=
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4=
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro=
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8=
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo=
sigs.k8s.io/structured-merge-diff/v4 v4.4.2 h1:MdmvkGuXi/8io6ixD5wud3vOLwc1rj0aNqRlpuvjmwA=
sigs.k8s.io/structured-merge-diff/v4 v4.4.2/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4=
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
k8s.io/api v0.17.8 h1:8JHlbqJ3A6sGhoacXfu/sASSD+HWWqVq67qt9lyB0kU=
k8s.io/api v0.17.8/go.mod h1:N++Llhs8kCixMUoCaXXAyMMPbo8dDVnh+IQ36xZV2/0=
k8s.io/apimachinery v0.17.8 h1:zXvd8rYMAjRJXpILP9tdAiUnFIENM9EmHuE81apIoms=
k8s.io/apimachinery v0.17.8/go.mod h1:Lg8zZ5iC/O8UjCqW6DNhcQG2m4TdjF9kwG3891OWbbA=
k8s.io/client-go v0.17.8 h1:cuZSfjqVrNjoZ3wViQHljFPyWMOcgxUjjmQs5Rifbxk=
k8s.io/client-go v0.17.8/go.mod h1:SJsDS64AAtt9VZyeaQMb4Ck5etCitZ/FwajWdzua5eY=
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
k8s.io/klog/v2 v2.5.0 h1:8mOnjf1RmUPW6KRqQCfYSZq/K20Unmp3IhuZUhxl8KI=
k8s.io/klog/v2 v2.5.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec=
k8s.io/kube-openapi v0.0.0-20200410145947-bcb3869e6f29/go.mod h1:F+5wygcW0wmRTnM3cOgIqGivxkwSWIWT5YdsDbeAOaU=
k8s.io/utils v0.0.0-20191114184206-e782cd3c129f h1:GiPwtSzdP43eI1hpPCbROQCCIgCuiMMNF8YUVLF3vJo=
k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
sigs.k8s.io/structured-merge-diff/v2 v2.0.1/go.mod h1:Wb7vfKAodbKgf6tn1Kl0VvGj7mRH6DGaRcixXEJXTsE=
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=

View File

@ -10,7 +10,6 @@ import (
"time"
"github.com/felixge/httpsnoop"
//nolint:staticcheck // SA1019 Can't migrate due to grpc gateway using old package and major version bump needed
"github.com/golang/protobuf/proto"
"github.com/grpc-ecosystem/grpc-gateway/runtime"
log "github.com/sirupsen/logrus"
@ -18,10 +17,9 @@ import (
)
type messageMarshaler struct {
fields map[string]interface{}
exclude bool
isSSE bool
fieldProcessor FieldProcessor
fields map[string]interface{}
exclude bool
isSSE bool
}
func (m *messageMarshaler) Unmarshal(data []byte, v interface{}) error {
@ -44,32 +42,12 @@ func (m *messageMarshaler) ContentType() string {
}
}
// FieldProcessor is a function that handles included/excluded fields
type FieldProcessor func(val interface{}, fields map[string]interface{}, exclude bool) (interface{}, error)
func (m *messageMarshaler) Marshal(v interface{}) ([]byte, error) {
var dataBytes []byte
var err error
if len(m.fields) == 0 {
dataBytes, err = json.Marshal(v)
if err != nil {
return nil, err
}
} else if m.fieldProcessor != nil {
res, err := m.fieldProcessor(v, m.fields, m.exclude)
if err != nil {
return nil, err
}
dataBytes, err = json.Marshal(res)
if err != nil {
return nil, err
}
} else {
dataBytes, err = json.Marshal(v)
if err != nil {
return nil, err
}
dataBytes, err := json.Marshal(v)
if err != nil {
return nil, err
}
if len(m.fields) > 0 {
if _, ok := v.([]interface{}); ok {
data := make([]interface{}, 0)
err = json.Unmarshal(dataBytes, &data)
@ -133,7 +111,7 @@ func (m *messageMarshaler) processItem(path []string, item interface{}) {
}
}
func newMarshaler(req *http.Request, isSSE bool) *messageMarshaler {
func newMarshaler(req *http.Request, isSSE bool) runtime.Marshaler {
fieldsQuery := req.URL.Query().Get("fields")
fields := make(map[string]interface{})
exclude := false
@ -159,15 +137,6 @@ type StreamForwarderFunc func(
opts ...func(context.Context, http.ResponseWriter, proto.Message) error,
)
func flush(flusher http.Flusher) {
defer func() {
if r := recover(); r != nil {
log.Warn("recovered in flush, issue with writer inside http.ResponseWriter")
}
}()
flusher.Flush()
}
func writeKeepalive(w http.ResponseWriter, mut *sync.Mutex) {
mut.Lock()
defer mut.Unlock()
@ -179,7 +148,7 @@ func writeKeepalive(w http.ResponseWriter, mut *sync.Mutex) {
if err != nil {
log.Warnf("failed to write http keepalive response: %v", err)
} else if f, ok := w.(http.Flusher); ok {
flush(f)
f.Flush()
}
}
@ -226,13 +195,11 @@ func NewStreamForwarder(messageKey func(proto.Message) (string, error)) StreamFo
opts ...func(context.Context, http.ResponseWriter, proto.Message) error,
) {
isSSE := req.Header.Get("Accept") == "text/event-stream"
processCtx, cancel := context.WithCancel(ctx)
defer cancel()
if isSSE {
w.Header().Set("Content-Type", "text/event-stream")
w.Header().Set("Transfer-Encoding", "chunked")
w.Header().Set("X-Content-Type-Options", "nosniff")
w = withKeepalive(processCtx, w)
w = withKeepalive(ctx, w)
}
dataByKey := make(map[string][]byte)
m := newMarshaler(req, isSSE)
@ -267,21 +234,6 @@ func NewStreamForwarder(messageKey func(proto.Message) (string, error)) StreamFo
runtime.ForwardResponseStream(ctx, mux, m, w, req, recv, opts...)
}
}
func UnaryForwarderWithFieldProcessor(fieldProcessor FieldProcessor) func(
ctx context.Context,
mux *runtime.ServeMux,
marshaler runtime.Marshaler,
w http.ResponseWriter,
req *http.Request,
resp proto.Message,
opts ...func(context.Context, http.ResponseWriter, proto.Message) error,
) {
return func(ctx context.Context, mux *runtime.ServeMux, marshaler runtime.Marshaler, w http.ResponseWriter, req *http.Request, resp proto.Message, opts ...func(context.Context, http.ResponseWriter, proto.Message) error) {
m := newMarshaler(req, false)
m.fieldProcessor = fieldProcessor
runtime.ForwardResponseMessage(ctx, mux, m, w, req, resp, opts...)
}
}
var (
// UnaryForwarder serializes protobuf message to JSON and removes fields using query parameter `fields`.

View File

@ -1,13 +1,9 @@
package http
import (
"bufio"
"bytes"
"fmt"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
type testStruct struct {
@ -21,17 +17,19 @@ type testStruct struct {
Message string `json:"message,omitempty"`
}
var testVal = testStruct{
Metadata: &testStruct{Name: "test"},
Spec: &testStruct{
Source: &testStruct{
Path: "test_path",
var (
testVal = testStruct{
Metadata: &testStruct{Name: "test"},
Spec: &testStruct{
Source: &testStruct{
Path: "test_path",
},
},
},
Status: &testStruct{
Message: "Failed",
},
}
Status: &testStruct{
Message: "Failed",
},
}
)
func TestMarshalerIncludeFields(t *testing.T) {
m := messageMarshaler{fields: map[string]interface{}{
@ -41,8 +39,8 @@ func TestMarshalerIncludeFields(t *testing.T) {
out, err := m.Marshal(testVal)
require.NoError(t, err)
assert.JSONEq(t, `{"metadata":{"name":"test"},"spec":{"source":{"path":"test_path"}}}`, string(out))
assert.Nil(t, err)
assert.Equal(t, `{"metadata":{"name":"test"},"spec":{"source":{"path":"test_path"}}}`, string(out))
}
func TestMarshalerExcludeFields(t *testing.T) {
@ -52,8 +50,8 @@ func TestMarshalerExcludeFields(t *testing.T) {
out, err := m.Marshal(testVal)
require.NoError(t, err)
assert.JSONEq(t, `{"metadata":{},"spec":{"source":{"path":"test_path"}},"status":{"message":"Failed"}}`, string(out))
assert.Nil(t, err)
assert.Equal(t, `{"metadata":{},"spec":{"source":{"path":"test_path"}},"status":{"message":"Failed"}}`, string(out))
}
func TestMarshalerSSE(t *testing.T) {
@ -61,43 +59,8 @@ func TestMarshalerSSE(t *testing.T) {
out, err := m.Marshal(testVal)
require.NoError(t, err)
assert.Nil(t, err)
assert.Equal(t, `data: {"metadata":{"name":"test"},"spec":{"source":{"path":"test_path"}},"status":{"message":"Failed"}}
`, string(out))
}
var flushed bool
type flusher struct {
w *bufio.Writer
}
func (f *flusher) Flush() {
err := f.w.Flush()
if err != nil {
panic(err)
}
flushed = true
}
func TestFlushSuccess(t *testing.T) {
flushed = false
var buf bytes.Buffer
_, _ = fmt.Fprintf(&buf, "Size: %d MB.", 85)
f := flusher{w: bufio.NewWriter(&buf)}
flush(&f)
assert.True(t, flushed)
}
func TestFlushFailed(t *testing.T) {
flushed = false
f := flusher{}
flush(&f)
assert.False(t, flushed)
}

86
humanize/humanize.go Normal file
View File

@ -0,0 +1,86 @@
package humanize
import (
"fmt"
"math"
"strings"
"time"
gohumanize "github.com/dustin/go-humanize"
)
func Timestamp(ts time.Time) string {
return fmt.Sprintf("%s (%s)", ts.Format("Mon Jan 02 15:04:05 -0700"), gohumanize.Time(ts))
}
var relativeMagnitudes = []gohumanize.RelTimeMagnitude{
{D: time.Second, Format: "0 seconds", DivBy: time.Second},
{D: 2 * time.Second, Format: "1 second %s", DivBy: 1},
{D: time.Minute, Format: "%d seconds %s", DivBy: time.Second},
{D: 2 * time.Minute, Format: "1 minute %s", DivBy: 1},
{D: time.Hour, Format: "%d minutes %s", DivBy: time.Minute},
{D: 2 * time.Hour, Format: "1 hour %s", DivBy: 1},
{D: gohumanize.Day, Format: "%d hours %s", DivBy: time.Hour},
{D: 2 * gohumanize.Day, Format: "1 day %s", DivBy: 1},
{D: gohumanize.Week, Format: "%d days %s", DivBy: gohumanize.Day},
{D: 2 * gohumanize.Week, Format: "1 week %s", DivBy: 1},
{D: gohumanize.Month, Format: "%d weeks %s", DivBy: gohumanize.Week},
{D: 2 * gohumanize.Month, Format: "1 month %s", DivBy: 1},
{D: gohumanize.Year, Format: "%d months %s", DivBy: gohumanize.Month},
{D: 18 * gohumanize.Month, Format: "1 year %s", DivBy: 1},
{D: 2 * gohumanize.Year, Format: "2 years %s", DivBy: 1},
{D: gohumanize.LongTime, Format: "%d years %s", DivBy: gohumanize.Year},
{D: math.MaxInt64, Format: "a long while %s", DivBy: 1},
}
// TruncatedDuration returns a duration truncated to a single unit
func TruncatedDuration(d time.Duration) string {
start := time.Time{}
finish := start.Add(d)
return strings.TrimSpace(gohumanize.CustomRelTime(start, finish, "", "", relativeMagnitudes))
}
// Duration humanizes time.Duration output to a meaningful value with up to two units
func Duration(d time.Duration) string {
if d.Seconds() < 60.0 {
return TruncatedDuration(d)
}
if d.Minutes() < 60.0 {
remainingSeconds := int64(math.Mod(d.Seconds(), 60))
return fmt.Sprintf("%s %d seconds", TruncatedDuration(d), remainingSeconds)
}
if d.Hours() < 24.0 {
remainingMinutes := int64(math.Mod(d.Minutes(), 60))
return fmt.Sprintf("%s %d minutes", TruncatedDuration(d), remainingMinutes)
}
remainingHours := int64(math.Mod(d.Hours(), 24))
return fmt.Sprintf("%s %d hours", TruncatedDuration(d), remainingHours)
}
// RelativeDuration returns a formatted duration from the relative times
func RelativeDuration(start, finish time.Time) string {
if finish.IsZero() && !start.IsZero() {
finish = time.Now().UTC()
}
return Duration(finish.Sub(start))
}
var shortTimeMagnitudes = []gohumanize.RelTimeMagnitude{
{D: time.Second, Format: "0s", DivBy: time.Second},
{D: 2 * time.Second, Format: "1s %s", DivBy: 1},
{D: time.Minute, Format: "%ds %s", DivBy: time.Second},
{D: 2 * time.Minute, Format: "1m %s", DivBy: 1},
{D: time.Hour, Format: "%dm %s", DivBy: time.Minute},
{D: 2 * time.Hour, Format: "1h %s", DivBy: 1},
{D: gohumanize.Day, Format: "%dh %s", DivBy: time.Hour},
{D: 2 * gohumanize.Day, Format: "1d %s", DivBy: 1},
{D: gohumanize.Week, Format: "%dd %s", DivBy: gohumanize.Day},
}
// RelativeDurationShort returns a relative duration in short format
func RelativeDurationShort(start, finish time.Time) string {
if finish.IsZero() && !start.IsZero() {
finish = time.Now().UTC()
}
return strings.TrimSpace(gohumanize.CustomRelTime(start, finish, "", "", shortTimeMagnitudes))
}

68
humanize/humanize_test.go Normal file
View File

@ -0,0 +1,68 @@
package humanize
import (
"testing"
"time"
"github.com/stretchr/testify/assert"
)
// TestRelativeDurationShort tests RelativeDurationShort
func TestRelativeDurationShort(t *testing.T) {
start := time.Now()
end := start
assert.Equal(t, "0s", RelativeDurationShort(start, end))
start = time.Now().Add(-1 * time.Hour)
end = time.Time{}
assert.Equal(t, "1h", RelativeDurationShort(start, end))
start = time.Now().Add(-1 * (time.Hour + 30*time.Minute))
end = time.Time{}
assert.Equal(t, "1h", RelativeDurationShort(start, end))
start = time.Time{}
end = time.Time{}
assert.Equal(t, "0s", RelativeDurationShort(start, end))
}
// TestDuration tests Duration
func TestDuration(t *testing.T) {
assert.Equal(t, "1 second", Duration(time.Second))
assert.Equal(t, "1 minute 0 seconds", Duration(time.Minute))
assert.Equal(t, "1 hour 0 minutes", Duration(time.Hour))
assert.Equal(t, "2 hours 0 minutes", Duration(2*time.Hour))
assert.Equal(t, "10 hours 0 minutes", Duration(10*time.Hour))
assert.Equal(t, "1 day 0 hours", Duration(24*time.Hour))
}
// TestDuration tests Duration
func TestRelativeDuration(t *testing.T) {
start := time.Now()
end := start
assert.Equal(t, "0 seconds", RelativeDuration(start, end))
start = time.Now()
end = start.Add(-1 * time.Second)
assert.Equal(t, "1 second", RelativeDuration(start, end))
start = time.Now()
end = start.Add(-59 * time.Second)
assert.Equal(t, "59 seconds", RelativeDuration(start, end))
start = time.Now().Add(-90 * time.Second)
end = time.Time{}
assert.Equal(t, "1 minute 30 seconds", RelativeDuration(start, end))
start = time.Now().Add(-1 * time.Hour)
end = time.Time{}
assert.Equal(t, "1 hour 0 minutes", RelativeDuration(start, end))
start = time.Now().Add(-1 * (time.Hour + 30*time.Minute))
end = time.Time{}
assert.Equal(t, "1 hour 30 minutes", RelativeDuration(start, end))
start = time.Time{}
end = time.Time{}
assert.Equal(t, "0 seconds", RelativeDuration(start, end))
}

36
json/json.go Normal file
View File

@ -0,0 +1,36 @@
package json
import (
"bytes"
"encoding/json"
)
// DisallowUnknownFields configures the JSON decoder to error out if unknown
// fields come along, instead of dropping them by default.
func DisallowUnknownFields(d *json.Decoder) *json.Decoder {
d.DisallowUnknownFields()
return d
}
// JSONOpt is a decoding option for decoding from JSON format.
type JSONOpt func(*json.Decoder) *json.Decoder
// Unmarshal is a convenience wrapper around json.Unmarshal to support json decode options
func Unmarshal(j []byte, o interface{}, opts ...JSONOpt) error {
d := json.NewDecoder(bytes.NewReader(j))
for _, opt := range opts {
d = opt(d)
}
return d.Decode(&o)
}
// UnmarshalStrict is a convenience wrapper around json.Unmarshal with strict unmarshal options
func UnmarshalStrict(j []byte, o interface{}) error {
return Unmarshal(j, o, DisallowUnknownFields)
}
// IsJSON tests whether or not the suppied byte array is valid JSON
func IsJSON(j []byte) bool {
var js json.RawMessage
return json.Unmarshal(j, &js) == nil
}

43
json/json_test.go Normal file
View File

@ -0,0 +1,43 @@
package json
import (
"testing"
"github.com/stretchr/testify/assert"
)
// TestDisallowUnknownFields tests ability to disallow unknown fields
func TestDisallowUnknownFields(t *testing.T) {
type mystruct struct {
MyField string `json:"myField"`
}
jsonWithUnknownField := []byte(`
{
"myField": "foo",
"unknown": "bar"
}
`)
var obj mystruct
err := Unmarshal(jsonWithUnknownField, &obj)
assert.NoError(t, err)
assert.Equal(t, "foo", obj.MyField)
obj = mystruct{}
err = Unmarshal(jsonWithUnknownField, &obj, DisallowUnknownFields)
assert.Error(t, err)
assert.Equal(t, "foo", obj.MyField)
obj = mystruct{}
err = UnmarshalStrict(jsonWithUnknownField, &obj)
assert.Error(t, err)
assert.Equal(t, "foo", obj.MyField)
}
func TestIsJSON(t *testing.T) {
assert.True(t, IsJSON([]byte(`"foo"`)))
assert.True(t, IsJSON([]byte(`{"a": "b"}`)))
assert.True(t, IsJSON([]byte(`[{"a": "b"}]`)))
assert.False(t, IsJSON([]byte(`foo`)))
assert.False(t, IsJSON([]byte(`foo: bar`)))
}

110
jwt/zjwt/zjwt.go Normal file
View File

@ -0,0 +1,110 @@
// The package provides a way to create compact JWTs, "zJWT".
//
// zJWTs for JWT with a large payload can be 1/3 the size of the original.
// This is achieved by gzipping the payload before base 64 encoding it.
//
// zJWTs are only compact if they are smaller than the original JWTs.
// as long as they are smaller than the zJWT representation.
//
// To help differentiate, zJWTs start with "zJWT/v1:"
package zjwt
import (
"bytes"
"compress/gzip"
"encoding/base64"
"fmt"
"io/ioutil"
"os"
"strings"
)
var encoding = base64.RawStdEncoding
// some magic text that is easy to search the Internet for and find your way to docs
var magicNumber = "zJWT/v1"
// when to use ZJWT
// - "never" - never use it - good for it goes wrong
// - "" - when better
// - "always" - always use it - good for forcing this on for testing
var featureFlag = os.Getenv("ARGO_ZJWT_FEATURE_FLAG")
// the smallest size JWT we'll compress, 3k chosen as cookies max out at 4k
var minSize = 3000
// ZJWT turns a JWT into either a zJWT or return the original JWT, whichever is smaller.
func ZJWT(text string) (string, error) {
if featureFlag == "never" || featureFlag != "always" && len(text) < minSize {
return text, nil
}
parts := strings.SplitN(text, ".", 3)
if len(parts) != 3 {
return "", fmt.Errorf("JWT '%s' should have 3 dot-delimited parts", text)
}
header := parts[0]
payload := parts[1]
signature := parts[2]
decodedPayload, err := encoding.DecodeString(payload)
if err != nil {
return "", err
}
var buf bytes.Buffer
w := gzip.NewWriter(&buf)
_, err = w.Write(decodedPayload)
if err != nil {
return "", err
}
err = w.Flush()
if err != nil {
return "", err
}
err = w.Close()
if err != nil {
return "", err
}
compressedPayload := encoding.EncodeToString(buf.Bytes())
zJWT := fmt.Sprintf("%s.%s.%s.%s", magicNumber, header, compressedPayload, signature)
if featureFlag == "always" || len(zJWT) < len(text) {
return zJWT, nil
} else {
return text, nil
}
}
// JWT expands either a zJWT or a JWT to a JWT.
func JWT(text string) (string, error) {
parts := strings.SplitN(text, ".", 4)
if len(parts) == 3 {
return text, nil
}
if len(parts) != 4 {
return "", fmt.Errorf("zJWT '%s' should have 4 dot-delimited parts", text)
}
part0 := parts[0]
if part0 != magicNumber {
return "", fmt.Errorf("the first part of the zJWT '%s' does not equal '%s'", part0, magicNumber)
}
header := parts[1]
payload := parts[2]
signature := parts[3]
decodedPayload, err := encoding.DecodeString(payload)
if err != nil {
return "", err
}
r, err := gzip.NewReader(bytes.NewReader(decodedPayload))
if err != nil {
return "", err
}
uncompressedPayload, err := ioutil.ReadAll(r)
if err != nil {
return "", err
}
err = r.Close()
if err != nil {
return "", err
}
encodedPayload := encoding.EncodeToString(uncompressedPayload)
jwt := fmt.Sprintf("%s.%s.%s", header, encodedPayload, signature)
return jwt, nil
}

49
jwt/zjwt/zjwt_test.go Normal file
View File

@ -0,0 +1,49 @@
package zjwt
import (
"testing"
"github.com/stretchr/testify/assert"
)
func init() {
minSize = 0
}
func TestCompactor(t *testing.T) {
t.Run("Small", func(t *testing.T) {
jwt := `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.EpM5XBzTJZ4J8AfoJEcJrjth8pfH28LWdjLo90sYb9g`
compactJWT, err := ZJWT(jwt)
assert.NoError(t, err)
expandedJWT, err := JWT(compactJWT)
assert.NoError(t, err)
assert.Equal(t, jwt, expandedJWT)
assert.Equal(t, jwt, compactJWT)
assert.Equal(t, 100, 100*len(compactJWT)/len(jwt))
})
t.Run("Large", func(t *testing.T) {
jwt := `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJncm91cHMiOlsiU0JHIiwiU0JHOk1lbWJlcnMiLCJTQkc6dWkiLCJTQkc6emlvbiIsIlNCRzpoYXJtb255LXVpLWNvbXBvbmVudHMtZXh0ZW5kZWQiLCJTQkc6bmV4Z2VuLXNlcnZpY2VzLXRlYW0iLCJTQkc6c2JnLXFhLXdyaXRlIiwiU0JHOmFkbWluLXFiYS11aS1hdXRvbWF0aW9uIiwiU0JHOnNiZy9xYm8tYXV0b21hdGlvbi10ZWFtLWFkbWluIiwiU0JHOmdpdGxhYi10b29scy13cml0ZSIsIlNCRzpTQkctT2ZmZXJpbmdzLVFFIiwiU0JHOnFiby1iaWxsaW5nLXRlc3QtcWUtdGVhbSIsIlNCRzpxYm8tYmlsbGluZy10ZXN0LWFkbWluIiwiU0JHOnFiYS1xdWFsaXR5LXRlYW0iLCJTQkc6cWJvLWZ0dS1xdWFsaXR5LWFkbWluLXRlYW0iLCJTQkc6cWJhLW1pZ3JhdGlvbi10b29scy10ZWFtIiwiU0JHOlhjZWxsZW5jZUZvcnVtSlMiLCJTQkc6dHJpbml0eWpzLW1vYmlsZSIsIm1vbmV5LWFuZC1tYWdpYyIsIm1vbmV5LWFuZC1tYWdpYzpPd25lcnMiLCJtb25leS1hbmQtbWFnaWM6TSAmIE0gU2NydW0gVGVhbSIsInBheXJvbGwiLCJwYXlyb2xsOkRFUFJFQ0FURURfT3duZXJzIiwicGF5cm9sbDpQYXlyb2xsQWxsIiwiU0JHLVBDUy1DSUNEIiwiU0JHLVBDUy1DSUNEOk93bmVycyIsIlNCLVFCTy1RRS1PZmZlcmluZ3MiLCJTQi1RQk8tUUUtT2ZmZXJpbmdzOk93bmVycyIsIlNCLVFCTy1RRS1PZmZlcmluZ3M6cWJvLWxxYS1hZG1pbiIsIlNCLVFCTy1RRS1PZmZlcmluZ3M6UUUtR3JvdXAiLCJTQi1RQk8tUUUtUGF5bWVudHMiLCJTQi1RQk8tUUUtUGF5bWVudHM6T3duZXJzIiwiU0ItUUJPLVFFLVBheW1lbnRzOnFiby1wYXltZW50cy1xZS10ZWFtIiwiU0ItUUJPLVFFLVBheXJvbGwiLCJTQi1RQk8tUUUtUGF5cm9sbDpPd25lcnMiLCJTQi1RQk8tUUUtUGF5cm9sbDpTQi1RQk8tUUUtUGF5cm9sbCIsIlNCLVFCTy1RRS1QYXlyb2xsOnBheXJvbGwtcWUtdGVhbSIsIml0YWciLCJpdGFnOml0YWctdWkiLCJpdGFnOml0YWctd3MiLCJpdGFnOm1hcnRpbmktanMiLCJnaXRsYWItbWlncmF0aW9uLTAwMDEiLCJnaXRsYWItbWlncmF0aW9uLTAwMDE6T3duZXJzIiwiZ2l0bGFiLW1pZ3JhdGlvbi0wMDAyIiwiZ2l0bGFiLW1pZ3JhdGlvbi0wMDAyOk93bmVycyIsImdpdGxhYi1taWdyYXRpb24tMDA0NSIsImdpdGxhYi1taWdyYXRpb24tMDA0NTpPd25lcnMiLCJnaXRsYWItbWlncmF0aW9uLTAwNDYiLCJnaXRsYWItbWlncmF0aW9uLTAwNDY6T3duZXJzIiwiU0JHLVBsdWdpbnMiLCJTQkctUGx1Z2luczpDb3JlIiwiU0JHLVBsdWdpbnM6aW1wcm92ZWQtaW52ZW50b3J5IiwiU0JHLVBsdWdpbnM6d2ludm9pY2UtZ21haWwiLCJBbHRpbWV0cmlrIiwiQWx0aW1ldHJpazpBbHRpbWV0cmlrIiwiU0ItUUJPLVFFLU9mZmVyaW5ncy1Nb2JpbGUiLCJTQi1RQk8tUUUtT2ZmZXJpbmdzLU1vYmlsZTpPd25lcnMiLCJRQk8tQ29yZSIsIlFCTy1Db3JlOnFiby1qYXZhLW1vbm9saXRoLXdyaXRlLWFjY2VzcyIsIlFCTy1JbnZlbnRvcnkiLCJRQk8tSW52ZW50b3J5OkFkbWlucyIsIlFCTy1JbnZlbnRvcnk6Q29udHJpYnV0b3JzIiwiUUJPLUludmVudG9yeTpDSUNEIiwiUUJPLUJpbGxpbmciLCJRQk8tQmlsbGluZzpCaWxsaW5nLVVJLUFkbWluIiwiU0JHLVFFIiwiZmFicmljLWFwcC1zaGVsbHMiLCJmYWJyaWMtYXBwLXNoZWxsczpNb2JpbGUgU2hlbGwiLCJmYWJyaWMtYXBwLXVpLWNvbXBvbmVudHMiLCJmYWJyaWMtYXBwLXVpLWNvbXBvbmVudHM6ZmFicmljLWFwcC11aS1jb21wb25lbnRzLW1lbWJlcnMiLCJmYWJyaWMtYXBwLXVpLWNvbXBvbmVudHM6SURTIEV4dGVuZGVkIENvcmUgVGVhbSIsImRldi10ZXN0IiwiZGV2LXRlc3Q6VEVQIiwidGVzdC1wbGF0Zm9ybSIsInRlc3QtcGxhdGZvcm06dGVzdC1wbGF0Zm9ybS1vcmctYWRtaW5zIiwidGVzdC1wbGF0Zm9ybTpvdmVyd2F0Y2gtY29yZSIsIm9wZW4tdWktY29tcG9uZW50cyIsIlNCU0VHLUVQSUMiLCJhY2NvdW50aW5nLWNvcmUiLCJhY2NvdW50aW5nLWNvcmU6YWNjb3VudGluZy1jb3JlLXFiby1jYXNlY2VudGVyLXVpIiwia3ViZXJuZXRlcyIsImt1YmVybmV0ZXM6c2Jnc2VnLWNkcC10ZWFtIiwic3ZjLXNic2VnLWNkcCIsIlNVRFMiLCJTVURTOlNVRFMiLCJhcHAtc2hlbGwiLCJhcHAtc2hlbGw6YXJnb2NkLWFkbWlucyIsIlNCRy1UcmlhZ2VCb3QiLCJzYW5kYm94LXNhbmRib3giLCJzYW5kYm94LXNhbmRib3g6dXgtd29ya3Nob3AtcmFqIiwic2FuZGJveC1zYW5kYm94OnV4LXdzLXJhaiIsInNhbmRib3gtc2FuZGJveDpyYWotdGVzdC13cyIsInBheXJvbGwtcGF5Y2hlY2siLCJwYXlyb2xsLXBheWNoZWNrOlNCR1NDVEVQIiwiYXBwLXVpY29tcG9uZW50cyIsImFwcC11aWNvbXBvbmVudHM6UGxheWdyb3VuZC1ydmFzaWthcmxhIiwiYXBwLXVpY29tcG9uZW50czphcHAtdWljb21wb25lbnRzLWlkcy1kYXNoYm9hcmQtdWkiLCJhcHAtdWljb21wb25lbnRzOmlkcy1wbGF5Z3JvdW5kLXVpIiwiVVgtSW5mcmEiLCJVWC1JbmZyYTpDb3JlIiwiZGVzaWduLXN5c3RlbXMiXX0.XDozAEiz9AIVkA3DFbPOKG6msM43gT5zup3oxsHg_4Q`
compactJWT, err := ZJWT(jwt)
assert.NoError(t, err)
expandedJWT, err := JWT(compactJWT)
assert.NoError(t, err)
assert.Equal(t, jwt, expandedJWT)
assert.Equal(t, 37, 100*len(compactJWT)/len(jwt))
})
}
func TestCompact(t *testing.T) {
_, err := ZJWT(".")
assert.Error(t, err)
}
func TestExpand(t *testing.T) {
_, err := JWT(".")
assert.Error(t, err)
_, err = JWT("...")
assert.Error(t, err)
_, err = JWT("zJWT/v1:...")
assert.Error(t, err)
_, err = JWT("zJWT/v1:..!.")
assert.Error(t, err)
}

20
kube/cli/cli.go Normal file
View File

@ -0,0 +1,20 @@
package cli
import (
"os"
"github.com/spf13/cobra"
"k8s.io/client-go/tools/clientcmd"
)
// AddKubectlFlagsToCmd adds kubectl like flags to a command and returns the ClientConfig interface
// for retrieving the values.
func AddKubectlFlagsToCmd(cmd *cobra.Command) clientcmd.ClientConfig {
loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
loadingRules.DefaultClientConfig = &clientcmd.DefaultClientConfig
overrides := clientcmd.ConfigOverrides{}
kflags := clientcmd.RecommendedConfigOverrideFlags("")
cmd.PersistentFlags().StringVar(&loadingRules.ExplicitPath, "kubeconfig", "", "Path to a kube config. Only required if out-of-cluster")
clientcmd.BindOverrideFlags(&overrides, cmd.PersistentFlags(), kflags)
return clientcmd.NewInteractiveDeferredLoadingClientConfig(loadingRules, &overrides, os.Stdin)
}

27
kube/errors/errors.go Normal file
View File

@ -0,0 +1,27 @@
package errors
import (
"net/http"
"strings"
"github.com/pkg/errors"
apierr "k8s.io/apimachinery/pkg/api/errors"
)
// IsRequestEntityTooLargeErr determines if err is an error which indicates the size of the request
// was too large for the server to handle.
func IsRequestEntityTooLargeErr(err error) bool {
err = errors.Cause(err)
switch t := err.(type) {
case apierr.APIStatus:
if t.Status().Code == http.StatusRequestEntityTooLarge {
return true
}
// This also manifest with a 500 error with the message:
// etcdserver: request is too large
if strings.Contains(t.Status().Message, "request is too large") {
return true
}
}
return false
}

View File

@ -0,0 +1,34 @@
package errors
import (
"net/http"
"testing"
"github.com/stretchr/testify/assert"
apierr "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// TestIsRequestEntityTooLargeErr test
func TestIsRequestEntityTooLargeErr(t *testing.T) {
assert.False(t, IsRequestEntityTooLargeErr(nil))
err := &apierr.StatusError{ErrStatus: metav1.Status{
Status: metav1.StatusFailure,
Code: http.StatusRequestEntityTooLarge,
}}
assert.True(t, IsRequestEntityTooLargeErr(err))
err = &apierr.StatusError{ErrStatus: metav1.Status{
Status: metav1.StatusFailure,
Code: http.StatusInternalServerError,
Message: "etcdserver: request is too large",
}}
assert.True(t, IsRequestEntityTooLargeErr(err))
err = &apierr.StatusError{ErrStatus: metav1.Status{
Status: metav1.StatusFailure,
Code: http.StatusInternalServerError,
}}
assert.False(t, IsRequestEntityTooLargeErr(err))
}

View File

@ -0,0 +1,46 @@
package unstructured
import (
"time"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/informers/internalinterfaces"
"k8s.io/client-go/tools/cache"
)
// NewUnstructuredInformer constructs a new informer for Unstructured type.
// Always prefer using an informer factory to get a shared informer instead of getting an independent
// one. This reduces memory footprint and number of connections to the server.
func NewUnstructuredInformer(resource schema.GroupVersionResource, client dynamic.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer {
return NewFilteredUnstructuredInformer(resource, client, namespace, resyncPeriod, indexers, nil)
}
// NewFilteredUnstructuredInformer constructs a new informer for Unstructured type.
// Always prefer using an informer factory to get a shared informer instead of getting an independent
// one. This reduces memory footprint and number of connections to the server.
func NewFilteredUnstructuredInformer(resource schema.GroupVersionResource, client dynamic.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer {
return cache.NewSharedIndexInformer(
&cache.ListWatch{
ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
if tweakListOptions != nil {
tweakListOptions(&options)
}
return client.Resource(resource).Namespace(namespace).List(options)
},
WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
if tweakListOptions != nil {
tweakListOptions(&options)
}
return client.Resource(resource).Namespace(namespace).Watch(options)
},
},
&unstructured.Unstructured{},
resyncPeriod,
indexers,
)
}

View File

@ -2,7 +2,7 @@ package kubeclientmetrics
import (
"encoding/json"
"io"
"io/ioutil"
"net/http"
"path"
"regexp"
@ -103,7 +103,7 @@ func handleCreate(r *http.Request) ResourceInfo {
log.WithField("Kind", kind).Warnf("Unable to Process Create request: %v", err)
return ResourceInfo{}
}
body, err := io.ReadAll(bodyIO)
body, err := ioutil.ReadAll(bodyIO)
if err != nil {
log.WithField("Kind", kind).Warnf("Unable to Process Create request: %v", err)
return ResourceInfo{}

View File

@ -1,7 +1,6 @@
package kubeclientmetrics
import (
"context"
"net/http"
"net/http/httptest"
"net/url"
@ -24,19 +23,10 @@ type fakeWrapper struct {
func (f fakeWrapper) RoundTrip(r *http.Request) (*http.Response, error) {
resp := httptest.NewRecorder()
resp.Code = 201
assert.Equal(f.t, f.expectedCount, f.currentCount)
assert.Equal(f.t, f.currentCount, f.expectedCount)
return resp.Result(), nil
}
func NewConfig(url string) *rest.Config {
return &rest.Config{
Host: url,
ContentConfig: rest.ContentConfig{
ContentType: "application/json",
},
}
}
// TestWrappingTwice Ensures that the config doesn't lose any previous wrappers and the previous wrapper
// gets executed first
func TestAddMetricsTransportWrapperWrapTwice(t *testing.T) {
@ -58,9 +48,10 @@ func TestAddMetricsTransportWrapperWrapTwice(t *testing.T) {
})
client := kubernetes.NewForConfigOrDie(newConfig)
_, _ = client.AppsV1().ReplicaSets(metav1.NamespaceDefault).Get(context.Background(), "test", metav1.GetOptions{})
_, _ = client.AppsV1().ReplicaSets(metav1.NamespaceDefault).Get("test", metav1.GetOptions{})
// Ensures second wrapper added by AddMetricsTransportWrapper is executed
assert.Equal(t, 1, currentCount)
}
func newGetRequest(str string) *http.Request {
@ -171,13 +162,13 @@ func TestParseRequest(t *testing.T) {
},
{
testName: "VirtualService GET",
url: "https://127.0.0.1/apis/networking.istio.io/v1alpha3/namespaces/default/virtualservices/virtual-service",
url: "https://127.0.0.1/apis/networking.istio.io/v1alpha3/namespaces/default/virtualservices/virutal-service",
expected: ResourceInfo{
Server: "https://127.0.0.1",
Verb: Get,
Kind: "virtualservices",
Namespace: "default",
Name: "virtual-service",
Name: "virutal-service",
},
},
{
@ -296,7 +287,9 @@ func TestGetRequest(t *testing.T) {
}))
defer ts.Close()
executed := false
config := NewConfig(ts.URL)
config := &rest.Config{
Host: ts.URL,
}
newConfig := AddMetricsTransportWrapper(config, func(info ResourceInfo) error {
assert.Equal(t, expectedStatusCode, info.StatusCode)
assert.Equal(t, "replicasets", info.Kind)
@ -307,7 +300,7 @@ func TestGetRequest(t *testing.T) {
return nil
})
client := kubernetes.NewForConfigOrDie(newConfig)
_, _ = client.AppsV1().ReplicaSets(metav1.NamespaceDefault).Get(context.Background(), "test", metav1.GetOptions{})
_, _ = client.AppsV1().ReplicaSets(metav1.NamespaceDefault).Get("test", metav1.GetOptions{})
assert.True(t, executed)
}
@ -318,18 +311,20 @@ func TestListRequest(t *testing.T) {
}))
defer ts.Close()
executed := false
config := NewConfig(ts.URL)
config := &rest.Config{
Host: ts.URL,
}
newConfig := AddMetricsTransportWrapper(config, func(info ResourceInfo) error {
assert.Equal(t, expectedStatusCode, info.StatusCode)
assert.Equal(t, "replicasets", info.Kind)
assert.Equal(t, metav1.NamespaceDefault, info.Namespace)
assert.Empty(t, info.Name)
assert.Equal(t, "", info.Name)
assert.Equal(t, List, info.Verb)
executed = true
return nil
})
client := kubernetes.NewForConfigOrDie(newConfig)
_, _ = client.AppsV1().ReplicaSets(metav1.NamespaceDefault).List(context.Background(), metav1.ListOptions{})
_, _ = client.AppsV1().ReplicaSets(metav1.NamespaceDefault).List(metav1.ListOptions{})
assert.True(t, executed)
}
@ -340,7 +335,9 @@ func TestCreateRequest(t *testing.T) {
}))
defer ts.Close()
executed := false
config := NewConfig(ts.URL)
config := &rest.Config{
Host: ts.URL,
}
newConfig := AddMetricsTransportWrapper(config, func(info ResourceInfo) error {
assert.Equal(t, expectedStatusCode, info.StatusCode)
assert.Equal(t, "replicasets", info.Kind)
@ -357,7 +354,7 @@ func TestCreateRequest(t *testing.T) {
Namespace: metav1.NamespaceDefault,
},
}
_, _ = client.AppsV1().ReplicaSets(metav1.NamespaceDefault).Create(context.Background(), rs, metav1.CreateOptions{})
_, _ = client.AppsV1().ReplicaSets(metav1.NamespaceDefault).Create(rs)
assert.True(t, executed)
}
@ -368,7 +365,9 @@ func TestDeleteRequest(t *testing.T) {
}))
defer ts.Close()
executed := false
config := NewConfig(ts.URL)
config := &rest.Config{
Host: ts.URL,
}
newConfig := AddMetricsTransportWrapper(config, func(info ResourceInfo) error {
assert.Equal(t, expectedStatusCode, info.StatusCode)
assert.Equal(t, "replicasets", info.Kind)
@ -379,7 +378,7 @@ func TestDeleteRequest(t *testing.T) {
return nil
})
client := kubernetes.NewForConfigOrDie(newConfig)
_ = client.AppsV1().ReplicaSets(metav1.NamespaceDefault).Delete(context.Background(), "test", metav1.DeleteOptions{})
_ = client.AppsV1().ReplicaSets(metav1.NamespaceDefault).Delete("test", &metav1.DeleteOptions{})
assert.True(t, executed)
}
@ -390,7 +389,9 @@ func TestPatchRequest(t *testing.T) {
}))
defer ts.Close()
executed := false
config := NewConfig(ts.URL)
config := &rest.Config{
Host: ts.URL,
}
newConfig := AddMetricsTransportWrapper(config, func(info ResourceInfo) error {
assert.Equal(t, expectedStatusCode, info.StatusCode)
assert.Equal(t, "replicasets", info.Kind)
@ -401,7 +402,7 @@ func TestPatchRequest(t *testing.T) {
return nil
})
client := kubernetes.NewForConfigOrDie(newConfig)
_, _ = client.AppsV1().ReplicaSets(metav1.NamespaceDefault).Patch(context.Background(), "test", types.MergePatchType, []byte("{}"), metav1.PatchOptions{})
_, _ = client.AppsV1().ReplicaSets(metav1.NamespaceDefault).Patch("test", types.MergePatchType, []byte("{}"))
assert.True(t, executed)
}
@ -412,7 +413,9 @@ func TestUpdateRequest(t *testing.T) {
}))
defer ts.Close()
executed := false
config := NewConfig(ts.URL)
config := &rest.Config{
Host: ts.URL,
}
newConfig := AddMetricsTransportWrapper(config, func(info ResourceInfo) error {
assert.Equal(t, expectedStatusCode, info.StatusCode)
assert.Equal(t, "replicasets", info.Kind)
@ -428,7 +431,7 @@ func TestUpdateRequest(t *testing.T) {
Name: "test",
},
}
_, _ = client.AppsV1().ReplicaSets(metav1.NamespaceDefault).Update(context.Background(), rs, metav1.UpdateOptions{})
_, _ = client.AppsV1().ReplicaSets(metav1.NamespaceDefault).Update(rs)
assert.True(t, executed)
}
@ -439,7 +442,9 @@ func TestUnknownRequest(t *testing.T) {
}))
defer ts.Close()
executed := false
config := NewConfig(ts.URL)
config := &rest.Config{
Host: ts.URL,
}
newConfig := AddMetricsTransportWrapper(config, func(info ResourceInfo) error {
assert.Equal(t, expectedStatusCode, info.StatusCode)
assert.Equal(t, Unknown, info.Verb)
@ -447,6 +452,6 @@ func TestUnknownRequest(t *testing.T) {
return nil
})
client := kubernetes.NewForConfigOrDie(newConfig)
client.Discovery().RESTClient().Verb("invalid-verb").Do(context.Background())
client.Discovery().RESTClient().Verb("invalid-verb").Do()
assert.True(t, executed)
}

43
rand/rand.go Normal file
View File

@ -0,0 +1,43 @@
package rand
import (
"math/rand"
"sync"
"time"
)
const letterBytes = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
const (
letterIdxBits = 6 // 6 bits to represent a letter index
letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
letterIdxMax = 63 / letterIdxBits // # of letter indices fitting in 63 bits
)
var srcMutex = sync.Mutex{}
var src = rand.NewSource(time.Now().UnixNano())
// RandString returns a cryptographically-secure pseudo-random alpha-numeric string of a given length
func RandString(n int) string {
return RandStringCharset(n, letterBytes)
}
// RandStringCharset generates, from a given charset, a cryptographically-secure pseudo-random string of a given length
func RandStringCharset(n int, charset string) string {
srcMutex.Lock()
defer srcMutex.Unlock()
b := make([]byte, n)
// A src.Int63() generates 63 random bits, enough for letterIdxMax characters!
for i, cache, remain := n-1, src.Int63(), letterIdxMax; i >= 0; {
if remain == 0 {
cache, remain = src.Int63(), letterIdxMax
}
if idx := int(cache & letterIdxMask); idx < len(charset) {
b[i] = charset[idx]
i--
}
cache >>= letterIdxBits
remain--
}
return string(b)
}

16
rand/rand_test.go Normal file
View File

@ -0,0 +1,16 @@
package rand
import (
"testing"
)
func TestRandString(t *testing.T) {
ss := RandStringCharset(10, "A")
if ss != "AAAAAAAAAA" {
t.Errorf("Expected 10 As, but got %q", ss)
}
ss = RandStringCharset(5, "ABC123")
if len(ss) != 5 {
t.Errorf("Expected random string of length 10, but got %q", ss)
}
}

298
s3/s3.go Normal file
View File

@ -0,0 +1,298 @@
package s3
import (
"context"
"os"
"path"
"path/filepath"
"strings"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/minio/minio-go/v7"
"github.com/minio/minio-go/v7/pkg/credentials"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
)
const nullIAMEndpoint = ""
type S3Client interface {
// PutFile puts a single file to a bucket at the specified key
PutFile(bucket, key, path string) error
// PutDirectory puts a complete directory into a bucket key prefix, with each file in the directory
// a separate key in the bucket.
PutDirectory(bucket, key, path string) error
// GetFile downloads a file to a local file path
GetFile(bucket, key, path string) error
// GetDirectory downloads a directory to a local file path
GetDirectory(bucket, key, path string) error
// ListDirectory list the contents of a directory/bucket
ListDirectory(bucket, keyPrefix string) ([]string, error)
// IsDirectory tests if the key is acting like a s3 directory
IsDirectory(bucket, key string) (bool, error)
// BucketExists returns whether a bucket exists
BucketExists(bucket string) (bool, error)
// MakeBucket creates a bucket with name bucketName and options opts
MakeBucket(bucketName string, opts minio.MakeBucketOptions) error
}
type S3ClientOpts struct {
Endpoint string
Region string
Secure bool
AccessKey string
SecretKey string
Trace bool
RoleARN string
RoleSessionName string
UseSDKCreds bool
}
type s3client struct {
S3ClientOpts
minioClient *minio.Client
ctx context.Context
}
var _ S3Client = &s3client{}
// Get AWS credentials based on default order from aws SDK
func GetAWSCredentials(opts S3ClientOpts) (*credentials.Credentials, error) {
sess := session.Must(session.NewSessionWithOptions(session.Options{
Config: aws.Config{Region: aws.String(opts.Region)},
SharedConfigState: session.SharedConfigEnable,
}))
value, err := sess.Config.Credentials.Get()
if err != nil {
return nil, err
}
return credentials.NewStaticV4(value.AccessKeyID, value.SecretAccessKey, value.SessionToken), nil
}
// GetAssumeRoleCredentials gets Assumed role credentials
func GetAssumeRoleCredentials(opts S3ClientOpts) (*credentials.Credentials, error) {
sess := session.Must(session.NewSession())
// Create the credentials from AssumeRoleProvider to assume the role
// referenced by the "myRoleARN" ARN. Prompt for MFA token from stdin.
creds := stscreds.NewCredentials(sess, opts.RoleARN)
value, err := creds.Get()
if err != nil {
return nil, err
}
return credentials.NewStaticV4(value.AccessKeyID, value.SecretAccessKey, value.SessionToken), nil
}
func GetCredentials(opts S3ClientOpts) (*credentials.Credentials, error) {
if opts.AccessKey != "" && opts.SecretKey != "" {
log.Infof("Creating minio client %s using static credentials", opts.Endpoint)
return credentials.NewStaticV4(opts.AccessKey, opts.SecretKey, ""), nil
} else if opts.RoleARN != "" {
log.Infof("Creating minio client %s using assumed-role credentials", opts.RoleARN)
return GetAssumeRoleCredentials(opts)
} else if opts.UseSDKCreds {
log.Infof("Creating minio client using AWS SDK credentials")
return GetAWSCredentials(opts)
} else {
log.Infof("Creating minio client using IAM role")
return credentials.NewIAM(nullIAMEndpoint), nil
}
}
// NewS3Client instantiates a new S3 client object backed
func NewS3Client(ctx context.Context, opts S3ClientOpts) (S3Client, error) {
s3cli := s3client{
S3ClientOpts: opts,
}
s3cli.AccessKey = strings.TrimSpace(s3cli.AccessKey)
s3cli.SecretKey = strings.TrimSpace(s3cli.SecretKey)
var minioClient *minio.Client
var err error
credentials, err := GetCredentials(opts)
if err != nil {
return nil, errors.WithStack(err)
}
minioOpts := &minio.Options{Creds: credentials, Secure: s3cli.Secure, Region: s3cli.Region}
minioClient, err = minio.New(s3cli.Endpoint, minioOpts)
if err != nil {
return nil, errors.WithStack(err)
}
if opts.Trace {
minioClient.TraceOn(log.StandardLogger().Out)
}
s3cli.ctx = ctx
s3cli.minioClient = minioClient
return &s3cli, nil
}
// PutFile puts a single file to a bucket at the specified key
func (s *s3client) PutFile(bucket, key, path string) error {
log.Infof("Saving from %s to s3 (endpoint: %s, bucket: %s, key: %s)", path, s.Endpoint, bucket, key)
// NOTE: minio will detect proper mime-type based on file extension
_, err := s.minioClient.FPutObject(s.ctx, bucket, key, path, minio.PutObjectOptions{})
if err != nil {
return errors.WithStack(err)
}
return nil
}
func (s *s3client) BucketExists(bucketName string) (bool, error) {
log.Infof("Checking if bucket %s exists.", bucketName)
result, err := s.minioClient.BucketExists(s.ctx, bucketName)
return result, errors.WithStack(err)
}
func (s *s3client) MakeBucket(bucketName string, opts minio.MakeBucketOptions) error {
log.Infof("Creating bucket: %s. (Region: %s, ObjectLocking: %t)", bucketName, opts.Region, opts.ObjectLocking)
err := s.minioClient.MakeBucket(s.ctx, bucketName, opts)
return errors.WithStack(err)
}
type uploadTask struct {
key string
path string
}
func generatePutTasks(keyPrefix, rootPath string) chan uploadTask {
rootPath = filepath.Clean(rootPath) + string(os.PathSeparator)
uploadTasks := make(chan uploadTask)
visit := func(localPath string, fi os.FileInfo, err error) error {
relPath := strings.TrimPrefix(localPath, rootPath)
if fi.IsDir() {
return nil
}
if fi.Mode()&os.ModeSymlink != 0 {
return nil
}
t := uploadTask{
key: path.Join(keyPrefix, relPath),
path: localPath,
}
uploadTasks <- t
return nil
}
go func() {
_ = filepath.Walk(rootPath, visit)
close(uploadTasks)
}()
return uploadTasks
}
// PutDirectory puts a complete directory into a bucket key prefix, with each file in the directory
// a separate key in the bucket.
func (s *s3client) PutDirectory(bucket, key, path string) error {
for putTask := range generatePutTasks(key, path) {
err := s.PutFile(bucket, putTask.key, putTask.path)
if err != nil {
return err
}
}
return nil
}
// GetFile downloads a file to a local file path
func (s *s3client) GetFile(bucket, key, path string) error {
log.Infof("Getting from s3 (endpoint: %s, bucket: %s, key: %s) to %s", s.Endpoint, bucket, key, path)
err := s.minioClient.FGetObject(s.ctx, bucket, key, path, minio.GetObjectOptions{})
if err != nil {
return errors.WithStack(err)
}
return nil
}
// GetDirectory downloads a s3 directory to a local path
func (s *s3client) GetDirectory(bucket, keyPrefix, path string) error {
log.Infof("Getting directory from s3 (endpoint: %s, bucket: %s, key: %s) to %s", s.Endpoint, bucket, keyPrefix, path)
keys, err := s.ListDirectory(bucket, keyPrefix)
if err != nil {
return err
}
for _, objKey := range keys {
relKeyPath := strings.TrimPrefix(objKey, keyPrefix)
localPath := filepath.Join(path, relKeyPath)
err := s.minioClient.FGetObject(s.ctx, bucket, objKey, localPath, minio.GetObjectOptions{})
if err != nil {
return errors.WithStack(err)
}
}
return nil
}
// IsDirectory tests if the key is acting like a s3 directory. This just means it has at least one
// object which is prefixed with the given key
func (s *s3client) IsDirectory(bucket, key string) (bool, error) {
doneCh := make(chan struct{})
defer close(doneCh)
listOpts := minio.ListObjectsOptions{
Prefix: key,
Recursive: false,
}
objCh := s.minioClient.ListObjects(s.ctx, bucket, listOpts)
for obj := range objCh {
if obj.Err != nil {
return false, errors.WithStack(obj.Err)
} else {
return true, nil
}
}
return false, nil
}
func (s *s3client) ListDirectory(bucket, keyPrefix string) ([]string, error) {
log.Infof("Listing directory from s3 (endpoint: %s, bucket: %s, key: %s)", s.Endpoint, bucket, keyPrefix)
keyPrefix = filepath.Clean(keyPrefix) + "/"
if os.PathSeparator == '\\' {
keyPrefix = strings.ReplaceAll(keyPrefix, "\\", "/")
}
doneCh := make(chan struct{})
defer close(doneCh)
listOpts := minio.ListObjectsOptions{
Prefix: keyPrefix,
Recursive: true,
}
var out []string
objCh := s.minioClient.ListObjects(s.ctx, bucket, listOpts)
for obj := range objCh {
if obj.Err != nil {
return nil, errors.WithStack(obj.Err)
}
if strings.HasSuffix(obj.Key, "/") {
// When a dir is created through AWS S3 console, a nameless obj will be created
// automatically, its key will be {dir_name} + "/". This obj does not display in the
// console, but you can see it when using aws cli.
// If obj.Key ends with "/" means it's a dir obj, we need to skip it, otherwise it
// will be downloaded as a regular file with the same name as the dir, and it will
// creates error when downloading the files under the dir.
continue
}
out = append(out, obj.Key)
}
return out, nil
}
// IsS3ErrCode returns if the supplied error is of a specific S3 error code
func IsS3ErrCode(err error, code string) bool {
err = errors.Cause(err)
if minioErr, ok := err.(minio.ErrorResponse); ok {
return minioErr.Code == code
}
return false
}

66
s3/s3_test.go Normal file
View File

@ -0,0 +1,66 @@
package s3
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
)
// TestNewS3Client tests the s3 construtor
func TestNewS3Client(t *testing.T) {
opts := S3ClientOpts{
Endpoint: "foo.com",
Region: "us-south-3",
Secure: false,
AccessKey: "key",
SecretKey: "secret",
Trace: true,
}
s3If, err := NewS3Client(context.Background(), opts)
assert.NoError(t, err)
s3cli := s3If.(*s3client)
assert.Equal(t, opts.Endpoint, s3cli.Endpoint)
assert.Equal(t, opts.Region, s3cli.Region)
assert.Equal(t, opts.Secure, s3cli.Secure)
assert.Equal(t, opts.AccessKey, s3cli.AccessKey)
assert.Equal(t, opts.Trace, s3cli.Trace)
// s3cli.minioClient.
// s3client.minioClient
}
// TestNewS3Client tests the s3 construtor
func TestNewS3ClientWithDiff(t *testing.T) {
t.Run("IAMRole", func(t *testing.T) {
opts := S3ClientOpts{
Endpoint: "foo.com",
Region: "us-south-3",
Secure: false,
Trace: true,
}
s3If, err := NewS3Client(context.Background(), opts)
assert.NoError(t, err)
s3cli := s3If.(*s3client)
assert.Equal(t, opts.Endpoint, s3cli.Endpoint)
assert.Equal(t, opts.Region, s3cli.Region)
assert.Equal(t, opts.Trace, s3cli.Trace)
assert.Equal(t, opts.Endpoint, s3cli.minioClient.EndpointURL().Host)
})
t.Run("AssumeIAMRole", func(t *testing.T) {
opts := S3ClientOpts{
Endpoint: "foo.com",
Region: "us-south-3",
Secure: false,
Trace: true,
RoleARN: "testARN",
}
s3If, err := NewS3Client(context.Background(), opts)
assert.NoError(t, err)
s3cli := s3If.(*s3client)
assert.Equal(t, opts.Endpoint, s3cli.Endpoint)
assert.Equal(t, opts.Region, s3cli.Region)
assert.Equal(t, opts.Trace, s3cli.Trace)
assert.Equal(t, opts.Endpoint, s3cli.minioClient.EndpointURL().Host)
})
}

View File

@ -72,6 +72,7 @@ func LogStats() {
runtime.ReadMemStats(&m)
log.Infof("Alloc=%v TotalAlloc=%v Sys=%v NumGC=%v Goroutines=%d",
m.Alloc/1024, m.TotalAlloc/1024, m.Sys/1024, m.NumGC, runtime.NumGoroutine())
}
// LogStack will log the current stack

View File

@ -70,7 +70,8 @@ func RegisterHeapDumper(filePath string) {
func LogStats() {
var m runtime.MemStats
runtime.ReadMemStats(&m)
log.Infof("Alloc=%v TotalAlloc=%v Sys=%v NumGC=%v Goroutines=%d", m.Alloc/1024, m.TotalAlloc/1024, m.Sys/1024, m.NumGC, runtime.NumGoroutine())
log.Infof("Alloc=%v TotalAlloc=%v Sys=%v NumGC=%v Goroutines=%d",
m.Alloc/1024, m.TotalAlloc/1024, m.Sys/1024, m.NumGC, runtime.NumGoroutine())
}

60
strftime/strftime.go Normal file
View File

@ -0,0 +1,60 @@
package strftime
// Adapted from: github.com/jehiah/go-strftime to expose available format characters
import (
"strings"
"time"
)
// FormatChars are the supported characters for strftime. It is a subset of all strftime characters
// See also, time/format.go
var FormatChars = map[rune]string{
'B': "January",
'b': "Jan",
'm': "01",
'A': "Monday",
'a': "Mon",
'd': "02",
'H': "15",
'I': "03",
'M': "04",
'S': "05",
'Y': "2006",
'y': "06",
'p': "PM",
'Z': "MST",
'z': "-0700",
'L': ".000",
}
// Format formats a time object using strftime syntax
func Format(format string, t time.Time) string {
retval := make([]byte, 0, len(format))
for i, ni := 0, 0; i < len(format); i = ni + 2 {
ni = strings.IndexByte(format[i:], '%')
if ni < 0 {
ni = len(format)
} else {
ni += i
}
retval = append(retval, []byte(format[i:ni])...)
if ni+1 < len(format) {
c := format[ni+1]
if c == '%' {
retval = append(retval, '%')
} else {
if layoutCmd, ok := FormatChars[rune(c)]; ok {
retval = append(retval, []byte(t.Format(layoutCmd))...)
} else {
retval = append(retval, '%', c)
}
}
} else {
if ni < len(format) {
retval = append(retval, '%')
}
}
}
return string(retval)
}

49
strftime/strftime_test.go Normal file
View File

@ -0,0 +1,49 @@
package strftime
import (
"fmt"
"testing"
"time"
)
func ExampleFormat() {
t := time.Unix(1340244776, 0)
utc, _ := time.LoadLocation("UTC")
t = t.In(utc)
fmt.Println(Format("%Y-%m-%d %H:%M:%S", t))
// Output:
// 2012-06-21 02:12:56
}
func TestNoLeadingPercentSign(t *testing.T) {
tm := time.Unix(1340244776, 0)
utc, _ := time.LoadLocation("UTC")
tm = tm.In(utc)
result := Format("aaabbb0123456789%Y", tm)
if result != "aaabbb01234567892012" {
t.Logf("%s != %s", result, "aaabbb01234567892012")
t.Fail()
}
}
func TestUnsupported(t *testing.T) {
tm := time.Unix(1340244776, 0)
utc, _ := time.LoadLocation("UTC")
tm = tm.In(utc)
result := Format("%0%1%%%2", tm)
if result != "%0%1%%2" {
t.Logf("%s != %s", result, "%0%1%%2")
t.Fail()
}
}
func TestRubyStrftime(t *testing.T) {
tm := time.Unix(1340244776, 0)
utc, _ := time.LoadLocation("UTC")
tm = tm.In(utc)
result := Format("%H:%M:%S%L", tm)
if result != "02:12:56.000" {
t.Logf("%s != %s", result, "02:12:56.000")
t.Fail()
}
}

View File

@ -5,23 +5,21 @@ import "sync"
type KeyLock interface {
Lock(key string)
Unlock(key string)
RLock(key string)
RUnlock(key string)
}
type keyLock struct {
guard sync.RWMutex
locks map[string]*sync.RWMutex
locks map[string]*sync.Mutex
}
func NewKeyLock() KeyLock {
return &keyLock{
guard: sync.RWMutex{},
locks: map[string]*sync.RWMutex{},
locks: map[string]*sync.Mutex{},
}
}
func (l *keyLock) getLock(key string) *sync.RWMutex {
func (l *keyLock) getLock(key string) *sync.Mutex {
l.guard.RLock()
if lock, ok := l.locks[key]; ok {
l.guard.RUnlock()
@ -36,7 +34,7 @@ func (l *keyLock) getLock(key string) *sync.RWMutex {
return lock
}
lock := &sync.RWMutex{}
lock := &sync.Mutex{}
l.locks[key] = lock
l.guard.Unlock()
return lock
@ -49,11 +47,3 @@ func (l *keyLock) Lock(key string) {
func (l *keyLock) Unlock(key string) {
l.getLock(key).Unlock()
}
func (l *keyLock) RLock(key string) {
l.getLock(key).RLock()
}
func (l *keyLock) RUnlock(key string) {
l.getLock(key).RUnlock()
}

View File

@ -7,7 +7,7 @@ import (
"github.com/stretchr/testify/assert"
)
func TestLockLock(t *testing.T) {
func TestKeyLock(t *testing.T) {
l := NewKeyLock()
l.Lock("my-key")
@ -32,78 +32,3 @@ func TestLockLock(t *testing.T) {
l.Unlock("my-key")
}
func TestLockRLock(t *testing.T) {
l := NewKeyLock()
l.Lock("my-key")
unlocked := false
wg := sync.WaitGroup{}
wg.Add(1)
go func() {
l.RLock("my-key")
unlocked = true
wg.Done()
}()
assert.False(t, unlocked)
l.Unlock("my-key")
wg.Wait()
assert.True(t, unlocked)
l.RUnlock("my-key")
}
func TestRLockLock(t *testing.T) {
l := NewKeyLock()
l.RLock("my-key")
unlocked := false
wg := sync.WaitGroup{}
wg.Add(1)
go func() {
l.Lock("my-key")
unlocked = true
wg.Done()
}()
assert.False(t, unlocked)
l.RUnlock("my-key")
wg.Wait()
assert.True(t, unlocked)
l.Unlock("my-key")
}
func TestRLockRLock(t *testing.T) {
l := NewKeyLock()
l.RLock("my-key")
unlocked := false
wg := sync.WaitGroup{}
wg.Add(1)
go func() {
l.RLock("my-key")
unlocked = true
wg.Done()
}()
wg.Wait()
assert.True(t, unlocked)
l.RUnlock("my-key")
l.RUnlock("my-key")
}

View File

@ -1,11 +1,12 @@
package time
import (
"fmt"
"log"
"regexp"
"strconv"
"time"
"github.com/pkg/errors"
)
var durationRegex = regexp.MustCompile(`^(\d+)([smhd])$`)
@ -14,7 +15,7 @@ var durationRegex = regexp.MustCompile(`^(\d+)([smhd])$`)
func ParseDuration(duration string) (*time.Duration, error) {
matches := durationRegex.FindStringSubmatch(duration)
if len(matches) != 3 {
return nil, fmt.Errorf("invalid since format '%s', expected format <duration><unit> (e.g. 3h)", duration)
return nil, errors.Errorf("Invalid since format '%s'. Expected format <duration><unit> (e.g. 3h)\n", duration)
}
amount, err := strconv.ParseInt(matches[1], 10, 64)
if err != nil {

View File

@ -5,7 +5,6 @@ import (
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// TestParseDuration tests TestParseDuration
@ -25,17 +24,17 @@ func TestParseDuration(t *testing.T) {
}
for _, data := range testdata {
dur, err := ParseDuration(data.duration)
require.NoError(t, err)
assert.Nil(t, err)
assert.Equal(t, dur.Nanoseconds(), data.xVal.Nanoseconds())
}
_, err := ParseDuration("1z")
assert.Error(t, err)
assert.NotNil(t, err)
}
// TestParseSince tests parsing of since strings
func TestParseSince(t *testing.T) {
oneDayAgo, err := ParseSince("1d")
require.NoError(t, err)
assert.Nil(t, err)
yesterday := time.Now().UTC().Add(-24 * time.Hour)
assert.Equal(t, yesterday.Minute(), oneDayAgo.Minute())
}