Skip to content

Commit 1ce5b94

Browse files
authored
log formatted error (#44353)
### What does this PR do? ensure `setup` subcommand prints human readable errors ### Motivation https://datadoghq.atlassian.net/browse/WINA-2100 show error with formatting (line breaks), not just in JSON blob MSI log error before <img width="1200" height="407" alt="image" src="https://github.com/user-attachments/assets/0899b0d3-5ba1-47b7-ba7b-f798fc540334" /> after <img width="1200" height="546" alt="image" src="https://github.com/user-attachments/assets/a6925dcd-7897-4069-8af4-7da25e32d299" /> --- cobra command error before ``` > .\datadog-installer-x86_64.exe setup --banana {"error":"unknown flag: --banana","code":0} ``` after ``` > C:\installer.exe setup --banana unknown flag: --banana ``` Co-authored-by: branden.clark <branden.clark@datadoghq.com>
1 parent ca9b8f9 commit 1ce5b94

7 files changed

Lines changed: 126 additions & 3 deletions

File tree

cmd/installer/command/command.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"github.com/spf13/cobra"
1515

1616
pkgconfigsetup "github.com/DataDog/datadog-agent/pkg/config/setup"
17+
"github.com/DataDog/datadog-agent/pkg/fleet/installer/commands"
1718
)
1819

