| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221 | 
							- /*
 
-    Copyright 2020 Docker, Inc.
 
-    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
 
- import (
 
- 	"strings"
 
- 	flag "github.com/spf13/pflag"
 
- )
 
- 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
 
- func Track(context string, args []string, flags *flag.FlagSet) {
 
- 	wasIn := make(chan bool)
 
- 	// Fire and forget, we don't want to slow down the user waiting for DD
 
- 	// metrics endpoint to respond. We could lose some events but that's ok.
 
- 	go func() {
 
- 		defer func() {
 
- 			_ = recover()
 
- 		}()
 
- 		wasIn <- true
 
- 		command := getCommand(args, flags)
 
- 		if command != "" {
 
- 			c := NewClient()
 
- 			c.Send(Command{
 
- 				Command: command,
 
- 				Context: context,
 
- 			})
 
- 		}
 
- 	}()
 
- 	<-wasIn
 
- }
 
- func getCommand(args []string, flags *flag.FlagSet) string {
 
- 	command := ""
 
- 	strippedArgs := stripFlags(args, flags)
 
- 	if len(strippedArgs) != 0 {
 
- 		command = strippedArgs[0]
 
- 		if command == scanCommand {
 
- 			return getScanCommand(args)
 
- 		}
 
- 		for {
 
- 			if contains(managementCommands, command) {
 
- 				if sub := getSubCommand(command, strippedArgs[1:]); sub != "" {
 
- 					command += " " + sub
 
- 					strippedArgs = strippedArgs[1:]
 
- 					continue
 
- 				}
 
- 			}
 
- 			break
 
- 		}
 
- 	}
 
- 	return command
 
- }
 
- func getScanCommand(args []string) string {
 
- 	command := args[0]
 
- 	if contains(args, "--auth") {
 
- 		return command + " auth"
 
- 	}
 
- 	if contains(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 contains(val, args[0]) {
 
- 			return args[0]
 
- 		}
 
- 		return ""
 
- 	}
 
- 	if isArg(args[0]) {
 
- 		return args[0]
 
- 	}
 
- 	return ""
 
- }
 
- func contains(array []string, needle string) bool {
 
- 	for _, val := range array {
 
- 		if val == needle {
 
- 			return true
 
- 		}
 
- 	}
 
- 	return false
 
- }
 
- 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 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 != ""
 
- }
 
 
  |