feat: port influx config
from influxdb
(#90)
This commit is contained in:
parent
6a9f17d100
commit
99d573bdaa
256
cmd/influx/config.go
Normal file
256
cmd/influx/config.go
Normal file
@ -0,0 +1,256 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/influxdata/influx-cli/v2/internal/cmd/config"
|
||||
iconfig "github.com/influxdata/influx-cli/v2/internal/config"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
var configPathAndPrintFlags = append([]cli.Flag{&configPathFlag}, printFlags...)
|
||||
|
||||
func newConfigCmd() *cli.Command {
|
||||
return &cli.Command{
|
||||
Name: "config",
|
||||
Usage: "Config management commands",
|
||||
ArgsUsage: "[config name]",
|
||||
Description: `
|
||||
Providing no argument to the config command will print the active configuration.
|
||||
When an argument is provided, the active config will be switched to the config with
|
||||
a name matching that of the argument provided.
|
||||
|
||||
Examples:
|
||||
# show active config
|
||||
influx config
|
||||
|
||||
# set active config to previously active config
|
||||
influx config -
|
||||
|
||||
# set active config
|
||||
influx config $CONFIG_NAME
|
||||
|
||||
The influx config command displays the active InfluxDB connection configuration and
|
||||
manages multiple connection configurations stored, by default, in ~/.influxdbv2/configs.
|
||||
Each connection includes a URL, token, associated organization, and active setting.
|
||||
InfluxDB reads the token from the active connection configuration, so you don't have
|
||||
to manually enter a token to log into InfluxDB.
|
||||
|
||||
For information about the config command, see
|
||||
https://docs.influxdata.com/influxdb/latest/reference/cli/influx/config/
|
||||
`,
|
||||
Before: withCli(),
|
||||
Flags: configPathAndPrintFlags,
|
||||
Action: func(ctx *cli.Context) error {
|
||||
prog := path.Base(os.Args[0])
|
||||
if ctx.NArg() > 1 {
|
||||
return fmt.Errorf("usage: %s config [config name]", prog)
|
||||
}
|
||||
client := config.Client{CLI: getCLI(ctx)}
|
||||
if ctx.NArg() == 1 {
|
||||
return client.SwitchActive(ctx.Args().Get(0))
|
||||
}
|
||||
return client.PrintActive()
|
||||
},
|
||||
Subcommands: []*cli.Command{
|
||||
newConfigCreateCmd(),
|
||||
newConfigDeleteCmd(),
|
||||
newConfigUpdateCmd(),
|
||||
newConfigListCmd(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func newConfigCreateCmd() *cli.Command {
|
||||
var cfg iconfig.Config
|
||||
return &cli.Command{
|
||||
Name: "create",
|
||||
Usage: "Create config",
|
||||
Description: `
|
||||
The influx config create command creates a new InfluxDB connection configuration
|
||||
and stores it in the configs file (by default, stored at ~/.influxdbv2/configs).
|
||||
|
||||
Examples:
|
||||
# create a config and set it active
|
||||
influx config create -a -n $CFG_NAME -u $HOST_URL -t $TOKEN -o $ORG_NAME
|
||||
|
||||
# create a config and without setting it active
|
||||
influx config create -n $CFG_NAME -u $HOST_URL -t $TOKEN -o $ORG_NAME
|
||||
|
||||
For information about the config command, see
|
||||
https://docs.influxdata.com/influxdb/latest/reference/cli/influx/config/
|
||||
and
|
||||
https://docs.influxdata.com/influxdb/latest/reference/cli/influx/config/create/
|
||||
`,
|
||||
Before: withCli(),
|
||||
Flags: append(
|
||||
configPathAndPrintFlags,
|
||||
&cli.StringFlag{
|
||||
Name: "config-name",
|
||||
Usage: "Name for the new config",
|
||||
Aliases: []string{"n"},
|
||||
Required: true,
|
||||
Destination: &cfg.Name,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "host-url",
|
||||
Usage: "Base URL of the InfluxDB server the new config should target",
|
||||
Aliases: []string{"u"},
|
||||
Required: true,
|
||||
Destination: &cfg.Host,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "token",
|
||||
Usage: "Auth token to use when communicating with the InfluxDB server",
|
||||
Aliases: []string{"t"},
|
||||
Required: true,
|
||||
Destination: &cfg.Token,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "org",
|
||||
Usage: "Default organization name to use in the new config",
|
||||
Aliases: []string{"o"},
|
||||
Destination: &cfg.Org,
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "active",
|
||||
Usage: "Set the new config as active",
|
||||
Aliases: []string{"a"},
|
||||
Destination: &cfg.Active,
|
||||
},
|
||||
),
|
||||
Action: func(ctx *cli.Context) error {
|
||||
client := config.Client{CLI: getCLI(ctx)}
|
||||
return client.Create(cfg)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func newConfigDeleteCmd() *cli.Command {
|
||||
return &cli.Command{
|
||||
Name: "rm",
|
||||
Aliases: []string{"delete", "remove"},
|
||||
Usage: "Delete config",
|
||||
ArgsUsage: "[cfg_name]...",
|
||||
Description: `
|
||||
The influx config delete command deletes an InfluxDB connection configuration from
|
||||
the configs file (by default, stored at ~/.influxdbv2/configs).
|
||||
|
||||
Examples:
|
||||
# delete a config
|
||||
influx config rm $CFG_NAME
|
||||
|
||||
# delete multiple configs
|
||||
influx config rm $CFG_NAME_1 $CFG_NAME_2
|
||||
|
||||
For information about the config command, see
|
||||
https://docs.influxdata.com/influxdb/latest/reference/cli/influx/config/
|
||||
and
|
||||
https://docs.influxdata.com/influxdb/latest/reference/cli/influx/config/rm/
|
||||
`,
|
||||
Before: withCli(),
|
||||
Flags: append([]cli.Flag{&configPathFlag}, printFlags...),
|
||||
Action: func(ctx *cli.Context) error {
|
||||
client := config.Client{CLI: getCLI(ctx)}
|
||||
return client.Delete(ctx.Args().Slice())
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func newConfigUpdateCmd() *cli.Command {
|
||||
var cfg iconfig.Config
|
||||
return &cli.Command{
|
||||
Name: "set",
|
||||
Aliases: []string{"update"},
|
||||
Usage: "Update config",
|
||||
Description: `
|
||||
The influx config set command updates information in an InfluxDB connection
|
||||
configuration in the configs file (by default, stored at ~/.influxdbv2/configs).
|
||||
|
||||
Examples:
|
||||
# update a config and set active
|
||||
influx config set -a -n $CFG_NAME -u $HOST_URL -t $TOKEN -o $ORG_NAME
|
||||
|
||||
# update a config and do not set to active
|
||||
influx config set -n $CFG_NAME -u $HOST_URL -t $TOKEN -o $ORG_NAME
|
||||
|
||||
For information about the config command, see
|
||||
https://docs.influxdata.com/influxdb/latest/reference/cli/influx/config/
|
||||
and
|
||||
https://docs.influxdata.com/influxdb/latest/reference/cli/influx/config/set/
|
||||
`,
|
||||
Before: withCli(),
|
||||
Flags: append(
|
||||
configPathAndPrintFlags,
|
||||
&cli.StringFlag{
|
||||
Name: "config-name",
|
||||
Usage: "Name of the config to update",
|
||||
Aliases: []string{"n"},
|
||||
Required: true,
|
||||
Destination: &cfg.Name,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "host-url",
|
||||
Usage: "New URL to set on the config",
|
||||
Aliases: []string{"u"},
|
||||
Destination: &cfg.Host,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "token",
|
||||
Usage: "New auth token to set on the config",
|
||||
Aliases: []string{"t"},
|
||||
Destination: &cfg.Token,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "org",
|
||||
Usage: "New default organization to set on the config",
|
||||
Aliases: []string{"o"},
|
||||
Destination: &cfg.Org,
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "active",
|
||||
Usage: "Set the config as active",
|
||||
Aliases: []string{"a"},
|
||||
Destination: &cfg.Active,
|
||||
},
|
||||
),
|
||||
Action: func(ctx *cli.Context) error {
|
||||
client := config.Client{CLI: getCLI(ctx)}
|
||||
return client.Update(cfg)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func newConfigListCmd() *cli.Command {
|
||||
return &cli.Command{
|
||||
Name: "ls",
|
||||
Aliases: []string{"list"},
|
||||
Usage: "List configs",
|
||||
Description: `
|
||||
The influx config ls command lists all InfluxDB connection configurations
|
||||
in the configs file (by default, stored at ~/.influxdbv2/configs). Each
|
||||
connection configuration includes a URL, authentication token, and active
|
||||
setting. An asterisk (*) indicates the active configuration.
|
||||
|
||||
Examples:
|
||||
# list configs
|
||||
influx config ls
|
||||
|
||||
# list configs with long alias
|
||||
influx config list
|
||||
|
||||
For information about the config command, see
|
||||
https://docs.influxdata.com/influxdb/latest/reference/cli/influx/config/
|
||||
and
|
||||
https://docs.influxdata.com/influxdb/latest/reference/cli/influx/config/list/
|
||||
`,
|
||||
Before: withCli(),
|
||||
Flags: configPathAndPrintFlags,
|
||||
Action: func(ctx *cli.Context) error {
|
||||
client := config.Client{CLI: getCLI(ctx)}
|
||||
return client.List()
|
||||
},
|
||||
}
|
||||
}
|
@ -35,15 +35,15 @@ func init() {
|
||||
}
|
||||
|
||||
var (
|
||||
tokenFlag = "token"
|
||||
hostFlag = "host"
|
||||
skipVerifyFlag = "skip-verify"
|
||||
traceIdFlag = "trace-debug-id"
|
||||
configPathFlag = "configs-path"
|
||||
configNameFlag = "active-config"
|
||||
httpDebugFlag = "http-debug"
|
||||
printJsonFlag = "json"
|
||||
hideHeadersFlag = "hide-headers"
|
||||
tokenFlagName = "token"
|
||||
hostFlagName = "host"
|
||||
skipVerifyFlagName = "skip-verify"
|
||||
traceIdFlagName = "trace-debug-id"
|
||||
configPathFlagName = "configs-path"
|
||||
configNameFlagName = "active-config"
|
||||
httpDebugFlagName = "http-debug"
|
||||
printJsonFlagName = "json"
|
||||
hideHeadersFlagName = "hide-headers"
|
||||
)
|
||||
|
||||
// NOTE: urfave/cli has dedicated support for global flags, but it only parses those flags
|
||||
@ -53,35 +53,37 @@ var (
|
||||
//
|
||||
// We replicate the pattern from the old CLI so existing scripts and docs stay valid.
|
||||
|
||||
// Flags used by all CLI commands.
|
||||
var configPathFlag = cli.PathFlag{
|
||||
Name: configPathFlagName,
|
||||
Usage: "Path to the influx CLI configurations",
|
||||
EnvVars: []string{"INFLUX_CLI_CONFIGS_PATH"},
|
||||
}
|
||||
|
||||
// Flags used by all CLI commands that make HTTP requests.
|
||||
var coreFlags = []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: hostFlag,
|
||||
Name: hostFlagName,
|
||||
Usage: "HTTP address of InfluxDB",
|
||||
EnvVars: []string{"INFLUX_HOST"},
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: skipVerifyFlag,
|
||||
Name: skipVerifyFlagName,
|
||||
Usage: "Skip TLS certificate chain and host name verification",
|
||||
},
|
||||
&cli.PathFlag{
|
||||
Name: configPathFlag,
|
||||
Usage: "Path to the influx CLI configurations",
|
||||
EnvVars: []string{"INFLUX_CLI_CONFIGS_PATH"},
|
||||
},
|
||||
&configPathFlag,
|
||||
&cli.StringFlag{
|
||||
Name: configNameFlag,
|
||||
Name: configNameFlagName,
|
||||
Usage: "Config name to use for command",
|
||||
Aliases: []string{"c"},
|
||||
EnvVars: []string{"INFLUX_ACTIVE_CONFIG"},
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: traceIdFlag,
|
||||
Name: traceIdFlagName,
|
||||
Hidden: true,
|
||||
EnvVars: []string{"INFLUX_TRACE_DEBUG_ID"},
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: httpDebugFlag,
|
||||
Name: httpDebugFlagName,
|
||||
Hidden: true,
|
||||
},
|
||||
}
|
||||
@ -89,12 +91,12 @@ var coreFlags = []cli.Flag{
|
||||
// Flags used by commands that display API resources to the user.
|
||||
var printFlags = []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: printJsonFlag,
|
||||
Name: printJsonFlagName,
|
||||
Usage: "Output data as JSON",
|
||||
EnvVars: []string{"INFLUX_OUTPUT_JSON"},
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: hideHeadersFlag,
|
||||
Name: hideHeadersFlagName,
|
||||
Usage: "Hide the table headers in output data",
|
||||
EnvVars: []string{"INFLUX_HIDE_HEADERS"},
|
||||
},
|
||||
@ -102,7 +104,7 @@ var printFlags = []cli.Flag{
|
||||
|
||||
// Flag used by commands that hit an authenticated API.
|
||||
var commonTokenFlag = cli.StringFlag{
|
||||
Name: tokenFlag,
|
||||
Name: tokenFlagName,
|
||||
Usage: "Authentication token",
|
||||
Aliases: []string{"t"},
|
||||
EnvVars: []string{"INFLUX_TOKEN"},
|
||||
@ -115,7 +117,7 @@ var commonFlags = append(commonFlagsNoToken, &commonTokenFlag)
|
||||
// newCli builds a CLI core that reads from stdin, writes to stdout/stderr, manages a local config store,
|
||||
// and optionally tracks a trace ID specified over the CLI.
|
||||
func newCli(ctx *cli.Context) (cmd.CLI, error) {
|
||||
configPath := ctx.String(configPathFlag)
|
||||
configPath := ctx.String(configPathFlagName)
|
||||
var err error
|
||||
if configPath == "" {
|
||||
configPath, err = config.DefaultPath()
|
||||
@ -125,8 +127,8 @@ func newCli(ctx *cli.Context) (cmd.CLI, error) {
|
||||
}
|
||||
configSvc := config.NewLocalConfigService(configPath)
|
||||
var activeConfig config.Config
|
||||
if ctx.IsSet(configNameFlag) {
|
||||
if activeConfig, err = configSvc.SwitchActive(ctx.String(configNameFlag)); err != nil {
|
||||
if ctx.IsSet(configNameFlagName) {
|
||||
if activeConfig, err = configSvc.SwitchActive(ctx.String(configNameFlagName)); err != nil {
|
||||
return cmd.CLI{}, err
|
||||
}
|
||||
} else if activeConfig, err = configSvc.Active(); err != nil {
|
||||
@ -135,8 +137,8 @@ func newCli(ctx *cli.Context) (cmd.CLI, error) {
|
||||
|
||||
return cmd.CLI{
|
||||
StdIO: stdio.TerminalStdio,
|
||||
PrintAsJSON: ctx.Bool(printJsonFlag),
|
||||
HideTableHeaders: ctx.Bool(hideHeadersFlag),
|
||||
PrintAsJSON: ctx.Bool(printJsonFlagName),
|
||||
HideTableHeaders: ctx.Bool(hideHeadersFlagName),
|
||||
ActiveConfig: activeConfig,
|
||||
ConfigService: configSvc,
|
||||
}, nil
|
||||
@ -149,11 +151,11 @@ func newApiClient(ctx *cli.Context, configSvc config.Service, injectToken bool)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ctx.IsSet(tokenFlag) {
|
||||
cfg.Token = ctx.String(tokenFlag)
|
||||
if ctx.IsSet(tokenFlagName) {
|
||||
cfg.Token = ctx.String(tokenFlagName)
|
||||
}
|
||||
if ctx.IsSet(hostFlag) {
|
||||
cfg.Host = ctx.String(hostFlag)
|
||||
if ctx.IsSet(hostFlagName) {
|
||||
cfg.Host = ctx.String(hostFlagName)
|
||||
}
|
||||
|
||||
parsedHost, err := url.Parse(cfg.Host)
|
||||
@ -162,7 +164,7 @@ func newApiClient(ctx *cli.Context, configSvc config.Service, injectToken bool)
|
||||
}
|
||||
|
||||
clientTransport := http.DefaultTransport.(*http.Transport)
|
||||
clientTransport.TLSClientConfig = &tls.Config{InsecureSkipVerify: ctx.Bool(skipVerifyFlag)}
|
||||
clientTransport.TLSClientConfig = &tls.Config{InsecureSkipVerify: ctx.Bool(skipVerifyFlagName)}
|
||||
|
||||
apiConfig := api.NewConfiguration()
|
||||
apiConfig.Host = parsedHost.Host
|
||||
@ -172,16 +174,16 @@ func newApiClient(ctx *cli.Context, configSvc config.Service, injectToken bool)
|
||||
if injectToken {
|
||||
apiConfig.DefaultHeader["Authorization"] = fmt.Sprintf("Token %s", cfg.Token)
|
||||
}
|
||||
if ctx.IsSet(traceIdFlag) {
|
||||
if ctx.IsSet(traceIdFlagName) {
|
||||
// NOTE: This is circumventing our codegen. If the header we use for tracing ever changes,
|
||||
// we'll need to manually update the string here to match.
|
||||
//
|
||||
// The alternative is to pass the trace ID to the business logic for every CLI command, and
|
||||
// use codegen'd logic to set the header on every HTTP request. Early versions of the CLI
|
||||
// used that technique, and we found it to be error-prone and easy to forget during testing.
|
||||
apiConfig.DefaultHeader["Zap-Trace-Span"] = ctx.String(traceIdFlag)
|
||||
apiConfig.DefaultHeader["Zap-Trace-Span"] = ctx.String(traceIdFlagName)
|
||||
}
|
||||
apiConfig.Debug = ctx.Bool(httpDebugFlag)
|
||||
apiConfig.Debug = ctx.Bool(httpDebugFlagName)
|
||||
|
||||
return api.NewAPIClient(apiConfig), nil
|
||||
}
|
||||
@ -200,6 +202,7 @@ var app = cli.App{
|
||||
newCompletionCmd(),
|
||||
newBucketSchemaCmd(),
|
||||
newQueryCmd(),
|
||||
newConfigCmd(),
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -27,7 +27,7 @@ func newSetupCmd() *cli.Command {
|
||||
Destination: ¶ms.Password,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: tokenFlag,
|
||||
Name: tokenFlagName,
|
||||
Usage: "Auth token to set on the initial user",
|
||||
Aliases: []string{"t"},
|
||||
EnvVars: []string{"INFLUX_TOKEN"},
|
||||
|
150
internal/cmd/config/config.go
Normal file
150
internal/cmd/config/config.go
Normal file
@ -0,0 +1,150 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
||||
"github.com/influxdata/influx-cli/v2/internal/api"
|
||||
"github.com/influxdata/influx-cli/v2/internal/cmd"
|
||||
"github.com/influxdata/influx-cli/v2/internal/config"
|
||||
)
|
||||
|
||||
var ErrInvalidHostUrlScheme = errors.New("a scheme of http or https must be provided for host url")
|
||||
|
||||
type Client struct {
|
||||
cmd.CLI
|
||||
}
|
||||
|
||||
func (c Client) SwitchActive(name string) error {
|
||||
cfg, err := c.ConfigService.SwitchActive(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.printConfigs(configPrintOpts{config: &cfg})
|
||||
}
|
||||
|
||||
func (c Client) PrintActive() error {
|
||||
active, err := c.CLI.ConfigService.Active()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.printConfigs(configPrintOpts{config: &active})
|
||||
}
|
||||
|
||||
func (c Client) Create(cfg config.Config) error {
|
||||
name := cfg.Name
|
||||
validated, err := validateHostUrl(cfg.Host)
|
||||
if err != nil {
|
||||
return fmt.Errorf("host URL %q is invalid: %w", cfg.Host, err)
|
||||
}
|
||||
cfg.Host = validated
|
||||
|
||||
cfg, err = c.ConfigService.CreateConfig(cfg)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create config %q: %w", name, err)
|
||||
}
|
||||
return c.printConfigs(configPrintOpts{config: &cfg})
|
||||
}
|
||||
|
||||
func (c Client) Delete(names []string) error {
|
||||
deleted := make(config.Configs)
|
||||
for _, name := range names {
|
||||
if name == "" {
|
||||
continue
|
||||
}
|
||||
cfg, err := c.ConfigService.DeleteConfig(name)
|
||||
if apiErr, ok := err.(*api.Error); ok && apiErr.Code == api.ERRORCODE_NOT_FOUND {
|
||||
continue
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
deleted[name] = cfg
|
||||
}
|
||||
return c.printConfigs(configPrintOpts{configs: deleted, deleted: true})
|
||||
}
|
||||
|
||||
func (c Client) Update(cfg config.Config) error {
|
||||
name := cfg.Name
|
||||
if cfg.Host != "" {
|
||||
validated, err := validateHostUrl(cfg.Host)
|
||||
if err != nil {
|
||||
return fmt.Errorf("host URL %q is invalid: %w", cfg.Host, err)
|
||||
}
|
||||
cfg.Host = validated
|
||||
}
|
||||
|
||||
cfg, err := c.ConfigService.UpdateConfig(cfg)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to update config %q: %w", name, err)
|
||||
}
|
||||
return c.printConfigs(configPrintOpts{config: &cfg})
|
||||
}
|
||||
|
||||
func (c Client) List() error {
|
||||
cfgs, err := c.ConfigService.ListConfigs()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.printConfigs(configPrintOpts{configs: cfgs})
|
||||
}
|
||||
|
||||
type configPrintOpts struct {
|
||||
deleted bool
|
||||
config *config.Config
|
||||
configs config.Configs
|
||||
}
|
||||
|
||||
func (c Client) printConfigs(opts configPrintOpts) error {
|
||||
if c.PrintAsJSON {
|
||||
var v interface{}
|
||||
if opts.config != nil {
|
||||
v = opts.config
|
||||
} else {
|
||||
v = opts.configs
|
||||
}
|
||||
return c.PrintJSON(v)
|
||||
}
|
||||
|
||||
headers := []string{"Active", "Name", "URL", "Org"}
|
||||
if opts.deleted {
|
||||
headers = append(headers, "Deleted")
|
||||
}
|
||||
|
||||
if opts.config != nil {
|
||||
opts.configs = config.Configs{
|
||||
opts.config.Name: *opts.config,
|
||||
}
|
||||
}
|
||||
|
||||
var rows []map[string]interface{}
|
||||
for _, c := range opts.configs {
|
||||
var active string
|
||||
if c.Active {
|
||||
active = "*"
|
||||
}
|
||||
row := map[string]interface{}{
|
||||
"Active": active,
|
||||
"Name": c.Name,
|
||||
"URL": c.Host,
|
||||
"Org": c.Org,
|
||||
}
|
||||
if opts.deleted {
|
||||
row["Deleted"] = true
|
||||
}
|
||||
rows = append(rows, row)
|
||||
}
|
||||
|
||||
return c.PrintTable(headers, rows...)
|
||||
}
|
||||
|
||||
func validateHostUrl(hostUrl string) (string, error) {
|
||||
u, err := url.Parse(hostUrl)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if u.Scheme != "http" && u.Scheme != "https" {
|
||||
return "", ErrInvalidHostUrlScheme
|
||||
}
|
||||
return u.String(), nil
|
||||
}
|
247
internal/cmd/config/config_test.go
Normal file
247
internal/cmd/config/config_test.go
Normal file
@ -0,0 +1,247 @@
|
||||
package config_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/influxdata/influx-cli/v2/internal/api"
|
||||
"github.com/influxdata/influx-cli/v2/internal/cmd"
|
||||
"github.com/influxdata/influx-cli/v2/internal/cmd/config"
|
||||
iconfig "github.com/influxdata/influx-cli/v2/internal/config"
|
||||
"github.com/influxdata/influx-cli/v2/internal/mock"
|
||||
"github.com/influxdata/influx-cli/v2/internal/testutils"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestClient_SwitchActive(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctrl := gomock.NewController(t)
|
||||
stdio := mock.NewMockStdIO(ctrl)
|
||||
writtenBytes := bytes.Buffer{}
|
||||
stdio.EXPECT().Write(gomock.Any()).DoAndReturn(writtenBytes.Write).AnyTimes()
|
||||
|
||||
name := "foo"
|
||||
cfg := iconfig.Config{
|
||||
Name: name,
|
||||
Active: true,
|
||||
Host: "http://localhost:8086",
|
||||
Token: "supersecret",
|
||||
Org: "me",
|
||||
}
|
||||
svc := mock.NewMockConfigService(ctrl)
|
||||
svc.EXPECT().SwitchActive(gomock.Eq(name)).Return(cfg, nil)
|
||||
|
||||
cli := config.Client{CLI: cmd.CLI{ConfigService: svc, StdIO: stdio}}
|
||||
require.NoError(t, cli.SwitchActive(name))
|
||||
testutils.MatchLines(t, []string{
|
||||
`Active\s+Name\s+URL\s+Org`,
|
||||
fmt.Sprintf(`\*\s+%s\s+%s\s+%s`, cfg.Name, cfg.Host, cfg.Org),
|
||||
}, strings.Split(writtenBytes.String(), "\n"))
|
||||
}
|
||||
|
||||
func TestClient_PrintActive(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctrl := gomock.NewController(t)
|
||||
stdio := mock.NewMockStdIO(ctrl)
|
||||
writtenBytes := bytes.Buffer{}
|
||||
stdio.EXPECT().Write(gomock.Any()).DoAndReturn(writtenBytes.Write).AnyTimes()
|
||||
|
||||
cfg := iconfig.Config{
|
||||
Name: "foo",
|
||||
Active: true,
|
||||
Host: "http://localhost:8086",
|
||||
Token: "supersecret",
|
||||
Org: "me",
|
||||
}
|
||||
svc := mock.NewMockConfigService(ctrl)
|
||||
svc.EXPECT().Active().Return(cfg, nil)
|
||||
|
||||
cli := config.Client{CLI: cmd.CLI{ConfigService: svc, StdIO: stdio}}
|
||||
require.NoError(t, cli.PrintActive())
|
||||
testutils.MatchLines(t, []string{
|
||||
`Active\s+Name\s+URL\s+Org`,
|
||||
fmt.Sprintf(`\*\s+%s\s+%s\s+%s`, cfg.Name, cfg.Host, cfg.Org),
|
||||
}, strings.Split(writtenBytes.String(), "\n"))
|
||||
}
|
||||
|
||||
func TestClient_Create(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctrl := gomock.NewController(t)
|
||||
stdio := mock.NewMockStdIO(ctrl)
|
||||
writtenBytes := bytes.Buffer{}
|
||||
stdio.EXPECT().Write(gomock.Any()).DoAndReturn(writtenBytes.Write).AnyTimes()
|
||||
|
||||
cfg := iconfig.Config{
|
||||
Name: "foo",
|
||||
Active: true,
|
||||
Host: "http://localhost:8086",
|
||||
Token: "supersecret",
|
||||
Org: "me",
|
||||
}
|
||||
svc := mock.NewMockConfigService(ctrl)
|
||||
svc.EXPECT().CreateConfig(cfg).Return(cfg, nil)
|
||||
|
||||
cli := config.Client{CLI: cmd.CLI{ConfigService: svc, StdIO: stdio}}
|
||||
require.NoError(t, cli.Create(cfg))
|
||||
testutils.MatchLines(t, []string{
|
||||
`Active\s+Name\s+URL\s+Org`,
|
||||
fmt.Sprintf(`\*\s+%s\s+%s\s+%s`, cfg.Name, cfg.Host, cfg.Org),
|
||||
}, strings.Split(writtenBytes.String(), "\n"))
|
||||
}
|
||||
|
||||
func TestClient_Delete(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
in []string
|
||||
registerExpectations func(service *mock.MockConfigService)
|
||||
out []string
|
||||
}{
|
||||
{
|
||||
name: "empty",
|
||||
},
|
||||
{
|
||||
name: "one",
|
||||
in: []string{"foo"},
|
||||
registerExpectations: func(svc *mock.MockConfigService) {
|
||||
svc.EXPECT().DeleteConfig(gomock.Eq("foo")).
|
||||
Return(iconfig.Config{Name: "foo", Host: "bar", Org: "baz"}, nil)
|
||||
},
|
||||
out: []string{`^\s+foo\s+bar\s+baz\s+true`},
|
||||
},
|
||||
{
|
||||
name: "many",
|
||||
in: []string{"foo", "qux", "wibble"},
|
||||
registerExpectations: func(svc *mock.MockConfigService) {
|
||||
svc.EXPECT().DeleteConfig(gomock.Eq("foo")).
|
||||
Return(iconfig.Config{Name: "foo", Host: "bar", Org: "baz"}, nil)
|
||||
svc.EXPECT().DeleteConfig(gomock.Eq("qux")).
|
||||
Return(iconfig.Config{}, &api.Error{Code: api.ERRORCODE_NOT_FOUND})
|
||||
svc.EXPECT().DeleteConfig(gomock.Eq("wibble")).
|
||||
Return(iconfig.Config{Name: "wibble", Host: "bar", Active: true}, nil)
|
||||
},
|
||||
out: []string{
|
||||
`^\s+foo\s+bar\s+baz\s+true`,
|
||||
`\*\s+wibble\s+bar\s+true`,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctrl := gomock.NewController(t)
|
||||
stdio := mock.NewMockStdIO(ctrl)
|
||||
writtenBytes := bytes.Buffer{}
|
||||
stdio.EXPECT().Write(gomock.Any()).DoAndReturn(writtenBytes.Write).AnyTimes()
|
||||
|
||||
svc := mock.NewMockConfigService(ctrl)
|
||||
if tc.registerExpectations != nil {
|
||||
tc.registerExpectations(svc)
|
||||
}
|
||||
|
||||
cli := config.Client{CLI: cmd.CLI{ConfigService: svc, StdIO: stdio}}
|
||||
require.NoError(t, cli.Delete(tc.in))
|
||||
testutils.MatchLines(t,
|
||||
append([]string{`Active\s+Name\s+URL\s+Org\s+Deleted`}, tc.out...),
|
||||
strings.Split(writtenBytes.String(), "\n"))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_Update(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctrl := gomock.NewController(t)
|
||||
stdio := mock.NewMockStdIO(ctrl)
|
||||
writtenBytes := bytes.Buffer{}
|
||||
stdio.EXPECT().Write(gomock.Any()).DoAndReturn(writtenBytes.Write).AnyTimes()
|
||||
|
||||
updates := iconfig.Config{
|
||||
Name: "foo",
|
||||
Active: true,
|
||||
Token: "doublesecret",
|
||||
}
|
||||
cfg := iconfig.Config{
|
||||
Name: updates.Name,
|
||||
Active: updates.Active,
|
||||
Host: "http://localhost:8086",
|
||||
Token: updates.Token,
|
||||
Org: "me",
|
||||
}
|
||||
svc := mock.NewMockConfigService(ctrl)
|
||||
svc.EXPECT().UpdateConfig(updates).Return(cfg, nil)
|
||||
|
||||
cli := config.Client{CLI: cmd.CLI{ConfigService: svc, StdIO: stdio}}
|
||||
require.NoError(t, cli.Update(updates))
|
||||
testutils.MatchLines(t, []string{
|
||||
`Active\s+Name\s+URL\s+Org`,
|
||||
fmt.Sprintf(`\*\s+%s\s+%s\s+%s`, cfg.Name, cfg.Host, cfg.Org),
|
||||
}, strings.Split(writtenBytes.String(), "\n"))
|
||||
}
|
||||
|
||||
func TestClient_List(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
cfgs iconfig.Configs
|
||||
expected []string
|
||||
}{
|
||||
{
|
||||
name: "empty",
|
||||
},
|
||||
{
|
||||
name: "one",
|
||||
cfgs: iconfig.Configs{
|
||||
"foo": iconfig.Config{Name: "foo", Host: "bar", Org: "baz"},
|
||||
},
|
||||
expected: []string{`\s+foo\s+bar\s+baz`},
|
||||
},
|
||||
{
|
||||
name: "many",
|
||||
cfgs: iconfig.Configs{
|
||||
"foo": iconfig.Config{Name: "foo", Host: "bar", Org: "baz"},
|
||||
"wibble": iconfig.Config{Name: "wibble", Host: "bar", Active: true},
|
||||
},
|
||||
expected: []string{
|
||||
`\s+foo\s+bar\s+baz`,
|
||||
`\*\s+wibble\s+bar`,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctrl := gomock.NewController(t)
|
||||
stdio := mock.NewMockStdIO(ctrl)
|
||||
writtenBytes := bytes.Buffer{}
|
||||
stdio.EXPECT().Write(gomock.Any()).DoAndReturn(writtenBytes.Write).AnyTimes()
|
||||
|
||||
svc := mock.NewMockConfigService(ctrl)
|
||||
svc.EXPECT().ListConfigs().Return(tc.cfgs, nil)
|
||||
|
||||
cli := config.Client{CLI: cmd.CLI{ConfigService: svc, StdIO: stdio}}
|
||||
require.NoError(t, cli.List())
|
||||
|
||||
// Can't use our usual 'MatchLines' because list output depends on map iteration,
|
||||
// so the order isn't well-defined.
|
||||
out := writtenBytes.String()
|
||||
for _, l := range append([]string{`Active\s+Name\s+URL\s+Org`}, tc.expected...) {
|
||||
require.Regexp(t, l, out)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user