Skip to content

Commit 2336120

Browse files
committed
Initial implementation of skupper debug check
This introduces a framework for diagnostics commands, with an initial implementation of two Kubernetes checks (verifying that the Kubernetes API is accessible, and that the Kubernetes version is supported). The framework supports simple declaration of Cobra commands constructed from individual diagnostics, and dependencies between diagnostics. The kind spinner is copied with some adaptations borrowed from the Submariner project. Signed-off-by: Stephen Kitt <[email protected]>
1 parent caf5ec8 commit 2336120

File tree

14 files changed

+1137
-6
lines changed

14 files changed

+1137
-6
lines changed

go.mod

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,18 @@ require (
99
github.com/briandowns/spinner v1.23.0
1010
github.com/cenkalti/backoff/v4 v4.3.0
1111
github.com/fsnotify/fsnotify v1.7.0
12+
github.com/go-openapi/errors v0.20.3
1213
github.com/go-openapi/runtime v0.24.1
1314
github.com/go-openapi/strfmt v0.21.3
15+
github.com/go-openapi/swag v0.22.8
16+
github.com/go-openapi/validate v0.22.0
1417
github.com/google/go-cmp v0.6.0
1518
github.com/google/uuid v1.6.0
1619
github.com/gorilla/handlers v1.5.2
1720
github.com/gorilla/mux v1.8.1
1821
github.com/heimdalr/dag v1.5.0
1922
github.com/interconnectedcloud/go-amqp v0.12.6-0.20200506124159-f51e540008b5
23+
github.com/mattn/go-isatty v0.0.20
2024
github.com/oapi-codegen/oapi-codegen/v2 v2.3.0
2125
github.com/oapi-codegen/runtime v1.1.1
2226
github.com/openshift/api v0.0.0-20210428205234-a8389931bee7
@@ -29,12 +33,12 @@ require (
2933
golang.org/x/sys v0.28.0
3034
golang.org/x/text v0.21.0
3135
golang.org/x/time v0.5.0
32-
gopkg.in/yaml.v3 v3.0.1
3336
gotest.tools/v3 v3.5.1
3437
k8s.io/api v0.31.0
3538
k8s.io/apimachinery v0.31.0
3639
k8s.io/client-go v0.31.0
3740
k8s.io/code-generator v0.31.0
41+
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8
3842
sigs.k8s.io/yaml v1.4.0
3943
)
4044

@@ -58,13 +62,10 @@ require (
5862
github.com/getkin/kin-openapi v0.124.0 // indirect
5963
github.com/go-logr/logr v1.4.2 // indirect
6064
github.com/go-openapi/analysis v0.21.2 // indirect
61-
github.com/go-openapi/errors v0.20.3 // indirect
6265
github.com/go-openapi/jsonpointer v0.20.2 // indirect
6366
github.com/go-openapi/jsonreference v0.20.2 // indirect
6467
github.com/go-openapi/loads v0.21.1 // indirect
6568
github.com/go-openapi/spec v0.20.4 // indirect
66-
github.com/go-openapi/swag v0.22.8 // indirect
67-
github.com/go-openapi/validate v0.22.0 // indirect
6869
github.com/gogo/protobuf v1.3.2 // indirect
6970
github.com/golang/protobuf v1.5.4 // indirect
7071
github.com/google/gnostic-models v0.6.8 // indirect
@@ -76,7 +77,6 @@ require (
7677
github.com/json-iterator/go v1.1.12 // indirect
7778
github.com/mailru/easyjson v0.7.7 // indirect
7879
github.com/mattn/go-colorable v0.1.13 // indirect
79-
github.com/mattn/go-isatty v0.0.20 // indirect
8080
github.com/mitchellh/mapstructure v1.4.3 // indirect
8181
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
8282
github.com/modern-go/reflect2 v1.0.2 // indirect
@@ -101,10 +101,10 @@ require (
101101
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
102102
gopkg.in/inf.v0 v0.9.1 // indirect
103103
gopkg.in/yaml.v2 v2.4.0 // indirect
104+
gopkg.in/yaml.v3 v3.0.1 // indirect
104105
k8s.io/gengo/v2 v2.0.0-20240228010128-51d4e06bde70 // indirect
105106
k8s.io/klog/v2 v2.130.1 // indirect
106107
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect
107-
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect
108108
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
109109
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
110110
)
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
package check
2+
3+
import (
4+
"github.com/skupperproject/skupper/api/types"
5+
"github.com/skupperproject/skupper/internal/cmd/skupper/common/utils"
6+
"github.com/skupperproject/skupper/internal/cmd/skupper/debug/check/cli"
7+
"github.com/skupperproject/skupper/internal/cmd/skupper/debug/check/command"
8+
"github.com/skupperproject/skupper/internal/cmd/skupper/debug/check/kube"
9+
"github.com/skupperproject/skupper/internal/config"
10+
"github.com/spf13/cobra"
11+
"k8s.io/utils/ptr"
12+
)
13+
14+
type cmdCheck struct {
15+
cmd *cobra.Command
16+
platformCommands map[types.Platform][]*command.Check
17+
}
18+
19+
func NewCmdCheck() *cobra.Command {
20+
checkCmd := cmdCheck{
21+
platformCommands: map[types.Platform][]*command.Check{},
22+
}
23+
24+
checkCmd.cmd = &cobra.Command{
25+
Use: "check",
26+
Short: "Run diagnostics",
27+
Long: `Runs diagnostics to identify potential issues in the environment hosting Skupper`,
28+
Example: `skupper debug check -p kubernetes`,
29+
Run: func(cmd *cobra.Command, args []string) {
30+
utils.HandleError(utils.GenericError, checkCmd.Run(cmd, args))
31+
},
32+
}
33+
34+
checkCmd.registerCommand(types.PlatformKubernetes, ptr.To(kube.NewCmdCheckK8sAccess()))
35+
checkCmd.registerCommand(types.PlatformKubernetes, ptr.To(kube.NewCmdCheckK8sVersion()))
36+
for _, cmds := range checkCmd.platformCommands {
37+
for i := range cmds {
38+
subCommand := *cmds[i]
39+
cmd := &cobra.Command{
40+
Use: subCommand.Name(),
41+
Short: "check that " + subCommand.CheckDescription(),
42+
Run: func(cmd *cobra.Command, args []string) {
43+
status := cli.NewReporter()
44+
defer status.End()
45+
runCommandWithDeps(status, subCommand, map[string]bool{}, cmd)
46+
},
47+
}
48+
// TODO Adjust "skupper" to args[0]
49+
cmd.Example = "skupper debug check " + subCommand.Name()
50+
checkCmd.cmd.AddCommand(cmd)
51+
}
52+
}
53+
54+
return checkCmd.cmd
55+
}
56+
57+
func (c *cmdCheck) registerCommand(platform types.Platform, cmd *command.Check) {
58+
c.platformCommands[platform] = append(c.platformCommands[platform], cmd)
59+
}
60+
61+
func (c cmdCheck) Run(cmd *cobra.Command, args []string) error {
62+
platform := config.GetPlatform()
63+
64+
// Run all available sub-commands, in dependency order (falling back to declaration order)
65+
// In the map of processed dependencies, true indicates that the command previously ran successfully,
66+
// false that it failed previously
67+
processedDependencies := make(map[string]bool)
68+
69+
status := cli.NewReporter()
70+
defer status.End()
71+
72+
for i := range c.platformCommands[platform] {
73+
subCommand := *c.platformCommands[platform][i]
74+
if _, seen := processedDependencies[subCommand.Name()]; seen {
75+
// The command has already been run as a dependency, skip it
76+
continue
77+
}
78+
_ = runCommandWithDeps(status, subCommand, processedDependencies, cmd)
79+
}
80+
81+
// For UX consistency, errors are handled internally
82+
return nil
83+
}
84+
85+
func runCommandWithDeps(status cli.Reporter, dc command.Check, processed map[string]bool, cmd *cobra.Command) error {
86+
dependencies := dc.Dependencies()
87+
for i := range dependencies {
88+
dependency := *dependencies[i]
89+
if succeeded, seen := processed[dependency.Name()]; seen {
90+
if succeeded {
91+
// The command previously succeeded, skip it but continue
92+
continue
93+
}
94+
// The command previously failed, stop (assuming that the previous run reported the error)
95+
return nil
96+
}
97+
if err := runCommandWithDeps(status, dependency, processed, cmd); err != nil {
98+
return err
99+
}
100+
}
101+
102+
status.Start("Checking that " + dc.CheckDescription())
103+
if err := dc.Run(status, cmd); err != nil {
104+
processed[dc.Name()] = false
105+
return err
106+
}
107+
108+
processed[dc.Name()] = true
109+
status.Success("")
110+
return nil
111+
}

0 commit comments

Comments
 (0)