Skip to content
This repository was archived by the owner on Nov 27, 2023. It is now read-only.

Commit 98291e0

Browse files
authored
Merge pull request #2223 from milas/metrics-compose-helper
metrics: move helper code from Compose
2 parents 2974b2a + 16482c0 commit 98291e0

File tree

7 files changed

+108
-26
lines changed

7 files changed

+108
-26
lines changed

cli/main.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ func main() {
256256
if err = root.ExecuteContext(ctx); err != nil {
257257
handleError(ctx, err, ctype, currentContext, cc, root)
258258
}
259-
metricsClient.Track(ctype, os.Args[1:], compose.SuccessStatus)
259+
metricsClient.Track(ctype, os.Args[1:], metrics.SuccessStatus)
260260
}
261261

262262
func customizeCliForACI(command *cobra.Command, proxy *api.ServiceProxy) {
@@ -278,7 +278,7 @@ func customizeCliForACI(command *cobra.Command, proxy *api.ServiceProxy) {
278278
func handleError(ctx context.Context, err error, ctype string, currentContext string, cc *store.DockerContext, root *cobra.Command) {
279279
// if user canceled request, simply exit without any error message
280280
if api.IsErrCanceled(err) || errors.Is(ctx.Err(), context.Canceled) {
281-
metricsClient.Track(ctype, os.Args[1:], compose.CanceledStatus)
281+
metricsClient.Track(ctype, os.Args[1:], metrics.CanceledStatus)
282282
os.Exit(130)
283283
}
284284
if ctype == store.AwsContextType {
@@ -300,20 +300,21 @@ $ docker context create %s <name>`, cc.Type(), store.EcsContextType), ctype)
300300

301301
func exit(ctx string, err error, ctype string) {
302302
if exit, ok := err.(cli.StatusError); ok {
303-
metricsClient.Track(ctype, os.Args[1:], compose.SuccessStatus)
303+
// TODO(milas): shouldn't this use the exit code to determine status?
304+
metricsClient.Track(ctype, os.Args[1:], metrics.SuccessStatus)
304305
os.Exit(exit.StatusCode)
305306
}
306307

307308
var composeErr compose.Error
308-
metricsStatus := compose.FailureStatus
309+
metricsStatus := metrics.FailureStatus
309310
exitCode := 1
310311
if errors.As(err, &composeErr) {
311312
metricsStatus = composeErr.GetMetricsFailureCategory().MetricsStatus
312313
exitCode = composeErr.GetMetricsFailureCategory().ExitCode
313314
}
314315
if strings.HasPrefix(err.Error(), "unknown shorthand flag:") || strings.HasPrefix(err.Error(), "unknown flag:") || strings.HasPrefix(err.Error(), "unknown docker command:") {
315-
metricsStatus = compose.CommandSyntaxFailure.MetricsStatus
316-
exitCode = compose.CommandSyntaxFailure.ExitCode
316+
metricsStatus = metrics.CommandSyntaxFailure.MetricsStatus
317+
exitCode = metrics.CommandSyntaxFailure.ExitCode
317318
}
318319
metricsClient.Track(ctype, os.Args[1:], metricsStatus)
319320

@@ -350,7 +351,7 @@ func checkIfUnknownCommandExistInDefaultContext(err error, currentContext string
350351

351352
if mobycli.IsDefaultContextCommand(dockerCommand) {
352353
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)
353-
metricsClient.Track(contextType, os.Args[1:], compose.FailureStatus)
354+
metricsClient.Track(contextType, os.Args[1:], metrics.FailureStatus)
354355
os.Exit(1)
355356
}
356357
}

cli/metrics/client.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,11 @@ type Client interface {
5757
// WithCliVersionFunc sets the docker cli version func
5858
// that returns the docker cli version (com.docker.cli)
5959
WithCliVersionFunc(f func() string)
60-
// Send sends the command to Docker Desktop. Note that the function doesn't
61-
// return anything, not even an error, this is because we don't really care
62-
// if the metrics were sent or not. We only fire and forget.
63-
Send(Command)
64-
// Track sends the tracking analytics to Docker Desktop
60+
// SendUsage sends the command to Docker Desktop.
61+
//
62+
// Note that metric collection is best-effort, so any errors are ignored.
63+
SendUsage(Command)
64+
// Track creates an event for a command execution and reports it.
6565
Track(context string, args []string, status string)
6666
}
6767

@@ -98,7 +98,7 @@ func (c *client) WithCliVersionFunc(f func() string) {
9898
c.cliversion.f = f
9999
}
100100

101-
func (c *client) Send(command Command) {
101+
func (c *client) SendUsage(command Command) {
102102
result := make(chan bool, 1)
103103
go func() {
104104
c.reporter.Heartbeat(command)

cli/metrics/event.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
Copyright 2020 Docker Compose CLI authors
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package metrics
18+
19+
// FailureCategory struct regrouping metrics failure status and specific exit code
20+
type FailureCategory struct {
21+
MetricsStatus string
22+
ExitCode int
23+
}
24+
25+
const (
26+
// APISource is sent for API metrics
27+
APISource = "api"
28+
// SuccessStatus command success
29+
SuccessStatus = "success"
30+
// FailureStatus command failure
31+
FailureStatus = "failure"
32+
// ComposeParseFailureStatus failure while parsing compose file
33+
ComposeParseFailureStatus = "failure-compose-parse"
34+
// FileNotFoundFailureStatus failure getting compose file
35+
FileNotFoundFailureStatus = "failure-file-not-found"
36+
// CommandSyntaxFailureStatus failure reading command
37+
CommandSyntaxFailureStatus = "failure-cmd-syntax"
38+
// BuildFailureStatus failure building imge
39+
BuildFailureStatus = "failure-build"
40+
// PullFailureStatus failure pulling imge
41+
PullFailureStatus = "failure-pull"
42+
// CanceledStatus command canceled
43+
CanceledStatus = "canceled"
44+
)
45+
46+
var (
47+
// FileNotFoundFailure failure for compose file not found
48+
FileNotFoundFailure = FailureCategory{MetricsStatus: FileNotFoundFailureStatus, ExitCode: 14}
49+
// ComposeParseFailure failure for composefile parse error
50+
ComposeParseFailure = FailureCategory{MetricsStatus: ComposeParseFailureStatus, ExitCode: 15}
51+
// CommandSyntaxFailure failure for command line syntax
52+
CommandSyntaxFailure = FailureCategory{MetricsStatus: CommandSyntaxFailureStatus, ExitCode: 16}
53+
// BuildFailure failure while building images.
54+
BuildFailure = FailureCategory{MetricsStatus: BuildFailureStatus, ExitCode: 17}
55+
// PullFailure failure while pulling image
56+
PullFailure = FailureCategory{MetricsStatus: PullFailureStatus, ExitCode: 18}
57+
)
58+
59+
// FailureCategoryFromExitCode retrieve FailureCategory based on command exit code
60+
func FailureCategoryFromExitCode(exitCode int) FailureCategory {
61+
switch exitCode {
62+
case 0:
63+
return FailureCategory{MetricsStatus: SuccessStatus, ExitCode: 0}
64+
case 14:
65+
return FileNotFoundFailure
66+
case 15:
67+
return ComposeParseFailure
68+
case 16:
69+
return CommandSyntaxFailure
70+
case 17:
71+
return BuildFailure
72+
case 18:
73+
return PullFailure
74+
case 130:
75+
return FailureCategory{MetricsStatus: CanceledStatus, ExitCode: exitCode}
76+
default:
77+
return FailureCategory{MetricsStatus: FailureStatus, ExitCode: exitCode}
78+
}
79+
}

cli/metrics/metrics.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ func (c *client) Track(context string, args []string, status string) {
3131
}
3232
command := GetCommand(args)
3333
if command != "" {
34-
c.Send(Command{
34+
c.SendUsage(Command{
3535
Command: command,
3636
Context: context,
3737
Source: c.getMetadata(CLISource, args),

cli/mobycli/exec.go

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ import (
2828
"runtime"
2929
"strings"
3030

31-
"github.com/docker/compose/v2/pkg/compose"
3231
"github.com/google/shlex"
3332
"github.com/spf13/cobra"
3433

@@ -72,7 +71,7 @@ func mustDelegateToMoby(ctxType string) bool {
7271
}
7372

7473
// Exec delegates to com.docker.cli if on moby context
75-
func Exec(root *cobra.Command) {
74+
func Exec(_ *cobra.Command) {
7675
metricsClient := metrics.NewDefaultClient()
7776
metricsClient.WithCliVersionFunc(func() string {
7877
return CliVersion()
@@ -83,10 +82,14 @@ func Exec(root *cobra.Command) {
8382
if err != nil {
8483
if exiterr, ok := err.(*exec.ExitError); ok {
8584
exitCode := exiterr.ExitCode()
86-
metricsClient.Track(store.DefaultContextType, os.Args[1:], compose.ByExitCode(exitCode).MetricsStatus)
85+
metricsClient.Track(
86+
store.DefaultContextType,
87+
os.Args[1:],
88+
metrics.FailureCategoryFromExitCode(exitCode).MetricsStatus,
89+
)
8790
os.Exit(exitCode)
8891
}
89-
metricsClient.Track(store.DefaultContextType, os.Args[1:], compose.FailureStatus)
92+
metricsClient.Track(store.DefaultContextType, os.Args[1:], metrics.FailureStatus)
9093
fmt.Fprintln(os.Stderr, err)
9194
os.Exit(1)
9295
}
@@ -95,7 +98,7 @@ func Exec(root *cobra.Command) {
9598
if command == "login" && !metrics.HasQuietFlag(commandArgs) {
9699
displayPATSuggestMsg(commandArgs)
97100
}
98-
metricsClient.Track(store.DefaultContextType, os.Args[1:], compose.SuccessStatus)
101+
metricsClient.Track(store.DefaultContextType, os.Args[1:], metrics.SuccessStatus)
99102

100103
os.Exit(0)
101104
}

cli/server/metrics.go

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ package server
1919
import (
2020
"context"
2121

22-
"github.com/docker/compose/v2/pkg/compose"
2322
"google.golang.org/grpc"
2423

2524
"github.com/docker/compose-cli/cli/metrics"
@@ -61,16 +60,16 @@ func metricsServerInterceptor(client metrics.Client) grpc.UnaryServerInterceptor
6160

6261
data, err := handler(ctx, req)
6362

64-
status := compose.SuccessStatus
63+
status := metrics.SuccessStatus
6564
if err != nil {
66-
status = compose.FailureStatus
65+
status = metrics.FailureStatus
6766
}
6867
command := methodMapping[info.FullMethod]
6968
if command != "" {
70-
client.Send(metrics.Command{
69+
client.SendUsage(metrics.Command{
7170
Command: command,
7271
Context: contextType,
73-
Source: compose.APISource,
72+
Source: metrics.APISource,
7473
Status: status,
7574
})
7675
}

cli/server/metrics_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ func TestAllMethodsHaveCorrespondingCliCommand(t *testing.T) {
6060

6161
func TestTrackSuccess(t *testing.T) {
6262
var mockMetrics = &mockMetricsClient{}
63-
mockMetrics.On("Send", metrics.Command{Command: "ps", Context: "aci", Status: "success", Source: "api"}).Return()
63+
mockMetrics.On("SendUsage", metrics.Command{Command: "ps", Context: "aci", Status: "success", Source: "api"}).Return()
6464
newClient := client.NewClient("aci", noopService{})
6565
interceptor := metricsServerInterceptor(mockMetrics)
6666

@@ -126,7 +126,7 @@ func (s *mockMetricsClient) WithCliVersionFunc(f func() string) {
126126
s.Called(f)
127127
}
128128

129-
func (s *mockMetricsClient) Send(command metrics.Command) {
129+
func (s *mockMetricsClient) SendUsage(command metrics.Command) {
130130
s.Called(command)
131131
}
132132

0 commit comments

Comments
 (0)