Skip to content

Commit b7953f1

Browse files
Add --log-file argument and log test result details
Signed-off-by: Kyle Edwards <[email protected]>
1 parent 0cec0a8 commit b7953f1

File tree

11 files changed

+115
-29
lines changed

11 files changed

+115
-29
lines changed

cmd/validate.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,11 @@ var validateCmd = &cobra.Command{
3434
SilenceUsage: true,
3535
SilenceErrors: false,
3636
RunE: func(cmd *cobra.Command, args []string) error {
37-
file, err := cmd.Flags().GetString("file")
37+
configPath, err := cmd.Flags().GetString("file")
3838
if err != nil {
3939
return err
4040
}
41-
if file == "" {
41+
if configPath == "" {
4242
return errors.New("you must specify a manifest with '--file path/url'")
4343
}
4444

@@ -48,7 +48,12 @@ var validateCmd = &cobra.Command{
4848
return err
4949
}
5050

51-
v, err := validator.Validate(image, file, cmd, debug)
51+
logPath, err := cmd.Flags().GetString("log-file")
52+
if err != nil {
53+
return err
54+
}
55+
56+
v, err := validator.Validate(image, configPath, logPath, cmd, debug)
5257
if err != nil {
5358
cmd.Printf("Error: %s\n", err.Error())
5459
return err
@@ -85,6 +90,7 @@ func imageArg(cmd *cobra.Command, args []string) error {
8590
func init() {
8691
rootCmd.AddCommand(validateCmd)
8792
validateCmd.PersistentFlags().String("file", "", "Path or URL of a manifest to validate against.")
93+
validateCmd.PersistentFlags().String("log-file", "", "File to print log output to.")
8894
validateCmd.PersistentFlags().Bool("debug", false, "Keep container running on failure for debugging.")
8995

9096
}

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ require (
2626
github.com/inconshreveable/mousetrap v1.0.0 // indirect
2727
github.com/json-iterator/go v1.1.12 // indirect
2828
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
29+
github.com/mattn/go-colorable v0.1.13 // indirect
2930
github.com/mattn/go-isatty v0.0.20 // indirect
3031
github.com/mattn/go-localereader v0.0.1 // indirect
3132
github.com/mattn/go-runewidth v0.0.15 // indirect
@@ -37,6 +38,7 @@ require (
3738
github.com/muesli/termenv v0.15.2 // indirect
3839
github.com/pmezard/go-difflib v1.0.0 // indirect
3940
github.com/rivo/uniseg v0.4.7 // indirect
41+
github.com/rs/zerolog v1.32.0 // indirect
4042
github.com/spf13/pflag v1.0.5 // indirect
4143
golang.org/x/net v0.17.0 // indirect
4244
golang.org/x/sync v0.6.0 // indirect

go.sum

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn
107107
github.com/containerd/console v1.0.4/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
108108
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
109109
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
110+
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
110111
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
111112
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
112113
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -303,13 +304,17 @@ github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVc
303304
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
304305
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
305306
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
307+
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
308+
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
306309
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
307310
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
308311
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
309312
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
310313
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
311314
github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
312315
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
316+
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
317+
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
313318
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
314319
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
315320
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
@@ -391,6 +396,9 @@ github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
391396
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
392397
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
393398
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
399+
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
400+
github.com/rs/zerolog v1.32.0 h1:keLypqrlIjaFsbmJOBdB/qvyF8KEtCWHwobLp5l/mQ0=
401+
github.com/rs/zerolog v1.32.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
394402
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
395403
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
396404
github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig=
@@ -635,8 +643,10 @@ golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBc
635643
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
636644
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
637645
golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
646+
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
638647
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
639648
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
649+
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
640650
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
641651
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
642652
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=

internal/container/container.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ type ContainerInterface interface {
4040
Start() error
4141
Remove() error
4242
Status() (*ContainerInfo, error)
43-
Exec(command ...string) (string, error)
43+
Exec(command ...string) (exitCode int, stdout string, stderr string, err error)
4444
Logs() (string, error)
4545
}
4646

internal/container/docker.go

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"encoding/json"
55
"errors"
66
"fmt"
7+
"io"
78
"os/exec"
89
"strings"
910
"time"
@@ -136,11 +137,42 @@ func (c DockerContainer) Status() (*ContainerInfo, error) {
136137
}
137138

138139
// Exec a command inside a container
139-
func (c DockerContainer) Exec(command ...string) (string, error) {
140+
func (c DockerContainer) Exec(command ...string) (exitCode int, stdout string, stderr string, err error) {
140141

141142
args := append([]string{"exec", c.Name}, command...)
142-
out, err := exec.Command("docker", args...).Output()
143-
return string(out), err
143+
cmd := exec.Command("docker", args...)
144+
stdoutPipe, err := cmd.StdoutPipe()
145+
if err != nil {
146+
return
147+
}
148+
stderrPipe, err := cmd.StderrPipe()
149+
if err != nil {
150+
return
151+
}
152+
153+
if err = cmd.Start(); err != nil {
154+
return
155+
}
156+
157+
stdoutBytes, err := io.ReadAll(stdoutPipe)
158+
if err != nil {
159+
return
160+
}
161+
stderrBytes, err := io.ReadAll(stderrPipe)
162+
if err != nil {
163+
return
164+
}
165+
166+
if err = cmd.Wait(); err != nil {
167+
switch t := err.(type) {
168+
case *exec.ExitError:
169+
exitCode = t.ExitCode()
170+
default:
171+
return
172+
}
173+
}
174+
175+
return exitCode, string(stdoutBytes), string(stderrBytes), nil
144176
}
145177

146178
// Get container logs

internal/container/docker_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,12 @@ func TestDockerContainer(t *testing.T) {
5858
}
5959
assert.Contains(status.RunCommand, "docker run", "Run command not stored correctly")
6060

61-
uname, err := c.Exec("uname", "-a")
61+
exitCode, uname, _, err := c.Exec("uname", "-a")
6262
if err != nil {
6363
t.Errorf("Failed to exec command in container: %s", err.Error())
6464
return
6565
}
66+
assert.Equal(0, exitCode)
6667
if !strings.Contains(uname, "Linux") {
6768
t.Error("Output for command 'uname' did not contain expected string 'Linux'")
6869
return

internal/validator/exec.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,18 @@
1818
package validator
1919

2020
import (
21+
"github.com/rs/zerolog"
22+
2123
canaryv1 "github.com/nvidia/container-canary/internal/apis/v1"
2224
"github.com/nvidia/container-canary/internal/container"
2325
)
2426

25-
func ExecCheck(c container.ContainerInterface, probe *canaryv1.Probe) (bool, error) {
27+
func ExecCheck(c container.ContainerInterface, probe *canaryv1.Probe, e *zerolog.Event) (bool, error) {
2628
action := probe.Exec
27-
_, err := c.Exec(action.Command...)
29+
exitCode, stdout, stderr, err := c.Exec(action.Command...)
2830
if err != nil {
29-
return false, nil
31+
return false, err
3032
}
31-
return true, nil
33+
e.Int("exitCode", exitCode).Str("stdout", stdout).Str("stderr", stderr)
34+
return exitCode == 0, nil
3235
}

internal/validator/httpget.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,10 @@ import (
2424

2525
canaryv1 "github.com/nvidia/container-canary/internal/apis/v1"
2626
"github.com/nvidia/container-canary/internal/container"
27+
"github.com/rs/zerolog"
2728
)
2829

29-
func HTTPGetCheck(c container.ContainerInterface, probe *canaryv1.Probe) (bool, error) {
30+
func HTTPGetCheck(c container.ContainerInterface, probe *canaryv1.Probe, e *zerolog.Event) (bool, error) {
3031
action := probe.HTTPGet
3132
client := &http.Client{}
3233
req, err := http.NewRequest("GET", fmt.Sprintf("http://localhost:%d%s", action.Port, action.Path), nil)
@@ -39,14 +40,23 @@ func HTTPGetCheck(c container.ContainerInterface, probe *canaryv1.Probe) (bool,
3940
req.Header.Set(header.Name, header.Value)
4041
}
4142
resp, err := client.Do(req)
43+
if resp != nil {
44+
headers := zerolog.Dict()
45+
for name, value := range resp.Header {
46+
headers.Str(name, strings.Join(value[:], ""))
47+
}
48+
e.Dict("headers", headers).Int("status", resp.StatusCode)
49+
}
4250
if err != nil {
4351
return false, nil
4452
}
4553
for _, header := range action.ResponseHTTPHeaders {
46-
if val, ok := resp.Header[header.Name]; ok {
54+
if val := resp.Header.Values(header.Name); len(val) != 0 {
4755
if header.Value != strings.Join(val[:], "") {
4856
return false, nil
4957
}
58+
} else {
59+
return false, nil
5060
}
5161
}
5262
defer resp.Body.Close()

internal/validator/tcp.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,10 @@ import (
2323

2424
canaryv1 "github.com/nvidia/container-canary/internal/apis/v1"
2525
"github.com/nvidia/container-canary/internal/container"
26+
"github.com/rs/zerolog"
2627
)
2728

28-
func TCPSocketCheck(c container.ContainerInterface, probe *canaryv1.Probe) (bool, error) {
29+
func TCPSocketCheck(c container.ContainerInterface, probe *canaryv1.Probe, e *zerolog.Event) (bool, error) {
2930
action := probe.TCPSocket
3031
address := fmt.Sprintf("localhost:%d", action.Port)
3132
_, err := net.Dial("tcp", address)

internal/validator/tui.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
tea "github.com/charmbracelet/bubbletea"
2626
canaryv1 "github.com/nvidia/container-canary/internal/apis/v1"
2727
"github.com/nvidia/container-canary/internal/container"
28+
"github.com/rs/zerolog"
2829
)
2930

3031
type model struct {
@@ -41,6 +42,7 @@ type model struct {
4142
configPath string
4243
err error
4344
tty bool
45+
log zerolog.Logger
4446
}
4547

4648
func (m model) Init() tea.Cmd {
@@ -148,7 +150,7 @@ func handleContainerStarted(m model, msg containerStarted) (model, tea.Cmd) {
148150
}
149151
commands = append(commands, tea.Printf("Validating %s against %s", highlightStyle(m.image), highlightStyle(m.validator.Name)))
150152
for _, check := range m.validator.Checks {
151-
commands = append(commands, runCheck(m.sub, m.container, check))
153+
commands = append(commands, runCheck(m.log, m.sub, m.container, check))
152154
}
153155
return m, tea.Batch(commands...)
154156
}

0 commit comments

Comments
 (0)