feat: implement ping command (#31)
* build: use deepmap/oapi-codegen to generate an HTTP client * feat: add global CLI options * feat: load local config to find host and token * feat: implement ping command * test: add unit tests for ping command
This commit is contained in:
parent
8c062cacf0
commit
ca8a5c5364
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
api/types.gen.go linguist-generated=true
|
||||
api/client.gen.go linguist-generated=true
|
13
Makefile
13
Makefile
@ -26,6 +26,14 @@ export GO_TEST=go test
|
||||
GO_TEST_PATHS=./...
|
||||
|
||||
### Build / dependency management
|
||||
internal/api/types.gen.go: internal/api/api.yml internal/api/gen.go
|
||||
go generate ./api
|
||||
|
||||
internal/api/client.gen.go: internal/api/api.yml internal/api/gen.go
|
||||
go generate ./api
|
||||
|
||||
openapi: internal/api/types.gen.go internal/api/client.gen.go
|
||||
|
||||
fmt: $(SOURCES_NO_VENDOR)
|
||||
gofmt -w -s $^
|
||||
go run github.com/daixiang0/gci -w $^
|
||||
@ -38,11 +46,12 @@ influx: bin/$(GOOS)/influx
|
||||
vendor: go.mod go.sum
|
||||
go mod vendor
|
||||
|
||||
build: fmt influx
|
||||
build: openapi fmt influx
|
||||
|
||||
clean:
|
||||
$(RM) -r bin
|
||||
$(RM) -r vendor
|
||||
$(RM) internal/api/types.gen.go internal/api/client.gen.go
|
||||
|
||||
### Linters
|
||||
checkfmt:
|
||||
@ -65,4 +74,4 @@ test-race:
|
||||
$(GO_TEST) -v -race -count=1 $(GO_TEST_PATHS)
|
||||
|
||||
### List of all targets that don't produce a file
|
||||
.PHONY: influx fmt build checkfmt checktidy staticcheck vet test test-race
|
||||
.PHONY: influx openapi fmt build checkfmt checktidy staticcheck vet test test-race
|
||||
|
@ -1,19 +1,103 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/influxdata/influx-cli/v2/internal"
|
||||
"github.com/influxdata/influx-cli/v2/internal/api"
|
||||
"github.com/influxdata/influx-cli/v2/internal/config"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
// Fields set via ldflags at build time.
|
||||
var (
|
||||
version = "dev"
|
||||
commit = "none"
|
||||
date = ""
|
||||
)
|
||||
|
||||
var (
|
||||
tokenFlag = "token"
|
||||
hostFlag = "host"
|
||||
skipVerifyFlag = "skip-verify"
|
||||
traceIdFlag = "trace-debug-id"
|
||||
configPathFlag = "config-path"
|
||||
configNameFlag = "active-config"
|
||||
)
|
||||
|
||||
// loadConfig reads CLI configs from disk, returning the config with the
|
||||
// name specified over the CLI (or default if no name was given).
|
||||
func loadConfig(ctx *cli.Context) (config.Config, error) {
|
||||
configs := config.GetConfigsOrDefault(ctx.String(configPathFlag))
|
||||
configName := ctx.String(configNameFlag)
|
||||
if configName != "" {
|
||||
if err := configs.Switch(configName); err != nil {
|
||||
return config.Config{}, err
|
||||
}
|
||||
}
|
||||
return configs.Active(), nil
|
||||
}
|
||||
|
||||
// newApiClient returns an API client configured to communicate with a remote InfluxDB instance over HTTP.
|
||||
// Client parameters are pulled from the CLI context.
|
||||
func newApiClient(ctx *cli.Context, injectToken bool) (api.ClientWithResponsesInterface, error) {
|
||||
cfg, err := loadConfig(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ctx.IsSet(tokenFlag) {
|
||||
cfg.Token = ctx.String(tokenFlag)
|
||||
}
|
||||
if ctx.IsSet(hostFlag) {
|
||||
cfg.Host = ctx.String(hostFlag)
|
||||
}
|
||||
|
||||
clientTransport := http.DefaultTransport.(*http.Transport)
|
||||
clientTransport.TLSClientConfig = &tls.Config{InsecureSkipVerify: ctx.Bool(skipVerifyFlag)}
|
||||
|
||||
client := &http.Client{Transport: clientTransport}
|
||||
userAgent := fmt.Sprintf("influx/%s (%s) Sha/%s Date/%s", version, runtime.GOOS, commit, date)
|
||||
|
||||
opts := []api.ClientOption{
|
||||
api.WithHTTPClient(client),
|
||||
api.WithRequestEditorFn(func(_ context.Context, req *http.Request) error {
|
||||
req.Header.Set("User-Agent", userAgent)
|
||||
return nil
|
||||
}),
|
||||
}
|
||||
if injectToken {
|
||||
authHeader := fmt.Sprintf("Token %s", cfg.Token)
|
||||
opts = append(opts, api.WithRequestEditorFn(func(_ context.Context, req *http.Request) error {
|
||||
req.Header.Set("Authorization", authHeader)
|
||||
return nil
|
||||
}))
|
||||
}
|
||||
|
||||
return api.NewClientWithResponses(cfg.Host, opts...)
|
||||
}
|
||||
|
||||
// newCli builds a CLI core that reads from stdin, writes to stdout/stderr, and
|
||||
// optionally tracks a trace ID specified over the CLI.
|
||||
func newCli(ctx *cli.Context) *internal.CLI {
|
||||
var traceId *api.TraceSpan
|
||||
if ctx.IsSet(traceIdFlag) {
|
||||
tid := api.TraceSpan(ctx.String(traceIdFlag))
|
||||
traceId = &tid
|
||||
}
|
||||
return &internal.CLI{
|
||||
Stdin: ctx.App.Reader,
|
||||
Stdout: ctx.App.Writer,
|
||||
Stderr: ctx.App.ErrWriter,
|
||||
TraceId: traceId,
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
if len(date) == 0 {
|
||||
date = time.Now().UTC().Format(time.RFC3339)
|
||||
@ -21,6 +105,46 @@ func main() {
|
||||
|
||||
cli.VersionFlag = nil
|
||||
|
||||
// NOTE: urfave/cli has dedicated support for global flags, but it only parses those flags
|
||||
// if they're specified before any command names. This is incompatible with the old influx
|
||||
// CLI, which repeatedly registered common flags on every "leaf" command, forcing the flags
|
||||
// to be specified after _all_ command names were given.
|
||||
//
|
||||
// We replicate the pattern from the old CLI so existing scripts and docs stay valid.
|
||||
commonFlags := []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: tokenFlag,
|
||||
Usage: "Authentication token",
|
||||
Aliases: []string{"t"},
|
||||
EnvVars: []string{"INFLUX_TOKEN"},
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: hostFlag,
|
||||
Usage: "HTTP address of InfluxDB",
|
||||
EnvVars: []string{"INFLUX_HOST"},
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: skipVerifyFlag,
|
||||
Usage: "Skip TLS certificate chain and host name verification",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: configPathFlag,
|
||||
Usage: "Path to the influx CLI configurations",
|
||||
EnvVars: []string{"INFLUX_CLI_CONFIGS_PATH"},
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: configNameFlag,
|
||||
Usage: "Config name to use for command",
|
||||
Aliases: []string{"c"},
|
||||
EnvVars: []string{"INFLUX_ACTIVE_CONFIG"},
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: traceIdFlag,
|
||||
Hidden: true,
|
||||
EnvVars: []string{"INFLUX_TRACE_DEBUG_ID"},
|
||||
},
|
||||
}
|
||||
|
||||
app := cli.App{
|
||||
Name: "influx",
|
||||
Usage: "Influx Client",
|
||||
@ -34,6 +158,18 @@ func main() {
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "ping",
|
||||
Usage: "Check the InfluxDB /health endpoint",
|
||||
Flags: commonFlags,
|
||||
Action: func(ctx *cli.Context) error {
|
||||
client, err := newApiClient(ctx, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return newCli(ctx).Ping(ctx.Context, client)
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,12 @@ for file in $(go list -f '{{$dir := .Dir}}{{range .GoFiles}}{{printf "%s/%s\n" $
|
||||
if ! grep -Exq '^// Code generated .* DO NOT EDIT\.$' $file; then
|
||||
FMT_OUT="$(gofmt -l -d -e $file)" # gofmt exits 0 regardless of whether it's formatted.
|
||||
GCI_OUT="$(go run github.com/daixiang0/gci -d $file)"
|
||||
|
||||
# Work around annoying output of gci
|
||||
if [[ "$GCI_OUT" = "skip file $file since no import" ]]; then
|
||||
GCI_OUT=""
|
||||
fi
|
||||
|
||||
if [[ -n "$FMT_OUT" || -n "$GCI_OUT" ]]; then
|
||||
HAS_FMT_ERR=1
|
||||
echo "Not formatted: $file"
|
||||
|
3
go.mod
3
go.mod
@ -3,7 +3,10 @@ module github.com/influxdata/influx-cli/v2
|
||||
go 1.16
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v0.3.1
|
||||
github.com/daixiang0/gci v0.2.8
|
||||
github.com/deepmap/oapi-codegen v1.6.0
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/urfave/cli/v2 v2.3.0
|
||||
honnef.co/go/tools v0.1.3
|
||||
)
|
||||
|
84
go.sum
84
go.sum
@ -2,37 +2,112 @@ github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4=
|
||||
github.com/daixiang0/gci v0.2.8 h1:1mrIGMBQsBu0P7j7m1M8Lb+ZeZxsZL+jyGX4YoMJJpg=
|
||||
github.com/daixiang0/gci v0.2.8/go.mod h1:+4dZ7TISfSmqfAGv59ePaHfNzgGtIkHAhhdKggP1JAc=
|
||||
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/deepmap/oapi-codegen v1.6.0 h1:w/d1ntwh91XI0b/8ja7+u5SvA4IFfM0UNNLmiDR1gg0=
|
||||
github.com/deepmap/oapi-codegen v1.6.0/go.mod h1:ryDa9AgbELGeB+YEXE1dR53yAjHwFvE9iAUlWl9Al3M=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/getkin/kin-openapi v0.53.0 h1:7WzP+MZRRe7YQz2Kc74Ley3dukJmXDvifVbElGmQfoA=
|
||||
github.com/getkin/kin-openapi v0.53.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4=
|
||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs=
|
||||
github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
|
||||
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
|
||||
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||
github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219 h1:utua3L2IbQJmauC5IXdEA547bcoU5dozgQAfc8Onsg4=
|
||||
github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y=
|
||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
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/labstack/echo/v4 v4.2.1 h1:LF5Iq7t/jrtUuSutNuiEWtB5eiHfZ5gSe2pcu5exjQw=
|
||||
github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg=
|
||||
github.com/labstack/gommon v0.3.0 h1:JEeO0bvc78PKdyHxloTKiF8BD5iGrH8T6MSeGvSgob0=
|
||||
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
|
||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8=
|
||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ=
|
||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
|
||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.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/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M=
|
||||
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
|
||||
github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4=
|
||||
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
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-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY=
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
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-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.0.0-20210119194325-5f4716e94777 h1:003p0dJM77cxMSyCPFphvZf/Y5/NXf5fzg6ufd1/Oew=
|
||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/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-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4 h1:myAQVi0cGEoqQVR5POX+8RR2mrocKqNN1hmeMqhX27k=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
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.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20201118003311-bd56c0adb394/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
@ -41,6 +116,13 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/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.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
gopkg.in/yaml.v2 v2.3.0/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=
|
||||
honnef.co/go/tools v0.1.3 h1:qTakTkI6ni6LFD5sBwwsdSO+AQqbSIxOauHTTQKZ/7o=
|
||||
honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las=
|
||||
|
113
internal/api/api.yml
Normal file
113
internal/api/api.yml
Normal file
@ -0,0 +1,113 @@
|
||||
openapi: 3.0.0
|
||||
info:
|
||||
title: Influx API Service
|
||||
version: 2.0.0
|
||||
servers:
|
||||
- url: /api/v2
|
||||
paths:
|
||||
/health:
|
||||
servers:
|
||||
- url: /
|
||||
get:
|
||||
operationId: GetHealth
|
||||
tags:
|
||||
- Health
|
||||
summary: Get the health of an instance
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/TraceSpan'
|
||||
responses:
|
||||
'200':
|
||||
description: The instance is healthy
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/HealthCheck'
|
||||
'503':
|
||||
description: The instance is unhealthy
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/HealthCheck'
|
||||
default:
|
||||
description: Unexpected error
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Error'
|
||||
components:
|
||||
parameters:
|
||||
TraceSpan:
|
||||
in: header
|
||||
name: Zap-Trace-Span
|
||||
description: OpenTracing span context
|
||||
example:
|
||||
trace_id: '1'
|
||||
span_id: '1'
|
||||
baggage:
|
||||
key: value
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
schemas:
|
||||
HealthCheck:
|
||||
type: object
|
||||
required:
|
||||
- name
|
||||
- status
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
message:
|
||||
type: string
|
||||
checks:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/HealthCheck'
|
||||
status:
|
||||
$ref: '#/components/schemas/HealthCheckStatus'
|
||||
version:
|
||||
type: string
|
||||
commit:
|
||||
type: string
|
||||
HealthCheckStatus:
|
||||
type: string
|
||||
readOnly: true
|
||||
enum:
|
||||
- pass
|
||||
- fail
|
||||
Error:
|
||||
properties:
|
||||
code:
|
||||
$ref: '#/components/schemas/ErrorCode'
|
||||
message:
|
||||
readOnly: true
|
||||
description: message is a human-readable message.
|
||||
type: string
|
||||
op:
|
||||
readOnly: true
|
||||
description: op describes the logical code operation during error. Useful for debugging.
|
||||
type: string
|
||||
err:
|
||||
readOnly: true
|
||||
description: err is a stack of errors that occurred during processing of the request. Useful for debugging.
|
||||
type: string
|
||||
required:
|
||||
- code
|
||||
- message
|
||||
ErrorCode:
|
||||
description: machine-readable error code.
|
||||
readOnly: true
|
||||
type: string
|
||||
enum:
|
||||
- internal error
|
||||
- not found
|
||||
- conflict
|
||||
- invalid
|
||||
- unprocessable entity
|
||||
- empty value
|
||||
- unavailable
|
||||
- forbidden
|
||||
- too many requests
|
||||
- unauthorized
|
||||
- method not allowed
|
||||
- request too large
|
263
internal/api/client.gen.go
Normal file
263
internal/api/client.gen.go
Normal file
@ -0,0 +1,263 @@
|
||||
// Package api provides primitives to interact with the openapi HTTP API.
|
||||
//
|
||||
// Code generated by github.com/deepmap/oapi-codegen DO NOT EDIT.
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/deepmap/oapi-codegen/pkg/runtime"
|
||||
)
|
||||
|
||||
// RequestEditorFn is the function signature for the RequestEditor callback function
|
||||
type RequestEditorFn func(ctx context.Context, req *http.Request) error
|
||||
|
||||
// Doer performs HTTP requests.
|
||||
//
|
||||
// The standard http.Client implements this interface.
|
||||
type HttpRequestDoer interface {
|
||||
Do(req *http.Request) (*http.Response, error)
|
||||
}
|
||||
|
||||
// Client which conforms to the OpenAPI3 specification for this service.
|
||||
type Client struct {
|
||||
// The endpoint of the server conforming to this interface, with scheme,
|
||||
// https://api.deepmap.com for example. This can contain a path relative
|
||||
// to the server, such as https://api.deepmap.com/dev-test, and all the
|
||||
// paths in the swagger spec will be appended to the server.
|
||||
Server string
|
||||
|
||||
// Doer for performing requests, typically a *http.Client with any
|
||||
// customized settings, such as certificate chains.
|
||||
Client HttpRequestDoer
|
||||
|
||||
// A list of callbacks for modifying requests which are generated before sending over
|
||||
// the network.
|
||||
RequestEditors []RequestEditorFn
|
||||
}
|
||||
|
||||
// ClientOption allows setting custom parameters during construction
|
||||
type ClientOption func(*Client) error
|
||||
|
||||
// Creates a new Client, with reasonable defaults
|
||||
func NewClient(server string, opts ...ClientOption) (*Client, error) {
|
||||
// create a client with sane default values
|
||||
client := Client{
|
||||
Server: server,
|
||||
}
|
||||
// mutate client and add all optional params
|
||||
for _, o := range opts {
|
||||
if err := o(&client); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
// ensure the server URL always has a trailing slash
|
||||
if !strings.HasSuffix(client.Server, "/") {
|
||||
client.Server += "/"
|
||||
}
|
||||
// create httpClient, if not already present
|
||||
if client.Client == nil {
|
||||
client.Client = &http.Client{}
|
||||
}
|
||||
return &client, nil
|
||||
}
|
||||
|
||||
// WithHTTPClient allows overriding the default Doer, which is
|
||||
// automatically created using http.Client. This is useful for tests.
|
||||
func WithHTTPClient(doer HttpRequestDoer) ClientOption {
|
||||
return func(c *Client) error {
|
||||
c.Client = doer
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithRequestEditorFn allows setting up a callback function, which will be
|
||||
// called right before sending the request. This can be used to mutate the request.
|
||||
func WithRequestEditorFn(fn RequestEditorFn) ClientOption {
|
||||
return func(c *Client) error {
|
||||
c.RequestEditors = append(c.RequestEditors, fn)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// The interface specification for the client above.
|
||||
type ClientInterface interface {
|
||||
// GetHealth request
|
||||
GetHealth(ctx context.Context, params *GetHealthParams, reqEditors ...RequestEditorFn) (*http.Response, error)
|
||||
}
|
||||
|
||||
func (c *Client) GetHealth(ctx context.Context, params *GetHealthParams, reqEditors ...RequestEditorFn) (*http.Response, error) {
|
||||
req, err := NewGetHealthRequest(c.Server, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req = req.WithContext(ctx)
|
||||
if err := c.applyEditors(ctx, req, reqEditors); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.Client.Do(req)
|
||||
}
|
||||
|
||||
// NewGetHealthRequest generates requests for GetHealth
|
||||
func NewGetHealthRequest(server string, params *GetHealthParams) (*http.Request, error) {
|
||||
var err error
|
||||
|
||||
serverURL, err := url.Parse(server)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
operationPath := fmt.Sprintf("/health")
|
||||
if operationPath[0] == '/' {
|
||||
operationPath = operationPath[1:]
|
||||
}
|
||||
operationURL := url.URL{
|
||||
Path: operationPath,
|
||||
}
|
||||
|
||||
queryURL := serverURL.ResolveReference(&operationURL)
|
||||
|
||||
req, err := http.NewRequest("GET", queryURL.String(), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if params.ZapTraceSpan != nil {
|
||||
var headerParam0 string
|
||||
|
||||
headerParam0, err = runtime.StyleParamWithLocation("simple", false, "Zap-Trace-Span", runtime.ParamLocationHeader, *params.ZapTraceSpan)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req.Header.Set("Zap-Trace-Span", headerParam0)
|
||||
}
|
||||
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func (c *Client) applyEditors(ctx context.Context, req *http.Request, additionalEditors []RequestEditorFn) error {
|
||||
for _, r := range c.RequestEditors {
|
||||
if err := r(ctx, req); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, r := range additionalEditors {
|
||||
if err := r(ctx, req); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ClientWithResponses builds on ClientInterface to offer response payloads
|
||||
type ClientWithResponses struct {
|
||||
ClientInterface
|
||||
}
|
||||
|
||||
// NewClientWithResponses creates a new ClientWithResponses, which wraps
|
||||
// Client with return type handling
|
||||
func NewClientWithResponses(server string, opts ...ClientOption) (*ClientWithResponses, error) {
|
||||
client, err := NewClient(server, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ClientWithResponses{client}, nil
|
||||
}
|
||||
|
||||
// WithBaseURL overrides the baseURL.
|
||||
func WithBaseURL(baseURL string) ClientOption {
|
||||
return func(c *Client) error {
|
||||
newBaseURL, err := url.Parse(baseURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Server = newBaseURL.String()
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// ClientWithResponsesInterface is the interface specification for the client with responses above.
|
||||
type ClientWithResponsesInterface interface {
|
||||
// GetHealth request
|
||||
GetHealthWithResponse(ctx context.Context, params *GetHealthParams, reqEditors ...RequestEditorFn) (*GetHealthResponse, error)
|
||||
}
|
||||
|
||||
type GetHealthResponse struct {
|
||||
Body []byte
|
||||
HTTPResponse *http.Response
|
||||
JSON200 *HealthCheck
|
||||
JSON503 *HealthCheck
|
||||
JSONDefault *Error
|
||||
}
|
||||
|
||||
// Status returns HTTPResponse.Status
|
||||
func (r GetHealthResponse) Status() string {
|
||||
if r.HTTPResponse != nil {
|
||||
return r.HTTPResponse.Status
|
||||
}
|
||||
return http.StatusText(0)
|
||||
}
|
||||
|
||||
// StatusCode returns HTTPResponse.StatusCode
|
||||
func (r GetHealthResponse) StatusCode() int {
|
||||
if r.HTTPResponse != nil {
|
||||
return r.HTTPResponse.StatusCode
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// GetHealthWithResponse request returning *GetHealthResponse
|
||||
func (c *ClientWithResponses) GetHealthWithResponse(ctx context.Context, params *GetHealthParams, reqEditors ...RequestEditorFn) (*GetHealthResponse, error) {
|
||||
rsp, err := c.GetHealth(ctx, params, reqEditors...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ParseGetHealthResponse(rsp)
|
||||
}
|
||||
|
||||
// ParseGetHealthResponse parses an HTTP response from a GetHealthWithResponse call
|
||||
func ParseGetHealthResponse(rsp *http.Response) (*GetHealthResponse, error) {
|
||||
bodyBytes, err := ioutil.ReadAll(rsp.Body)
|
||||
defer rsp.Body.Close()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
response := &GetHealthResponse{
|
||||
Body: bodyBytes,
|
||||
HTTPResponse: rsp,
|
||||
}
|
||||
|
||||
switch {
|
||||
case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
|
||||
var dest HealthCheck
|
||||
if err := json.Unmarshal(bodyBytes, &dest); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.JSON200 = &dest
|
||||
|
||||
case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 503:
|
||||
var dest HealthCheck
|
||||
if err := json.Unmarshal(bodyBytes, &dest); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.JSON503 = &dest
|
||||
|
||||
case strings.Contains(rsp.Header.Get("Content-Type"), "json") && true:
|
||||
var dest Error
|
||||
if err := json.Unmarshal(bodyBytes, &dest); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.JSONDefault = &dest
|
||||
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
4
internal/api/gen.go
Normal file
4
internal/api/gen.go
Normal file
@ -0,0 +1,4 @@
|
||||
package api
|
||||
|
||||
//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen -package api -generate types -o types.gen.go api.yml
|
||||
//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen -package api -generate client -o client.gen.go api.yml
|
68
internal/api/types.gen.go
Normal file
68
internal/api/types.gen.go
Normal file
@ -0,0 +1,68 @@
|
||||
// Package api provides primitives to interact with the openapi HTTP API.
|
||||
//
|
||||
// Code generated by github.com/deepmap/oapi-codegen DO NOT EDIT.
|
||||
package api
|
||||
|
||||
// Error defines model for Error.
|
||||
type Error struct {
|
||||
|
||||
// machine-readable error code.
|
||||
Code ErrorCode `json:"code"`
|
||||
|
||||
// err is a stack of errors that occurred during processing of the request. Useful for debugging.
|
||||
Err *string `json:"err,omitempty"`
|
||||
|
||||
// message is a human-readable message.
|
||||
Message string `json:"message"`
|
||||
|
||||
// op describes the logical code operation during error. Useful for debugging.
|
||||
Op *string `json:"op,omitempty"`
|
||||
}
|
||||
|
||||
// machine-readable error code.
|
||||
type ErrorCode string
|
||||
|
||||
// List of ErrorCode
|
||||
const (
|
||||
ErrorCode_conflict ErrorCode = "conflict"
|
||||
ErrorCode_empty_value ErrorCode = "empty value"
|
||||
ErrorCode_forbidden ErrorCode = "forbidden"
|
||||
ErrorCode_internal_error ErrorCode = "internal error"
|
||||
ErrorCode_invalid ErrorCode = "invalid"
|
||||
ErrorCode_method_not_allowed ErrorCode = "method not allowed"
|
||||
ErrorCode_not_found ErrorCode = "not found"
|
||||
ErrorCode_request_too_large ErrorCode = "request too large"
|
||||
ErrorCode_too_many_requests ErrorCode = "too many requests"
|
||||
ErrorCode_unauthorized ErrorCode = "unauthorized"
|
||||
ErrorCode_unavailable ErrorCode = "unavailable"
|
||||
ErrorCode_unprocessable_entity ErrorCode = "unprocessable entity"
|
||||
)
|
||||
|
||||
// HealthCheck defines model for HealthCheck.
|
||||
type HealthCheck struct {
|
||||
Checks *[]HealthCheck `json:"checks,omitempty"`
|
||||
Commit *string `json:"commit,omitempty"`
|
||||
Message *string `json:"message,omitempty"`
|
||||
Name string `json:"name"`
|
||||
Status HealthCheckStatus `json:"status"`
|
||||
Version *string `json:"version,omitempty"`
|
||||
}
|
||||
|
||||
// HealthCheckStatus defines model for HealthCheckStatus.
|
||||
type HealthCheckStatus string
|
||||
|
||||
// List of HealthCheckStatus
|
||||
const (
|
||||
HealthCheckStatus_fail HealthCheckStatus = "fail"
|
||||
HealthCheckStatus_pass HealthCheckStatus = "pass"
|
||||
)
|
||||
|
||||
// TraceSpan defines model for TraceSpan.
|
||||
type TraceSpan string
|
||||
|
||||
// GetHealthParams defines parameters for GetHealth.
|
||||
type GetHealthParams struct {
|
||||
|
||||
// OpenTracing span context
|
||||
ZapTraceSpan *TraceSpan `json:"Zap-Trace-Span,omitempty"`
|
||||
}
|
21
internal/api/types.go
Normal file
21
internal/api/types.go
Normal file
@ -0,0 +1,21 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (e *Error) Error() string {
|
||||
if e.Message != "" && e.Err != nil {
|
||||
var b strings.Builder
|
||||
b.WriteString(e.Message)
|
||||
b.WriteString(": ")
|
||||
b.WriteString(*e.Err)
|
||||
return b.String()
|
||||
} else if e.Message != "" {
|
||||
return e.Message
|
||||
} else if e.Err != nil {
|
||||
return *e.Err
|
||||
}
|
||||
return fmt.Sprintf("<%s>", e.Code)
|
||||
}
|
15
internal/cli.go
Normal file
15
internal/cli.go
Normal file
@ -0,0 +1,15 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/influxdata/influx-cli/v2/internal/api"
|
||||
)
|
||||
|
||||
type CLI struct {
|
||||
Stdin io.Reader
|
||||
Stdout io.Writer
|
||||
Stderr io.Writer
|
||||
|
||||
TraceId *api.TraceSpan
|
||||
}
|
132
internal/config/config.go
Normal file
132
internal/config/config.go
Normal file
@ -0,0 +1,132 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/influxdata/influx-cli/v2/internal/api"
|
||||
)
|
||||
|
||||
// Config store the crendentials of influxdb host and token.
|
||||
type Config struct {
|
||||
Name string `toml:"-" json:"-"`
|
||||
Host string `toml:"url" json:"url"`
|
||||
// Token is base64 encoded sequence.
|
||||
Token string `toml:"token" json:"token"`
|
||||
Org string `toml:"org" json:"org"`
|
||||
Active bool `toml:"active,omitempty" json:"active,omitempty"`
|
||||
PreviousActive bool `toml:"previous,omitempty" json:"previous,omitempty"`
|
||||
}
|
||||
|
||||
// DefaultConfig is default config without token
|
||||
var DefaultConfig = Config{
|
||||
Name: "default",
|
||||
Host: "http://localhost:8086",
|
||||
Active: true,
|
||||
}
|
||||
|
||||
// DefaultPath computes the path where CLI configs will be stored if not overridden.
|
||||
func DefaultPath() (string, error) {
|
||||
var dir string
|
||||
// By default, store meta and data files in current users home directory
|
||||
u, err := user.Current()
|
||||
if err == nil {
|
||||
dir = u.HomeDir
|
||||
} else if home := os.Getenv("HOME"); home != "" {
|
||||
dir = home
|
||||
} else {
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
dir = wd
|
||||
}
|
||||
dir = filepath.Join(dir, ".influxdbv2", "config")
|
||||
|
||||
return dir, nil
|
||||
}
|
||||
|
||||
// Service is the service to list and write configs.
|
||||
type Service interface {
|
||||
CreateConfig(Config) (Config, error)
|
||||
DeleteConfig(name string) (Config, error)
|
||||
UpdateConfig(Config) (Config, error)
|
||||
SwitchActive(name string) (Config, error)
|
||||
ListConfigs() (Configs, error)
|
||||
}
|
||||
|
||||
// Configs is map of configs indexed by name.
|
||||
type Configs map[string]Config
|
||||
|
||||
func GetConfigsOrDefault(path string) Configs {
|
||||
r, err := os.Open(path)
|
||||
if err != nil {
|
||||
return Configs{
|
||||
DefaultConfig.Name: DefaultConfig,
|
||||
}
|
||||
}
|
||||
defer r.Close()
|
||||
|
||||
cfgs, err := NewLocalConfigService(path).ListConfigs()
|
||||
if err != nil {
|
||||
return Configs{
|
||||
DefaultConfig.Name: DefaultConfig,
|
||||
}
|
||||
}
|
||||
|
||||
return cfgs
|
||||
}
|
||||
|
||||
var badNames = map[string]bool{
|
||||
"-": false,
|
||||
"list": false,
|
||||
"update": false,
|
||||
"set": false,
|
||||
"delete": false,
|
||||
"switch": false,
|
||||
"create": false,
|
||||
}
|
||||
|
||||
func blockBadName(cfgs Configs) error {
|
||||
for n := range cfgs {
|
||||
if _, ok := badNames[n]; ok {
|
||||
return &api.Error{
|
||||
Code: api.ErrorCode_invalid,
|
||||
Message: fmt.Sprintf("%q is not a valid config name", n),
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Switch to another config.
|
||||
func (cfgs Configs) Switch(name string) error {
|
||||
if _, ok := cfgs[name]; !ok {
|
||||
return &api.Error{
|
||||
Code: api.ErrorCode_not_found,
|
||||
Message: fmt.Sprintf("config %q is not found", name),
|
||||
}
|
||||
}
|
||||
for k, v := range cfgs {
|
||||
v.PreviousActive = v.Active && (k != name)
|
||||
v.Active = k == name
|
||||
cfgs[k] = v
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cfgs Configs) Active() Config {
|
||||
for _, cfg := range cfgs {
|
||||
if cfg.Active {
|
||||
return cfg
|
||||
}
|
||||
}
|
||||
if len(cfgs) > 0 {
|
||||
for _, cfg := range cfgs {
|
||||
return cfg
|
||||
}
|
||||
}
|
||||
return DefaultConfig
|
||||
}
|
262
internal/config/local.go
Normal file
262
internal/config/local.go
Normal file
@ -0,0 +1,262 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
"github.com/influxdata/influx-cli/v2/internal/api"
|
||||
)
|
||||
|
||||
// store is the embedded store of the Config service.
|
||||
type store interface {
|
||||
parsePreviousActive() (Config, error)
|
||||
ListConfigs() (Configs, error)
|
||||
writeConfigs(cfgs Configs) error
|
||||
}
|
||||
|
||||
// localConfigsSVC can write and parse configs from a local path.
|
||||
type localConfigsSVC struct {
|
||||
store
|
||||
}
|
||||
|
||||
// NewLocalConfigService creates a new service that can write and parse configs
|
||||
// to/from a path on local disk.
|
||||
func NewLocalConfigService(path string) Service {
|
||||
return &localConfigsSVC{ioStore{Path: path}}
|
||||
}
|
||||
|
||||
// CreateConfig create new config.
|
||||
func (svc localConfigsSVC) CreateConfig(cfg Config) (Config, error) {
|
||||
if cfg.Name == "" {
|
||||
return Config{}, &api.Error{
|
||||
Code: api.ErrorCode_invalid,
|
||||
Message: "config name is empty",
|
||||
}
|
||||
}
|
||||
cfgs, err := svc.ListConfigs()
|
||||
if err != nil {
|
||||
return Config{}, err
|
||||
}
|
||||
if _, ok := cfgs[cfg.Name]; ok {
|
||||
return Config{}, &api.Error{
|
||||
Code: api.ErrorCode_conflict,
|
||||
Message: fmt.Sprintf("config %q already exists", cfg.Name),
|
||||
}
|
||||
}
|
||||
cfgs[cfg.Name] = cfg
|
||||
if cfg.Active {
|
||||
if err := cfgs.Switch(cfg.Name); err != nil {
|
||||
return Config{}, err
|
||||
}
|
||||
}
|
||||
|
||||
return cfgs[cfg.Name], svc.writeConfigs(cfgs)
|
||||
}
|
||||
|
||||
// DeleteConfig will delete a config.
|
||||
func (svc localConfigsSVC) DeleteConfig(name string) (Config, error) {
|
||||
cfgs, err := svc.ListConfigs()
|
||||
if err != nil {
|
||||
return Config{}, err
|
||||
}
|
||||
|
||||
p, ok := cfgs[name]
|
||||
if !ok {
|
||||
return Config{}, &api.Error{
|
||||
Code: api.ErrorCode_not_found,
|
||||
Message: fmt.Sprintf("config %q is not found", name),
|
||||
}
|
||||
}
|
||||
delete(cfgs, name)
|
||||
|
||||
if p.Active && len(cfgs) > 0 {
|
||||
for name, cfg := range cfgs {
|
||||
cfg.Active = true
|
||||
cfgs[name] = cfg
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return p, svc.writeConfigs(cfgs)
|
||||
}
|
||||
|
||||
// SwitchActive will active the config by name, if name is "-", active the previous one.
|
||||
func (svc localConfigsSVC) SwitchActive(name string) (Config, error) {
|
||||
var up Config
|
||||
if name == "-" {
|
||||
p0, err := svc.parsePreviousActive()
|
||||
if err != nil {
|
||||
return Config{}, err
|
||||
}
|
||||
up.Name = p0.Name
|
||||
} else {
|
||||
up.Name = name
|
||||
}
|
||||
up.Active = true
|
||||
return svc.UpdateConfig(up)
|
||||
}
|
||||
|
||||
// UpdateConfig will update the config.
|
||||
func (svc localConfigsSVC) UpdateConfig(up Config) (Config, error) {
|
||||
cfgs, err := svc.ListConfigs()
|
||||
if err != nil {
|
||||
return Config{}, err
|
||||
}
|
||||
p0, ok := cfgs[up.Name]
|
||||
if !ok {
|
||||
return Config{}, &api.Error{
|
||||
Code: api.ErrorCode_not_found,
|
||||
Message: fmt.Sprintf("config %q is not found", up.Name),
|
||||
}
|
||||
}
|
||||
if up.Token != "" {
|
||||
p0.Token = up.Token
|
||||
}
|
||||
if up.Host != "" {
|
||||
p0.Host = up.Host
|
||||
}
|
||||
if up.Org != "" {
|
||||
p0.Org = up.Org
|
||||
}
|
||||
|
||||
cfgs[up.Name] = p0
|
||||
if up.Active {
|
||||
if err := cfgs.Switch(up.Name); err != nil {
|
||||
return Config{}, err
|
||||
}
|
||||
}
|
||||
|
||||
return cfgs[up.Name], svc.writeConfigs(cfgs)
|
||||
}
|
||||
|
||||
type baseRW struct {
|
||||
r io.Reader
|
||||
w io.Writer
|
||||
}
|
||||
|
||||
// ParseActiveConfig returns the active config from the reader.
|
||||
func ParseActiveConfig(r io.Reader) (Config, error) {
|
||||
return (baseRW{r: r}).parseActiveConfig(true)
|
||||
}
|
||||
|
||||
// parsePreviousActive return the previous active config from the reader
|
||||
func (s baseRW) parsePreviousActive() (Config, error) {
|
||||
return s.parseActiveConfig(false)
|
||||
}
|
||||
|
||||
// ListConfigs decodes configs from io readers
|
||||
func (s baseRW) ListConfigs() (Configs, error) {
|
||||
cfgs := make(Configs)
|
||||
_, err := toml.DecodeReader(s.r, &cfgs)
|
||||
for n, cfg := range cfgs {
|
||||
cfg.Name = n
|
||||
cfgs[n] = cfg
|
||||
}
|
||||
return cfgs, err
|
||||
}
|
||||
|
||||
func (s baseRW) writeConfigs(cfgs Configs) error {
|
||||
if err := blockBadName(cfgs); err != nil {
|
||||
return err
|
||||
}
|
||||
var b2 bytes.Buffer
|
||||
if err := toml.NewEncoder(s.w).Encode(cfgs); err != nil {
|
||||
return err
|
||||
}
|
||||
// a list cloud 2 clusters, commented out
|
||||
s.w.Write([]byte("# \n"))
|
||||
cfgs = map[string]Config{
|
||||
"us-central": {Host: "https://us-central1-1.gcp.cloud2.influxdata.com", Token: "XXX"},
|
||||
"us-west": {Host: "https://us-west-2-1.aws.cloud2.influxdata.com", Token: "XXX"},
|
||||
"eu-central": {Host: "https://eu-central-1-1.aws.cloud2.influxdata.com", Token: "XXX"},
|
||||
}
|
||||
|
||||
if err := toml.NewEncoder(&b2).Encode(cfgs); err != nil {
|
||||
return err
|
||||
}
|
||||
reader := bufio.NewReader(&b2)
|
||||
for {
|
||||
line, _, err := reader.ReadLine()
|
||||
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
s.w.Write([]byte("# " + string(line) + "\n"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s baseRW) parseActiveConfig(currentOrPrevious bool) (Config, error) {
|
||||
previousText := ""
|
||||
if !currentOrPrevious {
|
||||
previousText = "previous "
|
||||
}
|
||||
cfgs, err := s.ListConfigs()
|
||||
if err != nil {
|
||||
return DefaultConfig, err
|
||||
}
|
||||
var activated Config
|
||||
var hasActive bool
|
||||
for _, cfg := range cfgs {
|
||||
check := cfg.Active
|
||||
if !currentOrPrevious {
|
||||
check = cfg.PreviousActive
|
||||
}
|
||||
if check && !hasActive {
|
||||
activated = cfg
|
||||
hasActive = true
|
||||
} else if check {
|
||||
return DefaultConfig, &api.Error{
|
||||
Code: api.ErrorCode_conflict,
|
||||
Message: fmt.Sprintf("more than one %s activated configs found", previousText),
|
||||
}
|
||||
}
|
||||
}
|
||||
if hasActive {
|
||||
return activated, nil
|
||||
}
|
||||
return DefaultConfig, &api.Error{
|
||||
Code: api.ErrorCode_not_found,
|
||||
Message: fmt.Sprintf("%s activated config is not found", previousText),
|
||||
}
|
||||
}
|
||||
|
||||
type ioStore struct {
|
||||
Path string
|
||||
}
|
||||
|
||||
// ListConfigs from the local path.
|
||||
func (s ioStore) ListConfigs() (Configs, error) {
|
||||
r, err := os.Open(s.Path)
|
||||
if err != nil {
|
||||
return make(Configs), nil
|
||||
}
|
||||
return (baseRW{r: r}).ListConfigs()
|
||||
}
|
||||
|
||||
// parsePreviousActive from the local path.
|
||||
func (s ioStore) parsePreviousActive() (Config, error) {
|
||||
r, err := os.Open(s.Path)
|
||||
if err != nil {
|
||||
return Config{}, nil
|
||||
}
|
||||
return (baseRW{r: r}).parsePreviousActive()
|
||||
}
|
||||
|
||||
// writeConfigs to the path.
|
||||
func (s ioStore) writeConfigs(cfgs Configs) error {
|
||||
if err := os.MkdirAll(filepath.Dir(s.Path), os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
var b1 bytes.Buffer
|
||||
if err := (baseRW{w: &b1}).writeConfigs(cfgs); err != nil {
|
||||
return err
|
||||
}
|
||||
return ioutil.WriteFile(s.Path, b1.Bytes(), 0600)
|
||||
}
|
43
internal/ping.go
Normal file
43
internal/ping.go
Normal file
@ -0,0 +1,43 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/influxdata/influx-cli/v2/internal/api"
|
||||
)
|
||||
|
||||
type Client interface {
|
||||
GetHealthWithResponse(context.Context, *api.GetHealthParams, ...api.RequestEditorFn) (*api.GetHealthResponse, error)
|
||||
}
|
||||
|
||||
// Ping checks the health of a remote InfluxDB instance.
|
||||
func (c *CLI) Ping(ctx context.Context, client Client) error {
|
||||
resp, err := client.GetHealthWithResponse(ctx, &api.GetHealthParams{ZapTraceSpan: c.TraceId})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to make health check request: %w", err)
|
||||
}
|
||||
|
||||
var failureMessage string
|
||||
if resp.JSON503 != nil {
|
||||
if resp.JSON503.Message != nil {
|
||||
failureMessage = *resp.JSON503.Message
|
||||
} else {
|
||||
failureMessage = "status 503"
|
||||
}
|
||||
} else if resp.JSONDefault != nil {
|
||||
failureMessage = resp.JSONDefault.Error()
|
||||
} else if resp.JSON200.Status != api.HealthCheckStatus_pass {
|
||||
if resp.JSON200.Message != nil {
|
||||
failureMessage = *resp.JSON200.Message
|
||||
} else {
|
||||
failureMessage = fmt.Sprintf("check %s failed", resp.JSON200.Name)
|
||||
}
|
||||
}
|
||||
|
||||
if failureMessage != "" {
|
||||
return fmt.Errorf("health check failed: %s", failureMessage)
|
||||
}
|
||||
c.Stdout.Write([]byte("OK\n"))
|
||||
return nil
|
||||
}
|
144
internal/ping_test.go
Normal file
144
internal/ping_test.go
Normal file
@ -0,0 +1,144 @@
|
||||
package internal_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/influxdata/influx-cli/v2/internal"
|
||||
"github.com/influxdata/influx-cli/v2/internal/api"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type testClient struct {
|
||||
GetHealthFn func(context.Context, *api.GetHealthParams, ...api.RequestEditorFn) (*api.GetHealthResponse, error)
|
||||
}
|
||||
|
||||
func (tc *testClient) GetHealthWithResponse(ctx context.Context, p *api.GetHealthParams, fns ...api.RequestEditorFn) (*api.GetHealthResponse, error) {
|
||||
return tc.GetHealthFn(ctx, p, fns...)
|
||||
}
|
||||
|
||||
func Test_PingSuccessNoTracing(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
client := &testClient{
|
||||
GetHealthFn: func(_ context.Context, p *api.GetHealthParams, _ ...api.RequestEditorFn) (*api.GetHealthResponse, error) {
|
||||
require.Nil(t, p.ZapTraceSpan)
|
||||
return &api.GetHealthResponse{
|
||||
JSON200: &api.HealthCheck{Name: "test", Status: api.HealthCheckStatus_pass},
|
||||
}, nil
|
||||
},
|
||||
}
|
||||
|
||||
out := &bytes.Buffer{}
|
||||
cli := &internal.CLI{Stdout: out}
|
||||
|
||||
require.NoError(t, cli.Ping(context.Background(), client))
|
||||
require.Equal(t, "OK\n", out.String())
|
||||
}
|
||||
|
||||
func Test_PingSuccessWithTracing(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
traceId := api.TraceSpan("trace-id")
|
||||
client := &testClient{
|
||||
GetHealthFn: func(_ context.Context, p *api.GetHealthParams, _ ...api.RequestEditorFn) (*api.GetHealthResponse, error) {
|
||||
require.Equal(t, traceId, *p.ZapTraceSpan)
|
||||
return &api.GetHealthResponse{
|
||||
JSON200: &api.HealthCheck{Name: "test", Status: api.HealthCheckStatus_pass},
|
||||
}, nil
|
||||
},
|
||||
}
|
||||
|
||||
out := &bytes.Buffer{}
|
||||
cli := &internal.CLI{Stdout: out, TraceId: &traceId}
|
||||
|
||||
require.NoError(t, cli.Ping(context.Background(), client))
|
||||
require.Equal(t, "OK\n", out.String())
|
||||
}
|
||||
|
||||
func Test_PingFailedRequest(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
e := "the internet is down"
|
||||
client := &testClient{
|
||||
GetHealthFn: func(context.Context, *api.GetHealthParams, ...api.RequestEditorFn) (*api.GetHealthResponse, error) {
|
||||
return nil, errors.New(e)
|
||||
},
|
||||
}
|
||||
|
||||
out := &bytes.Buffer{}
|
||||
cli := &internal.CLI{Stdout: out}
|
||||
|
||||
err := cli.Ping(context.Background(), client)
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), e)
|
||||
require.Empty(t, out.String())
|
||||
}
|
||||
|
||||
func Test_PingFailedStatus(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
e := "I broke"
|
||||
client := &testClient{
|
||||
GetHealthFn: func(context.Context, *api.GetHealthParams, ...api.RequestEditorFn) (*api.GetHealthResponse, error) {
|
||||
return &api.GetHealthResponse{
|
||||
JSON503: &api.HealthCheck{Name: "test", Status: api.HealthCheckStatus_fail, Message: &e},
|
||||
}, nil
|
||||
},
|
||||
}
|
||||
|
||||
out := &bytes.Buffer{}
|
||||
cli := &internal.CLI{Stdout: out}
|
||||
|
||||
err := cli.Ping(context.Background(), client)
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), e)
|
||||
require.Empty(t, out.String())
|
||||
}
|
||||
|
||||
func Test_PingFailedUnhandledError(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
e := "something went boom"
|
||||
client := &testClient{
|
||||
GetHealthFn: func(context.Context, *api.GetHealthParams, ...api.RequestEditorFn) (*api.GetHealthResponse, error) {
|
||||
return &api.GetHealthResponse{
|
||||
JSONDefault: &api.Error{
|
||||
Code: api.ErrorCode_internal_error,
|
||||
Message: e,
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
}
|
||||
|
||||
out := &bytes.Buffer{}
|
||||
cli := &internal.CLI{Stdout: out}
|
||||
|
||||
err := cli.Ping(context.Background(), client)
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), e)
|
||||
require.Empty(t, out.String())
|
||||
}
|
||||
|
||||
func Test_PingFailedCheck(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
e := "oops, forgot to set the status code"
|
||||
client := &testClient{
|
||||
GetHealthFn: func(_ context.Context, p *api.GetHealthParams, _ ...api.RequestEditorFn) (*api.GetHealthResponse, error) {
|
||||
return &api.GetHealthResponse{
|
||||
JSON200: &api.HealthCheck{Name: "test", Status: api.HealthCheckStatus_fail, Message: &e},
|
||||
}, nil
|
||||
},
|
||||
}
|
||||
|
||||
out := &bytes.Buffer{}
|
||||
cli := &internal.CLI{Stdout: out}
|
||||
|
||||
err := cli.Ping(context.Background(), client)
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), e)
|
||||
require.Empty(t, out.String())
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user