mirror of
https://github.com/docker/compose.git
synced 2025-04-08 17:05:13 +02:00
Merge pull request #741 from gtardif/fix_cli_metrics
Simplify metrics gathering, separate running commands from getting help
This commit is contained in:
commit
cda66b5e90
22
cli/main.go
22
cli/main.go
@ -198,37 +198,37 @@ func main() {
|
|||||||
if err = root.ExecuteContext(ctx); err != nil {
|
if err = root.ExecuteContext(ctx); err != nil {
|
||||||
// if user canceled request, simply exit without any error message
|
// if user canceled request, simply exit without any error message
|
||||||
if errdefs.IsErrCanceled(err) || errors.Is(ctx.Err(), context.Canceled) {
|
if errdefs.IsErrCanceled(err) || errors.Is(ctx.Err(), context.Canceled) {
|
||||||
metrics.Track(ctype, os.Args[1:], root.PersistentFlags(), metrics.CanceledStatus)
|
metrics.Track(ctype, os.Args[1:], metrics.CanceledStatus)
|
||||||
os.Exit(130)
|
os.Exit(130)
|
||||||
}
|
}
|
||||||
if ctype == store.AwsContextType {
|
if ctype == store.AwsContextType {
|
||||||
exit(root, currentContext, errors.Errorf(`%q context type has been renamed. Recreate the context by running:
|
exit(currentContext, errors.Errorf(`%q context type has been renamed. Recreate the context by running:
|
||||||
$ docker context create %s <name>`, cc.Type(), store.EcsContextType), ctype)
|
$ docker context create %s <name>`, cc.Type(), store.EcsContextType), ctype)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Context should always be handled by new CLI
|
// Context should always be handled by new CLI
|
||||||
requiredCmd, _, _ := root.Find(os.Args[1:])
|
requiredCmd, _, _ := root.Find(os.Args[1:])
|
||||||
if requiredCmd != nil && isContextAgnosticCommand(requiredCmd) {
|
if requiredCmd != nil && isContextAgnosticCommand(requiredCmd) {
|
||||||
exit(root, currentContext, err, ctype)
|
exit(currentContext, err, ctype)
|
||||||
}
|
}
|
||||||
mobycli.ExecIfDefaultCtxType(ctx, root)
|
mobycli.ExecIfDefaultCtxType(ctx, root)
|
||||||
|
|
||||||
checkIfUnknownCommandExistInDefaultContext(err, currentContext, root, ctype)
|
checkIfUnknownCommandExistInDefaultContext(err, currentContext, ctype)
|
||||||
|
|
||||||
exit(root, currentContext, err, ctype)
|
exit(currentContext, err, ctype)
|
||||||
}
|
}
|
||||||
metrics.Track(ctype, os.Args[1:], root.PersistentFlags(), metrics.SuccessStatus)
|
metrics.Track(ctype, os.Args[1:], metrics.SuccessStatus)
|
||||||
}
|
}
|
||||||
|
|
||||||
func exit(root *cobra.Command, ctx string, err error, ctype string) {
|
func exit(ctx string, err error, ctype string) {
|
||||||
metrics.Track(ctype, os.Args[1:], root.PersistentFlags(), metrics.FailureStatus)
|
metrics.Track(ctype, os.Args[1:], metrics.FailureStatus)
|
||||||
|
|
||||||
if errors.Is(err, errdefs.ErrLoginRequired) {
|
if errors.Is(err, errdefs.ErrLoginRequired) {
|
||||||
fmt.Fprintln(os.Stderr, err)
|
fmt.Fprintln(os.Stderr, err)
|
||||||
os.Exit(errdefs.ExitCodeLoginRequired)
|
os.Exit(errdefs.ExitCodeLoginRequired)
|
||||||
}
|
}
|
||||||
if errors.Is(err, errdefs.ErrNotImplemented) {
|
if errors.Is(err, errdefs.ErrNotImplemented) {
|
||||||
name := metrics.GetCommand(os.Args[1:], root.PersistentFlags())
|
name := metrics.GetCommand(os.Args[1:])
|
||||||
fmt.Fprintf(os.Stderr, "Command %q not available in current context (%s)\n", name, ctx)
|
fmt.Fprintf(os.Stderr, "Command %q not available in current context (%s)\n", name, ctx)
|
||||||
|
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
@ -242,14 +242,14 @@ func fatal(err error) {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkIfUnknownCommandExistInDefaultContext(err error, currentContext string, root *cobra.Command, contextType string) {
|
func checkIfUnknownCommandExistInDefaultContext(err error, currentContext string, contextType string) {
|
||||||
submatch := unknownCommandRegexp.FindSubmatch([]byte(err.Error()))
|
submatch := unknownCommandRegexp.FindSubmatch([]byte(err.Error()))
|
||||||
if len(submatch) == 2 {
|
if len(submatch) == 2 {
|
||||||
dockerCommand := string(submatch[1])
|
dockerCommand := string(submatch[1])
|
||||||
|
|
||||||
if mobycli.IsDefaultContextCommand(dockerCommand) {
|
if mobycli.IsDefaultContextCommand(dockerCommand) {
|
||||||
fmt.Fprintf(os.Stderr, "Command %q not available in current context (%s), you can use the \"default\" context to run this command\n", dockerCommand, currentContext)
|
fmt.Fprintf(os.Stderr, "Command %q not available in current context (%s), you can use the \"default\" context to run this command\n", dockerCommand, currentContext)
|
||||||
metrics.Track(contextType, os.Args[1:], root.PersistentFlags(), metrics.FailureStatus)
|
metrics.Track(contextType, os.Args[1:], metrics.FailureStatus)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -86,7 +86,7 @@ func Exec(root *cobra.Command) {
|
|||||||
err := cmd.Run()
|
err := cmd.Run()
|
||||||
childExit <- true
|
childExit <- true
|
||||||
if err != nil {
|
if err != nil {
|
||||||
metrics.Track(store.DefaultContextType, os.Args[1:], root.PersistentFlags(), metrics.FailureStatus)
|
metrics.Track(store.DefaultContextType, os.Args[1:], metrics.FailureStatus)
|
||||||
|
|
||||||
if exiterr, ok := err.(*exec.ExitError); ok {
|
if exiterr, ok := err.(*exec.ExitError); ok {
|
||||||
os.Exit(exiterr.ExitCode())
|
os.Exit(exiterr.ExitCode())
|
||||||
@ -94,7 +94,7 @@ func Exec(root *cobra.Command) {
|
|||||||
fmt.Fprintln(os.Stderr, err)
|
fmt.Fprintln(os.Stderr, err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
metrics.Track(store.DefaultContextType, os.Args[1:], root.PersistentFlags(), metrics.SuccessStatus)
|
metrics.Track(store.DefaultContextType, os.Args[1:], metrics.SuccessStatus)
|
||||||
|
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
147
metrics/commands.go
Normal file
147
metrics/commands.go
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2020 Docker Compose CLI authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package metrics
|
||||||
|
|
||||||
|
var commandFlags = []string{
|
||||||
|
//added to catch scan details
|
||||||
|
"--version", "--login",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generated with generatecommands/main.go
|
||||||
|
var managementCommands = []string{
|
||||||
|
"ecs",
|
||||||
|
"scan",
|
||||||
|
"app",
|
||||||
|
"builder",
|
||||||
|
"imagetools",
|
||||||
|
"buildx",
|
||||||
|
"checkpoint",
|
||||||
|
"config",
|
||||||
|
"container",
|
||||||
|
"context",
|
||||||
|
"create",
|
||||||
|
"image",
|
||||||
|
"manifest",
|
||||||
|
"network",
|
||||||
|
"node",
|
||||||
|
"plugin",
|
||||||
|
"secret",
|
||||||
|
"service",
|
||||||
|
"stack",
|
||||||
|
"swarm",
|
||||||
|
"system",
|
||||||
|
"key",
|
||||||
|
"signer",
|
||||||
|
"trust",
|
||||||
|
"volume",
|
||||||
|
"login",
|
||||||
|
"logout",
|
||||||
|
"compose",
|
||||||
|
}
|
||||||
|
|
||||||
|
var commands = []string{
|
||||||
|
"bundle",
|
||||||
|
"completion",
|
||||||
|
"init",
|
||||||
|
"inspect",
|
||||||
|
"install",
|
||||||
|
"deploy",
|
||||||
|
"list",
|
||||||
|
"ls",
|
||||||
|
"merge",
|
||||||
|
"pull",
|
||||||
|
"push",
|
||||||
|
"render",
|
||||||
|
"split",
|
||||||
|
"status",
|
||||||
|
"uninstall",
|
||||||
|
"upgrade",
|
||||||
|
"validate",
|
||||||
|
"version",
|
||||||
|
"build",
|
||||||
|
"prune",
|
||||||
|
"create",
|
||||||
|
"bake",
|
||||||
|
"f",
|
||||||
|
"b",
|
||||||
|
"du",
|
||||||
|
"rm",
|
||||||
|
"stop",
|
||||||
|
"use",
|
||||||
|
"remove",
|
||||||
|
"attach",
|
||||||
|
"commit",
|
||||||
|
"cp",
|
||||||
|
"diff",
|
||||||
|
"exec",
|
||||||
|
"export",
|
||||||
|
"kill",
|
||||||
|
"logs",
|
||||||
|
"ps",
|
||||||
|
"pause",
|
||||||
|
"port",
|
||||||
|
"rename",
|
||||||
|
"restart",
|
||||||
|
"run",
|
||||||
|
"start",
|
||||||
|
"stats",
|
||||||
|
"top",
|
||||||
|
"unpause",
|
||||||
|
"update",
|
||||||
|
"wait",
|
||||||
|
"aci",
|
||||||
|
"ecs",
|
||||||
|
"show",
|
||||||
|
"history",
|
||||||
|
"import",
|
||||||
|
"load",
|
||||||
|
"images",
|
||||||
|
"rmi",
|
||||||
|
"save",
|
||||||
|
"tag",
|
||||||
|
"annotate",
|
||||||
|
"connect",
|
||||||
|
"disconnect",
|
||||||
|
"demote",
|
||||||
|
"promote",
|
||||||
|
"disable",
|
||||||
|
"enable",
|
||||||
|
"set",
|
||||||
|
"rollback",
|
||||||
|
"scale",
|
||||||
|
"up",
|
||||||
|
"down",
|
||||||
|
"services",
|
||||||
|
"ca",
|
||||||
|
"join",
|
||||||
|
"join-token",
|
||||||
|
"leave",
|
||||||
|
"unlock",
|
||||||
|
"unlock-key",
|
||||||
|
"df",
|
||||||
|
"events",
|
||||||
|
"info",
|
||||||
|
"generate",
|
||||||
|
"add",
|
||||||
|
"revoke",
|
||||||
|
"sign",
|
||||||
|
"login",
|
||||||
|
"azure",
|
||||||
|
"logout",
|
||||||
|
"search",
|
||||||
|
"convert",
|
||||||
|
}
|
106
metrics/generatecommands/main.go
Normal file
106
metrics/generatecommands/main.go
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2020 Docker Compose CLI authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/docker/compose-cli/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
var managementCommands = []string{"ecs", "scan"}
|
||||||
|
|
||||||
|
var commands = []string{}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
fmt.Println("Walking through docker help to list commands...")
|
||||||
|
getCommands()
|
||||||
|
getCommands("compose")
|
||||||
|
|
||||||
|
fmt.Printf(`
|
||||||
|
var managementCommands = []string{
|
||||||
|
"%s",
|
||||||
|
}
|
||||||
|
|
||||||
|
var commands = []string{
|
||||||
|
"%s",
|
||||||
|
}
|
||||||
|
`, strings.Join(managementCommands, "\", \n\t\""), strings.Join(commands, "\", \n\t\""))
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
mgtCommandsSection = "Management Commands:"
|
||||||
|
commandsSection = "Commands:"
|
||||||
|
aliasesSection = "Aliases:"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getCommands(execCommands ...string) {
|
||||||
|
withHelp := append(execCommands, "--help")
|
||||||
|
cmd := exec.Command("docker", withHelp...)
|
||||||
|
output, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
text := string(output)
|
||||||
|
lines := strings.Split(text, "\n")
|
||||||
|
section := ""
|
||||||
|
for _, line := range lines {
|
||||||
|
trimmedLine := strings.TrimSpace(line)
|
||||||
|
if strings.HasPrefix(trimmedLine, mgtCommandsSection) {
|
||||||
|
section = mgtCommandsSection
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(trimmedLine, commandsSection) || strings.HasPrefix(trimmedLine, "Available Commands:") {
|
||||||
|
section = commandsSection
|
||||||
|
if len(execCommands) > 0 {
|
||||||
|
command := execCommands[len(execCommands)-1]
|
||||||
|
managementCommands = append(managementCommands, command)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(trimmedLine, aliasesSection) {
|
||||||
|
section = aliasesSection
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if trimmedLine == "" {
|
||||||
|
section = ""
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
tokens := strings.Split(trimmedLine, " ")
|
||||||
|
command := strings.Replace(tokens[0], "*", "", 1)
|
||||||
|
switch section {
|
||||||
|
case mgtCommandsSection:
|
||||||
|
getCommands(append(execCommands, command)...)
|
||||||
|
case commandsSection:
|
||||||
|
if !utils.StringContains(commands, command) {
|
||||||
|
commands = append(commands, command)
|
||||||
|
}
|
||||||
|
getCommands(append(execCommands, command)...)
|
||||||
|
case aliasesSection:
|
||||||
|
aliases := strings.Split(trimmedLine, ",")
|
||||||
|
for _, alias := range aliases {
|
||||||
|
trimmedAlias := strings.TrimSpace(alias)
|
||||||
|
if !utils.StringContains(commands, trimmedAlias) {
|
||||||
|
commands = append(commands, trimmedAlias)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -17,68 +17,12 @@
|
|||||||
package metrics
|
package metrics
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
|
||||||
|
|
||||||
flag "github.com/spf13/pflag"
|
|
||||||
|
|
||||||
"github.com/docker/compose-cli/utils"
|
"github.com/docker/compose-cli/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
var managementCommands = []string{
|
|
||||||
"app",
|
|
||||||
"assemble",
|
|
||||||
"builder",
|
|
||||||
"buildx",
|
|
||||||
"ecs",
|
|
||||||
"ecs compose",
|
|
||||||
"cluster",
|
|
||||||
"compose",
|
|
||||||
"config",
|
|
||||||
"container",
|
|
||||||
"context",
|
|
||||||
// We add "context create" as a management command to be able to catch
|
|
||||||
// calls to "context create aci"
|
|
||||||
"context create",
|
|
||||||
"help",
|
|
||||||
"image",
|
|
||||||
// Adding "login" as a management command so that the system can catch
|
|
||||||
// commands like `docker login azure`
|
|
||||||
"login",
|
|
||||||
"manifest",
|
|
||||||
"network",
|
|
||||||
"node",
|
|
||||||
"plugin",
|
|
||||||
"registry",
|
|
||||||
"secret",
|
|
||||||
"service",
|
|
||||||
"stack",
|
|
||||||
"swarm",
|
|
||||||
"system",
|
|
||||||
"template",
|
|
||||||
"trust",
|
|
||||||
"volume",
|
|
||||||
}
|
|
||||||
|
|
||||||
// managementSubCommands holds a list of allowed subcommands of a management
|
|
||||||
// command. For example we want to send an event for "docker login azure" but
|
|
||||||
// we don't wat to send the name of the registry when the user does a
|
|
||||||
// "docker login my-registry", we only want to send "login"
|
|
||||||
var managementSubCommands = map[string][]string{
|
|
||||||
"login": {
|
|
||||||
"azure",
|
|
||||||
},
|
|
||||||
"context create": {
|
|
||||||
"aci",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
scanCommand = "scan"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Track sends the tracking analytics to Docker Desktop
|
// Track sends the tracking analytics to Docker Desktop
|
||||||
func Track(context string, args []string, flags *flag.FlagSet, status string) {
|
func Track(context string, args []string, status string) {
|
||||||
command := GetCommand(args, flags)
|
command := GetCommand(args)
|
||||||
if command != "" {
|
if command != "" {
|
||||||
c := NewClient()
|
c := NewClient()
|
||||||
c.Send(Command{
|
c.Send(Command{
|
||||||
@ -90,115 +34,39 @@ func Track(context string, args []string, flags *flag.FlagSet, status string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isCommand(word string) bool {
|
||||||
|
return utils.StringContains(commands, word) || isManagementCommand(word)
|
||||||
|
}
|
||||||
|
|
||||||
|
func isManagementCommand(word string) bool {
|
||||||
|
return utils.StringContains(managementCommands, word)
|
||||||
|
}
|
||||||
|
|
||||||
|
func isCommandFlag(word string) bool {
|
||||||
|
return utils.StringContains(commandFlags, word)
|
||||||
|
}
|
||||||
|
|
||||||
// GetCommand get the invoked command
|
// GetCommand get the invoked command
|
||||||
func GetCommand(args []string, flags *flag.FlagSet) string {
|
func GetCommand(args []string) string {
|
||||||
command := ""
|
result := ""
|
||||||
strippedArgs := stripFlags(args, flags)
|
onlyFlags := false
|
||||||
|
for _, arg := range args {
|
||||||
if len(strippedArgs) != 0 {
|
if arg == "--help" {
|
||||||
command = strippedArgs[0]
|
return ""
|
||||||
|
|
||||||
if command == scanCommand {
|
|
||||||
return getScanCommand(args)
|
|
||||||
}
|
}
|
||||||
|
if arg == "--" {
|
||||||
for {
|
|
||||||
if utils.StringContains(managementCommands, command) {
|
|
||||||
if sub := getSubCommand(command, strippedArgs[1:]); sub != "" {
|
|
||||||
command += " " + sub
|
|
||||||
strippedArgs = strippedArgs[1:]
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
if isCommandFlag(arg) || (!onlyFlags && isCommand(arg)) {
|
||||||
|
if result == "" {
|
||||||
return command
|
result = arg
|
||||||
}
|
} else {
|
||||||
|
result = result + " " + arg
|
||||||
func getScanCommand(args []string) string {
|
}
|
||||||
command := args[0]
|
if !isManagementCommand(arg) {
|
||||||
|
onlyFlags = true
|
||||||
if utils.StringContains(args, "--auth") {
|
|
||||||
return command + " auth"
|
|
||||||
}
|
|
||||||
|
|
||||||
if utils.StringContains(args, "--version") {
|
|
||||||
return command + " version"
|
|
||||||
}
|
|
||||||
|
|
||||||
return command
|
|
||||||
}
|
|
||||||
|
|
||||||
func getSubCommand(command string, args []string) string {
|
|
||||||
if len(args) == 0 {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
if val, ok := managementSubCommands[command]; ok {
|
|
||||||
if utils.StringContains(val, args[0]) {
|
|
||||||
return args[0]
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
if isArg(args[0]) {
|
|
||||||
return args[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func stripFlags(args []string, flags *flag.FlagSet) []string {
|
|
||||||
commands := []string{}
|
|
||||||
|
|
||||||
for len(args) > 0 {
|
|
||||||
s := args[0]
|
|
||||||
args = args[1:]
|
|
||||||
|
|
||||||
if s == "--" {
|
|
||||||
return commands
|
|
||||||
}
|
|
||||||
|
|
||||||
if flagArg(s, flags) {
|
|
||||||
if len(args) <= 1 {
|
|
||||||
return commands
|
|
||||||
}
|
}
|
||||||
args = args[1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
if isArg(s) {
|
|
||||||
commands = append(commands, s)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return result
|
||||||
return commands
|
|
||||||
}
|
|
||||||
|
|
||||||
func flagArg(s string, flags *flag.FlagSet) bool {
|
|
||||||
return strings.HasPrefix(s, "--") && !strings.Contains(s, "=") && !hasNoOptDefVal(s[2:], flags) ||
|
|
||||||
strings.HasPrefix(s, "-") && !strings.Contains(s, "=") && len(s) == 2 && !shortHasNoOptDefVal(s[1:], flags)
|
|
||||||
}
|
|
||||||
|
|
||||||
func isArg(s string) bool {
|
|
||||||
return s != "" && !strings.HasPrefix(s, "-")
|
|
||||||
}
|
|
||||||
|
|
||||||
func hasNoOptDefVal(name string, fs *flag.FlagSet) bool {
|
|
||||||
flag := fs.Lookup(name)
|
|
||||||
if flag == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return flag.NoOptDefVal != ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func shortHasNoOptDefVal(name string, fs *flag.FlagSet) bool {
|
|
||||||
flag := fs.ShorthandLookup(name[:1])
|
|
||||||
if flag == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return flag.NoOptDefVal != ""
|
|
||||||
}
|
}
|
||||||
|
@ -19,15 +19,10 @@ package metrics
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"gotest.tools/v3/assert"
|
"gotest.tools/v3/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestFlag(t *testing.T) {
|
func TestGetCommand(t *testing.T) {
|
||||||
root := &cobra.Command{}
|
|
||||||
root.PersistentFlags().BoolP("debug", "D", false, "debug")
|
|
||||||
root.PersistentFlags().String("str", "str", "str")
|
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
args []string
|
args []string
|
||||||
@ -58,16 +53,6 @@ func TestFlag(t *testing.T) {
|
|||||||
args: []string{"--debug", "--str", "str-value"},
|
args: []string{"--debug", "--str", "str-value"},
|
||||||
expected: "",
|
expected: "",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "with unknown short flag",
|
|
||||||
args: []string{"-f", "run"},
|
|
||||||
expected: "",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "with unknown long flag",
|
|
||||||
args: []string{"--unknown-flag", "run"},
|
|
||||||
expected: "",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: "management command",
|
name: "management command",
|
||||||
args: []string{"image", "ls"},
|
args: []string{"image", "ls"},
|
||||||
@ -76,7 +61,7 @@ func TestFlag(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "management command with flag",
|
name: "management command with flag",
|
||||||
args: []string{"image", "--test", "ls"},
|
args: []string{"image", "--test", "ls"},
|
||||||
expected: "image",
|
expected: "image ls",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "management subcommand with flag",
|
name: "management subcommand with flag",
|
||||||
@ -88,26 +73,36 @@ func TestFlag(t *testing.T) {
|
|||||||
args: []string{"login", "azure"},
|
args: []string{"login", "azure"},
|
||||||
expected: "login azure",
|
expected: "login azure",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "azure logout",
|
||||||
|
args: []string{"logout", "azure"},
|
||||||
|
expected: "logout azure",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "azure login with flags",
|
name: "azure login with flags",
|
||||||
args: []string{"login", "-u", "test", "azure"},
|
args: []string{"login", "-u", "test", "azure"},
|
||||||
expected: "login azure",
|
expected: "login azure",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "azure login with azure user",
|
name: "login to a registry",
|
||||||
args: []string{"login", "-u", "azure"},
|
args: []string{"login", "myregistry"},
|
||||||
expected: "login",
|
expected: "login",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "login to a registry",
|
name: "logout from a registry",
|
||||||
args: []string{"login", "registry"},
|
args: []string{"logout", "myregistry"},
|
||||||
expected: "login",
|
expected: "logout",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "context create aci",
|
name: "context create aci",
|
||||||
args: []string{"context", "create", "aci"},
|
args: []string{"context", "create", "aci"},
|
||||||
expected: "context create aci",
|
expected: "context create aci",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "context create ecs",
|
||||||
|
args: []string{"context", "create", "ecs"},
|
||||||
|
expected: "context create ecs",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "create a context from another context",
|
name: "create a context from another context",
|
||||||
args: []string{"context", "create", "test-context", "--from=default"},
|
args: []string{"context", "create", "test-context", "--from=default"},
|
||||||
@ -119,9 +114,9 @@ func TestFlag(t *testing.T) {
|
|||||||
expected: "create",
|
expected: "create",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "create a container named aci",
|
name: "start a container named aci",
|
||||||
args: []string{"create", "aci"},
|
args: []string{"start", "aci"},
|
||||||
expected: "create",
|
expected: "start",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "create a container named test-container",
|
name: "create a container named test-container",
|
||||||
@ -152,15 +147,44 @@ func TestFlag(t *testing.T) {
|
|||||||
|
|
||||||
for _, testCase := range testCases {
|
for _, testCase := range testCases {
|
||||||
t.Run(testCase.name, func(t *testing.T) {
|
t.Run(testCase.name, func(t *testing.T) {
|
||||||
result := GetCommand(testCase.args, root.PersistentFlags())
|
result := GetCommand(testCase.args)
|
||||||
|
assert.Equal(t, testCase.expected, result)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIgnoreHelpCommands(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
args []string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "help",
|
||||||
|
args: []string{"--help"},
|
||||||
|
expected: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "help on run",
|
||||||
|
args: []string{"run", "--help"},
|
||||||
|
expected: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "help on compose up",
|
||||||
|
args: []string{"compose", "up", "--help"},
|
||||||
|
expected: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, testCase := range testCases {
|
||||||
|
t.Run(testCase.name, func(t *testing.T) {
|
||||||
|
result := GetCommand(testCase.args)
|
||||||
assert.Equal(t, testCase.expected, result)
|
assert.Equal(t, testCase.expected, result)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEcs(t *testing.T) {
|
func TestEcs(t *testing.T) {
|
||||||
root := &cobra.Command{}
|
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
args []string
|
args []string
|
||||||
@ -217,23 +241,21 @@ func TestEcs(t *testing.T) {
|
|||||||
expected: "ecs compose logs",
|
expected: "ecs compose logs",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "setup",
|
name: "ecs",
|
||||||
args: []string{"ecs", "setup"},
|
args: []string{"ecs", "anything"},
|
||||||
expected: "ecs setup",
|
expected: "ecs",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, testCase := range testCases {
|
for _, testCase := range testCases {
|
||||||
t.Run(testCase.name, func(t *testing.T) {
|
t.Run(testCase.name, func(t *testing.T) {
|
||||||
result := GetCommand(testCase.args, root.PersistentFlags())
|
result := GetCommand(testCase.args)
|
||||||
assert.Equal(t, testCase.expected, result)
|
assert.Equal(t, testCase.expected, result)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestScan(t *testing.T) {
|
func TestScan(t *testing.T) {
|
||||||
root := &cobra.Command{}
|
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
args []string
|
args []string
|
||||||
@ -246,34 +268,34 @@ func TestScan(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "scan image with long flags",
|
name: "scan image with long flags",
|
||||||
args: []string{"scan", "--file", "file", "image"},
|
args: []string{"scan", "--file", "file", "myimage"},
|
||||||
expected: "scan",
|
expected: "scan",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "scan image with short flags",
|
name: "scan image with short flags",
|
||||||
args: []string{"scan", "-f", "file", "image"},
|
args: []string{"scan", "-f", "file", "myimage"},
|
||||||
expected: "scan",
|
expected: "scan",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "scan with long flag",
|
name: "scan with long flag",
|
||||||
args: []string{"scan", "--dependency-tree", "image"},
|
args: []string{"scan", "--dependency-tree", "myimage"},
|
||||||
expected: "scan",
|
expected: "scan",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "auth",
|
name: "auth",
|
||||||
args: []string{"scan", "--auth"},
|
args: []string{"scan", "--login"},
|
||||||
expected: "scan auth",
|
expected: "scan --login",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "version",
|
name: "version",
|
||||||
args: []string{"scan", "--version"},
|
args: []string{"scan", "--version"},
|
||||||
expected: "scan version",
|
expected: "scan --version",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, testCase := range testCases {
|
for _, testCase := range testCases {
|
||||||
t.Run(testCase.name, func(t *testing.T) {
|
t.Run(testCase.name, func(t *testing.T) {
|
||||||
result := GetCommand(testCase.args, root.PersistentFlags())
|
result := GetCommand(testCase.args)
|
||||||
assert.Equal(t, testCase.expected, result)
|
assert.Equal(t, testCase.expected, result)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -164,6 +164,17 @@ func TestContextMetrics(t *testing.T) {
|
|||||||
s.Start()
|
s.Start()
|
||||||
defer s.Stop()
|
defer s.Stop()
|
||||||
|
|
||||||
|
t.Run("do not send metrics on help commands", func(t *testing.T) {
|
||||||
|
s.ResetUsage()
|
||||||
|
|
||||||
|
c.RunDockerCmd("--help")
|
||||||
|
c.RunDockerCmd("ps", "--help")
|
||||||
|
c.RunDockerCmd("run", "--help")
|
||||||
|
|
||||||
|
usage := s.GetUsage()
|
||||||
|
assert.Equal(t, 0, len(usage))
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("metrics on default context", func(t *testing.T) {
|
t.Run("metrics on default context", func(t *testing.T) {
|
||||||
s.ResetUsage()
|
s.ResetUsage()
|
||||||
|
|
||||||
@ -185,7 +196,7 @@ func TestContextMetrics(t *testing.T) {
|
|||||||
c.RunDockerCmd("ps")
|
c.RunDockerCmd("ps")
|
||||||
c.RunDockerCmd("context", "use", "test-example")
|
c.RunDockerCmd("context", "use", "test-example")
|
||||||
c.RunDockerCmd("ps")
|
c.RunDockerCmd("ps")
|
||||||
c.RunDockerOrExitError("error")
|
c.RunDockerOrExitError("stop", "unknown")
|
||||||
c.RunDockerCmd("context", "use", "default")
|
c.RunDockerCmd("context", "use", "default")
|
||||||
c.RunDockerCmd("--context", "test-example", "ps")
|
c.RunDockerCmd("--context", "test-example", "ps")
|
||||||
|
|
||||||
@ -195,7 +206,7 @@ func TestContextMetrics(t *testing.T) {
|
|||||||
assert.Equal(t, `{"command":"ps","context":"moby","source":"cli","status":"success"}`, usage[1])
|
assert.Equal(t, `{"command":"ps","context":"moby","source":"cli","status":"success"}`, usage[1])
|
||||||
assert.Equal(t, `{"command":"context use","context":"moby","source":"cli","status":"success"}`, usage[2])
|
assert.Equal(t, `{"command":"context use","context":"moby","source":"cli","status":"success"}`, usage[2])
|
||||||
assert.Equal(t, `{"command":"ps","context":"example","source":"cli","status":"success"}`, usage[3])
|
assert.Equal(t, `{"command":"ps","context":"example","source":"cli","status":"success"}`, usage[3])
|
||||||
assert.Equal(t, `{"command":"error","context":"example","source":"cli","status":"failure"}`, usage[4])
|
assert.Equal(t, `{"command":"stop","context":"example","source":"cli","status":"failure"}`, usage[4])
|
||||||
assert.Equal(t, `{"command":"context use","context":"example","source":"cli","status":"success"}`, usage[5])
|
assert.Equal(t, `{"command":"context use","context":"example","source":"cli","status":"success"}`, usage[5])
|
||||||
assert.Equal(t, `{"command":"ps","context":"example","source":"cli","status":"success"}`, usage[6])
|
assert.Equal(t, `{"command":"ps","context":"example","source":"cli","status":"success"}`, usage[6])
|
||||||
})
|
})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user