1920
// common constants for all the updater subcommands.
@@ -111,6 +112,9 @@ Datadog Installer installs datadog-packages based on your commands.`,
111112
// TODO: Specific to Windows for now, as Linux needs more testing/validation of
112113
// the additional migration cases, and the main setup entrypoint is
113114
// currently `install.sh` not the `installer` binary.
115+
agentCmd.Annotations = map[string]string{
116+
commands.AnnotationHumanReadableErrors: "true",
117+
}
114118
agentCmd.RunE = func(cmd *cobra.Command, args []string) error {
115119
if len(args) == 0 {
116120
cmd.SetArgs([]string{"setup", "--flavor", "default"})
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Unless explicitly stated otherwise all files in this repository are licensed
2+
// under the Apache License Version 2.0.
3+
// This product includes software developed at Datadog (https://www.datadoghq.com/).
4+
// Copyright 2016-present Datadog, Inc.
5+
6+
package command
7+
8+
import (
9+
"runtime"
10+
"testing"
11+
12+
"github.com/stretchr/testify/assert"
13+
14+
"github.com/DataDog/datadog-agent/pkg/fleet/installer/commands"
15+
)
16+
17+
func TestMakeCommandHasHumanReadableAnnotationOnWindows(t *testing.T) {
18+
cmd := MakeCommand(nil)
19+
20+
if runtime.GOOS == "windows" {
21+
// We expect the default command to redirect to the setup command, which
22+
// should print human-readable errors
23+
assert.Equal(t, "true", cmd.Annotations[commands.AnnotationHumanReadableErrors],
24+
"root command should have human-readable-errors annotation on Windows")
25+
} else {
26+
assert.Empty(t, cmd.Annotations[commands.AnnotationHumanReadableErrors],
27+
"root command should not have human-readable-errors annotation on non-Windows platforms")
28+
}
29+
}

cmd/installer/main.go

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,23 +15,36 @@ import (
1515
"github.com/spf13/cobra"
1616
"go.uber.org/dig"
1717

18+
"github.com/DataDog/datadog-agent/pkg/fleet/installer/commands"
1819
installerErrors "github.com/DataDog/datadog-agent/pkg/fleet/installer/errors"
1920
)
2021

2122
func main() {
2223
os.Exit(runCmd(command.MakeCommand(subcommands.InstallerSubcommands())))
2324
}
2425

26+
// formatError returns the error formatted as human-readable text or JSON
27+
// based on the command's annotations.
28+
//
29+
// Most commands are internal and will use JSON errors to communicate result to the parent process.
30+
// The setup command is a special case and will print human-readable errors.
31+
func formatError(cmd *cobra.Command, err error) string {
32+
if cmd != nil && cmd.Annotations[commands.AnnotationHumanReadableErrors] == "true" {
33+
return err.Error()
34+
}
35+
return installerErrors.ToJSON(err)
36+
}
37+
2538
func runCmd(cmd *cobra.Command) int {
2639
// always silence errors, since they are handled here
2740
cmd.SilenceErrors = true
2841

29-
err := cmd.Execute()
42+
executedCmd, err := cmd.ExecuteC()
3043
if err != nil {
3144
if rootCauseErr := dig.RootCause(err); rootCauseErr != err {
32-
fmt.Fprintln(cmd.ErrOrStderr(), installerErrors.ToJSON(rootCauseErr))
45+
fmt.Fprintln(cmd.ErrOrStderr(), formatError(executedCmd, rootCauseErr))
3346
} else {
34-
fmt.Fprintln(cmd.ErrOrStderr(), installerErrors.ToJSON(err))
47+
fmt.Fprintln(cmd.ErrOrStderr(), formatError(executedCmd, err))
3548
}
3649
return -1
3750
}

cmd/installer/main_test.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// Unless explicitly stated otherwise all files in this repository are licensed
2+
// under the Apache License Version 2.0.
3+
// This product includes software developed at Datadog (https://www.datadoghq.com/).
4+
// Copyright 2023-present Datadog, Inc.
5+
6+
package main
7+
8+
import (
9+
"encoding/json"
10+
"errors"
11+
"testing"
12+
13+
"github.com/spf13/cobra"
14+
"github.com/stretchr/testify/assert"
15+
16+
"github.com/DataDog/datadog-agent/pkg/fleet/installer/commands"
17+
)
18+
19+
func TestFormatError_HumanReadable(t *testing.T) {
20+
cmd := &cobra.Command{
21+
Annotations: map[string]string{commands.AnnotationHumanReadableErrors: "true"},
22+
}
23+
err := errors.New("something went wrong")
24+
25+
result := formatError(cmd, err)
26+
27+
assert.Equal(t, "something went wrong", result)
28+
assert.NotContains(t, result, `"code"`)
29+
}
30+
31+
func TestFormatError_JSON(t *testing.T) {
32+
cmd := &cobra.Command{}
33+
err := errors.New("something went wrong")
34+
35+
result := formatError(cmd, err)
36+
37+
var parsed map[string]interface{}
38+
unmarshalErr := json.Unmarshal([]byte(result), &parsed)
39+
assert.NoError(t, unmarshalErr, "result should be valid JSON")
40+
assert.Contains(t, parsed, "error")
41+
assert.Contains(t, parsed, "code")
42+
assert.Equal(t, "something went wrong", parsed["error"])
43+
}
44+
45+
func TestFormatError_NilCommand(t *testing.T) {
46+
err := errors.New("something went wrong")
47+
48+
result := formatError(nil, err)
49+
50+
// Should fall back to JSON when command is nil
51+
var parsed map[string]interface{}
52+
unmarshalErr := json.Unmarshal([]byte(result), &parsed)
53+
assert.NoError(t, unmarshalErr, "result should be valid JSON")
54+
assert.Contains(t, parsed, "error")
55+
assert.Equal(t, "something went wrong", parsed["error"])
56+
}

pkg/fleet/installer/commands/command.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,14 @@ import (
3131
"github.com/DataDog/datadog-agent/pkg/version"
3232
)
3333

34+
const (
35+
// AnnotationHumanReadableErrors is the annotation key for commands that should
36+
// display errors in human-readable format instead of JSON.
37+
//
38+
// For example, `setup` is run by humans and its output should be human readable.
39+
AnnotationHumanReadableErrors = "human-readable-errors"
40+
)
41+
3442
type cmd struct {
3543
t *telemetry.Telemetry
3644
span *telemetry.Span
@@ -215,6 +223,9 @@ func setupCommand() *cobra.Command {
215223
Use: "setup",
216224
Hidden: true,
217225
GroupID: "installer",
226+
Annotations: map[string]string{
227+
AnnotationHumanReadableErrors: "true",
228+
},
218229
RunE: func(_ *cobra.Command, _ []string) (err error) {
219230
cmd := newCmd("setup")
220231
defer func() { cmd.stop(err) }()

pkg/fleet/installer/commands/command_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,3 +116,9 @@ func TestConfigAndPackageStates(t *testing.T) {
116116

117117
assert.Equal(t, expected, res)
118118
}
119+
120+
func TestSetupCommandHasHumanReadableAnnotation(t *testing.T) {
121+
cmd := setupCommand()
122+
assert.Equal(t, "true", cmd.Annotations[AnnotationHumanReadableErrors],
123+
"setup command should have human-readable-errors annotation")
124+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
enhancements:
3+
- |
4+
The datadog-installer `setup` command now prints human-readable errors instead of mixing JSON and text.

0 commit comments

Comments
 (0)