-
Notifications
You must be signed in to change notification settings - Fork 303
/
doctor.go
200 lines (164 loc) · 5.61 KB
/
doctor.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
package cli
import (
"context"
"fmt"
"runtime"
"time"
"github.com/spf13/cobra"
"github.com/tilt-dev/tilt/internal/analytics"
"github.com/tilt-dev/tilt/internal/container"
"github.com/tilt-dev/tilt/internal/docker"
"github.com/tilt-dev/tilt/internal/dockercompose"
"github.com/tilt-dev/tilt/pkg/logger"
"github.com/tilt-dev/tilt/pkg/model"
)
type doctorCmd struct {
}
func (c *doctorCmd) name() model.TiltSubcommand { return "doctor" }
func (c *doctorCmd) register() *cobra.Command {
cmd := &cobra.Command{
Use: "doctor",
Short: "Print diagnostic information about the Tilt environment, for filing bug reports",
}
addKubeContextFlag(cmd)
return cmd
}
func (c *doctorCmd) run(ctx context.Context, args []string) error {
analytics.Get(ctx).Incr("cmd.doctor", map[string]string{})
defer analytics.Get(ctx).Flush(time.Second)
fmt.Printf("Tilt: %s\n", buildStamp())
fmt.Printf("System: %s-%s\n", runtime.GOOS, runtime.GOARCH)
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
var localDocker, clusterDocker docker.Client
var localDockerErr, clusterDockerErr error
multipleClients := false
client, err := wireDockerCompositeClient(ctx)
if err == nil {
localDocker = client.DefaultLocalClient()
clusterDocker = client.DefaultClusterClient()
multipleClients = client.HasMultipleClients()
} else { // Figure out which client(s) had errors so we can show them
localDocker, localDockerErr = wireDockerLocalClient(ctx)
clusterDocker, clusterDockerErr = wireDockerClusterClient(ctx)
multipleClients = (localDockerErr != nil) != (clusterDockerErr != nil)
}
fmt.Println("---")
if multipleClients {
fmt.Println("Docker (cluster)")
} else {
fmt.Println("Docker")
}
if clusterDockerErr != nil {
printField("Host", nil, clusterDockerErr)
} else {
dockerEnv := clusterDocker.Env()
host := dockerEnv.DaemonHost()
if host == "" {
host = "[default]"
}
printField("Host", host, nil)
version, err := clusterDocker.ServerVersion(ctx)
printField("Server Version", version.Version, err)
printField("API Version", version.APIVersion, err)
builderVersion, err := clusterDocker.BuilderVersion(ctx)
printField("Builder", builderVersion, err)
}
if multipleClients {
fmt.Println("---")
fmt.Println("Docker (local)")
if localDockerErr != nil {
printField("Host", nil, localDockerErr)
} else {
dockerEnv := localDocker.Env()
host := dockerEnv.DaemonHost()
if host == "" {
host = "[default]"
}
printField("Host", host, nil)
version, err := localDocker.ServerVersion(ctx)
printField("Server Version", version.Version, err)
printField("Version", version.APIVersion, err)
builderVersion, err := localDocker.BuilderVersion(ctx)
printField("Builder", builderVersion, err)
}
}
// in theory, the env shouldn't matter since we're just calling the version subcommand,
// but to be safe, we'll try to use the actual local env if available
composeEnv := docker.LocalEnv{}
if localDockerErr == nil {
composeEnv = docker.LocalEnv(localDocker.Env())
}
dcCli := dockercompose.NewDockerComposeClient(composeEnv)
// errors getting the version aren't generally useful; in many cases it'll just mean that
// the command couldn't exec since Docker Compose isn't installed, for example, so they
// are just ignored and the field skipped
if composeVersion, composeBuild, err := dcCli.Version(ctx); err == nil {
composeField := composeVersion
if composeBuild != "" {
composeField += fmt.Sprintf(" (build %s)", composeBuild)
}
printField("Compose Version", composeField, nil)
}
fmt.Println("---")
fmt.Println("Kubernetes")
env, err := wireEnv(ctx)
printField("Env", env, err)
kContext, err := wireKubeContext(ctx)
printField("Context", kContext, err)
clusterName, err := wireClusterName(ctx)
if clusterName == "" {
clusterName = "Unknown"
}
printField("Cluster Name", clusterName, err)
ns, err := wireNamespace(ctx)
printField("Namespace", ns, err)
runtime, err := containerRuntime(ctx)
printField("Container Runtime", runtime, err)
kVersion, err := wireK8sVersion(ctx)
printField("Version", kVersion, err)
registryDisplay, err := clusterLocalRegistryDisplay(ctx)
printField("Cluster Local Registry", registryDisplay, err)
fmt.Println("---")
fmt.Println("Thanks for seeing the Tilt Doctor!")
fmt.Println("Please send the info above when filing bug reports. 💗")
fmt.Println("")
fmt.Println("The info below helps us understand how you're using Tilt so we can improve,")
fmt.Println("but is not required to ask for help.")
fmt.Println("---")
fmt.Println("Analytics Settings")
fmt.Println("--> (These results reflect your personal opt in/out status and may be overridden by an `analytics_settings` call in your Tiltfile)")
a := analytics.Get(ctx)
opt := a.UserOpt()
fmt.Printf("- User Mode: %s\n", opt)
fmt.Printf("- Machine: %s\n", a.MachineHash())
fmt.Printf("- Repo: %s\n", a.GitRepoHash())
return nil
}
func containerRuntime(ctx context.Context) (container.Runtime, error) {
kClient, err := wireK8sClient(ctx)
if err != nil {
return "", err
}
return kClient.ContainerRuntime(ctx), nil
}
func clusterLocalRegistryDisplay(ctx context.Context) (string, error) {
kClient, err := wireK8sClient(ctx)
if err != nil {
return "", err
}
// blackhole any warnings
newCtx := logger.WithLogger(ctx, logger.NewDeferredLogger(ctx))
registry := kClient.LocalRegistry(newCtx)
if container.IsEmptyRegistry(registry) {
return "none", nil
}
return fmt.Sprintf("%+v", registry), nil
}
func printField(name string, v interface{}, err error) {
if err != nil {
fmt.Printf("- %s: Error: %v\n", name, err)
} else {
fmt.Printf("- %s: %s\n", name, v)
}
}