-
Notifications
You must be signed in to change notification settings - Fork 3
feat: initial implementation of coder-doctor #3
Changes from 1 commit
8a7c768
7152293
215ee39
fabea0d
04e12e3
12836d2
84882af
5a26ffc
f2f9988
31cb1c5
71f97fc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package api | ||
|
||
// ErrorResult returns a CheckResult when an error occurs. | ||
func ErrorResult(name string, summary string, err error) *CheckResult { | ||
return &CheckResult{ | ||
Name: name, | ||
State: StateFailed, | ||
Summary: summary, | ||
Details: map[string]interface{}{ | ||
"error": err, | ||
}, | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package api_test | ||
|
||
import ( | ||
"errors" | ||
"testing" | ||
|
||
"cdr.dev/slog/sloggers/slogtest/assert" | ||
"github.com/cdr/coder-doctor/internal/api" | ||
) | ||
|
||
func TestErrorResult(t *testing.T) { | ||
t.Parallel() | ||
|
||
err := errors.New("failed to connect to database") | ||
res := api.ErrorResult("check-name", "check failed", err) | ||
|
||
assert.Equal(t, "name matches", "check-name", res.Name) | ||
assert.Equal(t, "state matches", api.StateFailed, res.State) | ||
assert.Equal(t, "summary matches", "check failed", res.Summary) | ||
assert.Equal(t, "error matches", err, res.Details["error"]) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package api_test | ||
|
||
import ( | ||
"testing" | ||
|
||
"cdr.dev/slog/sloggers/slogtest/assert" | ||
"github.com/cdr/coder-doctor/internal/api" | ||
) | ||
|
||
func TestKnownStates(t *testing.T) { | ||
t.Parallel() | ||
|
||
states := []api.CheckState{ | ||
api.StatePassed, | ||
api.StateWarning, | ||
api.StateFailed, | ||
api.StateInfo, | ||
api.StateSkipped, | ||
} | ||
|
||
for _, state := range states { | ||
t.Run(state.String(), func(t *testing.T) { | ||
emoji, err := state.Emoji() | ||
assert.Success(t, "state.Emoji() error non-nil", err) | ||
assert.True(t, "state.Emoji() is non-empty", len(emoji) > 0) | ||
|
||
text, err := state.Text() | ||
assert.Success(t, "state.Text() error non-nil", err) | ||
assert.True(t, "state.Text() is non-empty", len(text) > 0) | ||
|
||
str := state.String() | ||
assert.True(t, "state.String() is non-empty", len(str) > 0) | ||
}) | ||
} | ||
} |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package api | ||
|
||
// ResultWriter writes the given result to a configured output. | ||
type ResultWriter interface { | ||
WriteResult(result *CheckResult) error | ||
} | ||
|
||
// DiscardWriter is a writer that discards all results. | ||
type DiscardWriter struct { | ||
} | ||
|
||
func (*DiscardWriter) WriteResult(result *CheckResult) error { | ||
return nil | ||
} | ||
jawnsy marked this conversation as resolved.
Show resolved
Hide resolved
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package api_test | ||
|
||
import ( | ||
"testing" | ||
|
||
"cdr.dev/slog/sloggers/slogtest/assert" | ||
"github.com/cdr/coder-doctor/internal/api" | ||
) | ||
|
||
var _ = api.ResultWriter(&api.DiscardWriter{}) | ||
|
||
func TestDiscardWriter(t *testing.T) { | ||
t.Parallel() | ||
|
||
w := &api.DiscardWriter{} | ||
err := w.WriteResult(nil) | ||
|
||
assert.Success(t, "discard with nil result", err) | ||
|
||
err = w.WriteResult(&api.CheckResult{ | ||
Name: "test-check", | ||
State: api.StatePassed, | ||
Summary: "check successful", | ||
}) | ||
assert.Success(t, "discard with success result", err) | ||
} |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,7 +8,7 @@ import ( | |
"cdr.dev/slog" | ||
"cdr.dev/slog/sloggers/sloghuman" | ||
|
||
kclient "k8s.io/client-go/kubernetes" | ||
"k8s.io/client-go/kubernetes" | ||
// Kubernetes authentication plugins | ||
_ "k8s.io/client-go/plugin/pkg/client/auth/azure" | ||
_ "k8s.io/client-go/plugin/pkg/client/auth/exec" | ||
|
@@ -17,8 +17,8 @@ import ( | |
_ "k8s.io/client-go/plugin/pkg/client/auth/openstack" | ||
"k8s.io/client-go/tools/clientcmd" | ||
|
||
"github.com/cdr/coder-doctor/internal/api" | ||
"github.com/cdr/coder-doctor/internal/checks/kube" | ||
"github.com/cdr/coder-doctor/internal/humanwriter" | ||
) | ||
|
||
func NewCommand() *cobra.Command { | ||
|
@@ -78,7 +78,7 @@ func run(cmd *cobra.Command, _ []string) error { | |
return err | ||
} | ||
|
||
clientset, err := kclient.NewForConfig(config) | ||
clientset, err := kubernetes.NewForConfig(config) | ||
if err != nil { | ||
return err | ||
} | ||
|
@@ -99,6 +99,8 @@ func run(cmd *cobra.Command, _ []string) error { | |
return err | ||
} | ||
|
||
// TODO: this is pretty arbitrary, use a defined verbosity similar to | ||
// kubectl | ||
if verbosity > 5 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ha, I usually set There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. kubectl has a really nicely-defined list, with criteria for what goes where -- I think we should adopt similar definitions, because otherwise (as a developer), it's difficult to know what level to log things, and it makes the level kinda useless for users: https://kubernetes.io/docs/reference/kubectl/_print/#kubectl-output-verbosity-and-debugging |
||
log = log.Leveled(slog.LevelDebug) | ||
} | ||
|
@@ -109,10 +111,15 @@ func run(cmd *cobra.Command, _ []string) error { | |
kube.WithLogger(log), | ||
) | ||
|
||
writer := humanwriter.New() | ||
|
||
results := checker.Run(cmd.Context()) | ||
err = api.WriteResults(cmd.OutOrStdout(), results) | ||
if err != nil { | ||
return xerrors.Errorf("failed to write results to stdout: %w", err) | ||
for _, result := range results { | ||
err = writer.WriteResult(result) | ||
if err != nil { | ||
return xerrors.Errorf("failed to write results to stdout: %w", err) | ||
} | ||
|
||
} | ||
|
||
return nil | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason we make these integers rather than strings?
That way we could eliminate the string function, and set the values to the const names.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There wasn't a particular reason, no! I thought that integers were just the common way to do enums like this in Go.
A nice benefit of this is that the filtering can be done with bitwise operations (to determine whether to log each level), but of course we could do that with a set of bools or something too: https://github.com/cdr/coder-doctor/blob/31cb1c565fc42be9b40d9c68e634625287086b0b/internal/filterwriter/filter.go#L42-L60
I suppose another benefit is that comparisons should be faster with integers, because then it's just a numeric equality instead of string comparison
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Neato. Appreciate the context, and I'd agree with you here!