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

Commit 6da92ff

Browse files
committed
refactor interfaces, add logging
1 parent 3a7cbd4 commit 6da92ff

File tree

14 files changed

+304
-186
lines changed

14 files changed

+304
-186
lines changed

internal/api/types.go

Lines changed: 13 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,21 @@ package api
22

33
import (
44
"context"
5-
6-
"github.com/Masterminds/semver/v3"
7-
"k8s.io/client-go/kubernetes"
85
)
96

10-
type Check func(context.Context, CheckOptions) CheckResults
11-
12-
type CheckOptions struct {
13-
CoderVersion *semver.Version
14-
Kubernetes kubernetes.Interface
7+
type Checker interface {
8+
// Validate returns an error if, and only if, the Checker was not
9+
// configured correctly.
10+
//
11+
// This method is responsible for verifying that the Checker has
12+
// all required parameters and the required parameters are valid,
13+
// and that optional parameters are valid, if set.
14+
Validate() error
15+
16+
// Run runs the checks and returns the results.
17+
//
18+
// This method will run through the checks and return results.
19+
Run(context.Context) CheckResults
1520
}
1621

1722
type CheckState int
@@ -31,15 +36,4 @@ type CheckResult struct {
3136
Details map[string]interface{}
3237
}
3338

34-
func ErrorResult(name string, summary string, err error) *CheckResult {
35-
return &CheckResult{
36-
Name: name,
37-
State: StateFailed,
38-
Summary: summary,
39-
Details: map[string]interface{}{
40-
"error": err,
41-
},
42-
}
43-
}
44-
4539
type CheckResults []*CheckResult

internal/api/util.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package api
2+
3+
import (
4+
"fmt"
5+
"io"
6+
)
7+
8+
func ErrorResult(name string, summary string, err error) *CheckResult {
9+
return &CheckResult{
10+
Name: name,
11+
State: StateFailed,
12+
Summary: summary,
13+
Details: map[string]interface{}{
14+
"error": err,
15+
},
16+
}
17+
}
18+
19+
func WriteResults(out io.Writer, results CheckResults) {
20+
for _, result := range results {
21+
switch result.State {
22+
case StatePassed:
23+
io.WriteString(out, "PASS ")
24+
case StateWarning:
25+
io.WriteString(out, "WARN ")
26+
case StateFailed:
27+
io.WriteString(out, "FAIL ")
28+
case StateInfo:
29+
io.WriteString(out, "INFO ")
30+
case StateSkipped:
31+
io.WriteString(out, "SKIP ")
32+
}
33+
34+
fmt.Fprintln(out, result.Summary)
35+
}
36+
}

internal/checks/checks.go

Lines changed: 0 additions & 14 deletions
This file was deleted.

internal/checks/kube/kubernetes.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package kube
2+
3+
import (
4+
"context"
5+
"io"
6+
7+
"cdr.dev/slog"
8+
"cdr.dev/slog/sloggers/sloghuman"
9+
"github.com/Masterminds/semver/v3"
10+
"k8s.io/client-go/kubernetes"
11+
12+
"github.com/cdr/coder-doctor/internal/api"
13+
)
14+
15+
var _ = api.Checker(&KubernetesChecker{})
16+
17+
type KubernetesChecker struct {
18+
client kubernetes.Interface
19+
coderVersion *semver.Version
20+
log slog.Logger
21+
}
22+
23+
type KubernetesCheckOption func(k *KubernetesChecker)
24+
25+
func NewKubernetesChecker(opts ...KubernetesCheckOption) *KubernetesChecker {
26+
checker := &KubernetesChecker{
27+
log: slog.Make(sloghuman.Sink(io.Discard)),
28+
}
29+
30+
for _, opt := range opts {
31+
opt(checker)
32+
}
33+
34+
return checker
35+
}
36+
37+
func (k *KubernetesChecker) Validate() error {
38+
return nil
39+
}
40+
41+
func (k *KubernetesChecker) Run(ctx context.Context) api.CheckResults {
42+
return api.CheckResults{
43+
k.CheckVersion(ctx),
44+
}
45+
}
46+
47+
func WithClient(client kubernetes.Interface) KubernetesCheckOption {
48+
return func(k *KubernetesChecker) {
49+
k.client = client
50+
}
51+
}
52+
53+
func WithCoderVersion(version *semver.Version) KubernetesCheckOption {
54+
return func(k *KubernetesChecker) {
55+
k.coderVersion = version
56+
}
57+
}
58+
59+
func WithLogger(log slog.Logger) KubernetesCheckOption {
60+
return func(k *KubernetesChecker) {
61+
k.log = log
62+
}
63+
}

internal/checks/kube/rbac.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
package kube

internal/checks/kube/resources.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
package kube

internal/checks/kube/version.go

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
package kube
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"fmt"
7+
8+
"cdr.dev/slog"
9+
"github.com/Masterminds/semver/v3"
10+
"k8s.io/apimachinery/pkg/version"
11+
12+
"github.com/cdr/coder-doctor/internal/api"
13+
)
14+
15+
type CoderVersionRequirement struct {
16+
CoderVersion *semver.Version
17+
KubernetesVersionMin *semver.Version
18+
KubernetesVersionMax *semver.Version
19+
}
20+
21+
var versionRequirements = []CoderVersionRequirement{
22+
{
23+
CoderVersion: semver.MustParse("1.21"),
24+
KubernetesVersionMin: semver.MustParse("1.19"),
25+
KubernetesVersionMax: semver.MustParse("1.22"),
26+
}, {
27+
CoderVersion: semver.MustParse("1.20"),
28+
KubernetesVersionMin: semver.MustParse("1.19"),
29+
KubernetesVersionMax: semver.MustParse("1.21"),
30+
},
31+
}
32+
33+
func findNearestVersion(coderVersion *semver.Version) *CoderVersionRequirement {
34+
var selectedVersion *CoderVersionRequirement
35+
36+
for _, v := range versionRequirements {
37+
if !v.CoderVersion.GreaterThan(coderVersion) {
38+
selectedVersion = &v
39+
break
40+
}
41+
}
42+
43+
return selectedVersion
44+
}
45+
46+
func (k *KubernetesChecker) CheckVersion(ctx context.Context) *api.CheckResult {
47+
const checkName = "kubernetes-version"
48+
49+
var versionInfo version.Info
50+
51+
// This uses the RESTClient rather than Discovery().ServerVersion()
52+
// because the latter does not accept a context.
53+
body, err := k.client.Discovery().RESTClient().Get().AbsPath("/version").Do(ctx).Raw()
54+
if err != nil {
55+
return api.ErrorResult(checkName, "failed to get version from server", err)
56+
}
57+
58+
err = json.Unmarshal(body, &versionInfo)
59+
if err != nil {
60+
return api.ErrorResult(checkName, "failed to parse server version", err)
61+
}
62+
63+
selectedVersion := findNearestVersion(k.coderVersion)
64+
k.log.Debug(ctx, "selected coder version",
65+
slog.F("requested", k.coderVersion),
66+
slog.F("selected", selectedVersion.CoderVersion))
67+
68+
kubernetesVersion, err := semver.NewVersion(versionInfo.GitVersion)
69+
if err != nil {
70+
return api.ErrorResult(checkName, "failed to parse server version", err)
71+
}
72+
73+
result := &api.CheckResult{
74+
Name: checkName,
75+
Details: map[string]interface{}{
76+
"coder-version": selectedVersion.CoderVersion.String(),
77+
"coder-version-major": selectedVersion.CoderVersion.Major(),
78+
"coder-version-minor": selectedVersion.CoderVersion.Minor(),
79+
"coder-version-patch": selectedVersion.CoderVersion.Patch(),
80+
"platform": versionInfo.Platform,
81+
"major": versionInfo.Major,
82+
"minor": versionInfo.Minor,
83+
"git-version": versionInfo.GitVersion,
84+
"git-commit": versionInfo.GitCommit,
85+
"git-tree-state": versionInfo.GitTreeState,
86+
"build-date": versionInfo.BuildDate,
87+
"go-version": versionInfo.GoVersion,
88+
"compiler": versionInfo.Compiler,
89+
},
90+
}
91+
92+
if kubernetesVersion.LessThan(selectedVersion.KubernetesVersionMin) || kubernetesVersion.GreaterThan(selectedVersion.KubernetesVersionMax) {
93+
result.State = api.StateFailed
94+
result.Summary = fmt.Sprintf("Coder %s supports Kubernetes %s to %s and was not tested with %s",
95+
k.coderVersion, selectedVersion.KubernetesVersionMin, selectedVersion.KubernetesVersionMax, kubernetesVersion)
96+
} else {
97+
result.State = api.StatePassed
98+
result.Summary = fmt.Sprintf("Coder %s supports Kubernetes %s to %s (server version %s)",
99+
k.coderVersion, selectedVersion.KubernetesVersionMin, selectedVersion.KubernetesVersionMax, kubernetesVersion)
100+
}
101+
102+
return result
103+
}

0 commit comments

Comments
 (0)