Skip to content

Commit 8059482

Browse files
committedJan 22, 2018
diagnostics: refactor build-and-run for clarity
Improve the legibility of the code that builds and runs diagnostics. The main confusion was the need to track and report the number of diagnostic errors and warnings versus problems that halt execution prematurely and the need to return a correct status code at completion. In the end it seemed simplest to just have the logger report how many diagnostic errors and warnings were seen, leaving function signatures to return only build/run errors. As a side effect, I looked at the ConfigLoading code that does an early check to see if there is a client config, and concluded it was confusing and unnecessary for it to be a diagnostic, so I refactored it away. Main diagnostics as well as pod diagnostics are now implemented more uniformly.
1 parent 241fd4f commit 8059482

File tree

10 files changed

+347
-484
lines changed

10 files changed

+347
-484
lines changed
 

‎pkg/oc/admin/diagnostics/client.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,14 @@ func availableClientDiagnostics() types.DiagnosticList {
1818
}
1919

2020
// buildClientDiagnostics builds client Diagnostic objects based on the rawConfig passed in.
21-
// Returns the Diagnostics built, "ok" bool for whether to proceed or abort, and an error if any was encountered during the building of diagnostics.) {
22-
func (o DiagnosticsOptions) buildClientDiagnostics(rawConfig *clientcmdapi.Config) ([]types.Diagnostic, bool, error) {
21+
// Returns the Diagnostics built, and any fatal errors encountered during the building of diagnostics.
22+
func (o DiagnosticsOptions) buildClientDiagnostics(rawConfig *clientcmdapi.Config) ([]types.Diagnostic, error) {
2323
available := availableClientDiagnostics().Names()
2424

2525
networkClient, err := o.Factory.OpenshiftInternalNetworkClient()
2626
kubeClient, clientErr := o.Factory.ClientSet()
2727
if clientErr != nil || err != nil {
28-
o.Logger.Notice("CED0001", "Could not configure a client, so client diagnostics are limited to testing configuration and connection")
28+
o.Logger().Notice("CED0001", "Could not configure a client, so client diagnostics are limited to testing configuration and connection")
2929
available = sets.NewString(clientdiags.ConfigContextsName)
3030
}
3131

@@ -64,8 +64,8 @@ func (o DiagnosticsOptions) buildClientDiagnostics(rawConfig *clientcmdapi.Confi
6464
nd.PreventModification = o.PreventModification
6565
diagnostics = append(diagnostics, nd)
6666
default:
67-
return nil, false, fmt.Errorf("unknown diagnostic: %v", diagnosticName)
67+
return nil, fmt.Errorf("unknown diagnostic: %v", diagnosticName)
6868
}
6969
}
70-
return diagnostics, true, clientErr
70+
return diagnostics, clientErr
7171
}

‎pkg/oc/admin/diagnostics/cluster.go

+53-51
Original file line numberDiff line numberDiff line change
@@ -43,52 +43,50 @@ func availableClusterDiagnostics() types.DiagnosticList {
4343
}
4444

4545
// buildClusterDiagnostics builds cluster Diagnostic objects if a cluster-admin client can be extracted from the rawConfig passed in.
46-
// Returns the Diagnostics built, "ok" bool for whether to proceed or abort, and an error if any was encountered during the building of diagnostics.) {
47-
func (o DiagnosticsOptions) buildClusterDiagnostics(rawConfig *clientcmdapi.Config) ([]types.Diagnostic, bool, error) {
46+
// Returns the Diagnostics built and any fatal error encountered during the building of diagnostics.
47+
func (o DiagnosticsOptions) buildClusterDiagnostics(rawConfig *clientcmdapi.Config) ([]types.Diagnostic, error) {
4848
requestedDiagnostics := availableClusterDiagnostics().Names().Intersection(sets.NewString(o.RequestedDiagnostics.List()...)).List()
4949
if len(requestedDiagnostics) == 0 { // no diagnostics to run here
50-
return nil, true, nil // don't waste time on discovery
50+
return nil, nil // don't waste time on discovery
5151
}
5252

53-
var (
54-
kclusterClient kclientset.Interface
55-
)
53+
var kclusterClient kclientset.Interface
5654

57-
config, kclusterClient, found, serverUrl, err := o.findClusterClients(rawConfig)
58-
if !found {
59-
o.Logger.Notice("CED1002", "Could not configure a client with cluster-admin permissions for the current server, so cluster diagnostics will be skipped")
60-
return nil, true, err
55+
config, kclusterClient, serverUrl, err := o.findClusterClients(rawConfig)
56+
if config == nil {
57+
o.Logger().Notice("CED1002", "Could not configure a client with cluster-admin permissions for the current server, so cluster diagnostics will be skipped")
58+
return nil, nil
6159
}
6260
if err != nil {
63-
return nil, false, err
61+
return nil, err
6462
}
6563
imageClient, err := imageclient.NewForConfig(config)
6664
if err != nil {
67-
return nil, false, err
65+
return nil, err
6866
}
6967
projectClient, err := projectclient.NewForConfig(config)
7068
if err != nil {
71-
return nil, false, err
69+
return nil, err
7270
}
7371
routeClient, err := routeclient.NewForConfig(config)
7472
if err != nil {
75-
return nil, false, err
73+
return nil, err
7674
}
7775
appsClient, err := appsclient.NewForConfig(config)
7876
if err != nil {
79-
return nil, false, err
77+
return nil, err
8078
}
8179
oauthClient, err := oauthclient.NewForConfig(config)
8280
if err != nil {
83-
return nil, false, err
81+
return nil, err
8482
}
8583
oauthorizationClient, err := oauthorizationclient.NewForConfig(config)
8684
if err != nil {
87-
return nil, false, err
85+
return nil, err
8886
}
8987
securityClient, err := securityclient.NewForConfig(config)
9088
if err != nil {
91-
return nil, false, err
89+
return nil, err
9290
}
9391

9492
diagnostics := []types.Diagnostic{}
@@ -116,64 +114,68 @@ func (o DiagnosticsOptions) buildClusterDiagnostics(rawConfig *clientcmdapi.Conf
116114
case clustdiags.RouteCertificateValidationName:
117115
d = &clustdiags.RouteCertificateValidation{SARClient: kclusterClient.Authorization(), RESTConfig: config}
118116
default:
119-
return nil, false, fmt.Errorf("unknown diagnostic: %v", diagnosticName)
117+
return nil, fmt.Errorf("unknown diagnostic: %v", diagnosticName)
120118
}
121119
diagnostics = append(diagnostics, d)
122120
}
123-
return diagnostics, true, nil
121+
return diagnostics, nil
124122
}
125123

126124
// attempts to find which context in the config might be a cluster-admin for the server in the current context.
127-
func (o DiagnosticsOptions) findClusterClients(rawConfig *clientcmdapi.Config) (*rest.Config, kclientset.Interface, bool, string, error) {
125+
// returns config for the context chosen, kclusterClient for same, serverUrl of same, and any fatal error
126+
func (o DiagnosticsOptions) findClusterClients(rawConfig *clientcmdapi.Config) (*rest.Config, kclientset.Interface, string, error) {
128127
if o.ClientClusterContext != "" { // user has specified cluster context to use
129-
if context, exists := rawConfig.Contexts[o.ClientClusterContext]; !exists {
128+
context, exists := rawConfig.Contexts[o.ClientClusterContext]
129+
if !exists {
130130
configErr := fmt.Errorf("Specified '%s' as cluster-admin context, but it was not found in your client configuration.", o.ClientClusterContext)
131-
o.Logger.Error("CED1003", configErr.Error())
132-
return nil, nil, false, "", configErr
133-
} else if config, kube, found, serverUrl, err := o.makeClusterClients(rawConfig, o.ClientClusterContext, context); found {
134-
return config, kube, true, serverUrl, err
135-
} else {
136-
return nil, nil, false, "", err
131+
o.Logger().Error("CED1003", configErr.Error())
132+
return nil, nil, "", configErr
137133
}
134+
config, kube, serverUrl, err := o.makeClusterClients(rawConfig, o.ClientClusterContext, context)
135+
if err != nil || config == nil {
136+
return nil, nil, "", err
137+
}
138+
return config, kube, serverUrl, nil
138139
}
139140
currentContext, exists := rawConfig.Contexts[rawConfig.CurrentContext]
140141
if !exists { // config specified cluster admin context that doesn't exist; complain and quit
141142
configErr := fmt.Errorf("Current context '%s' not found in client configuration; will not attempt cluster diagnostics.", rawConfig.CurrentContext)
142-
o.Logger.Error("CED1004", configErr.Error())
143-
return nil, nil, false, "", configErr
143+
o.Logger().Error("CED1004", configErr.Error())
144+
return nil, nil, "", configErr
144145
}
145146
// check if current context is already cluster admin
146-
if config, kube, found, serverUrl, err := o.makeClusterClients(rawConfig, rawConfig.CurrentContext, currentContext); found {
147-
return config, kube, true, serverUrl, err
147+
config, kube, serverUrl, err := o.makeClusterClients(rawConfig, rawConfig.CurrentContext, currentContext)
148+
if err == nil && config != nil {
149+
return config, kube, serverUrl, nil
148150
}
149151
// otherwise, for convenience, search for a context with the same server but with the system:admin user
150152
for name, context := range rawConfig.Contexts {
151153
if context.Cluster == currentContext.Cluster && name != rawConfig.CurrentContext && strings.HasPrefix(context.AuthInfo, "system:admin/") {
152-
if config, kube, found, serverUrl, err := o.makeClusterClients(rawConfig, name, context); found {
153-
return config, kube, true, serverUrl, err
154-
} else {
155-
return nil, nil, false, "", err // don't try more than one such context, they'll probably fail the same
154+
config, kube, serverUrl, err := o.makeClusterClients(rawConfig, name, context)
155+
if err != nil || config == nil {
156+
break // don't try more than one such context, they'll probably fail the same
156157
}
158+
return config, kube, serverUrl, nil
157159
}
158160
}
159-
return nil, nil, false, "", nil
161+
return nil, nil, "", nil
160162
}
161163

162164
// makes the client from the specified context and determines whether it is a cluster-admin.
163-
func (o DiagnosticsOptions) makeClusterClients(rawConfig *clientcmdapi.Config, contextName string, context *clientcmdapi.Context) (*rest.Config, kclientset.Interface, bool, string, error) {
165+
func (o DiagnosticsOptions) makeClusterClients(rawConfig *clientcmdapi.Config, contextName string, context *clientcmdapi.Context) (*rest.Config, kclientset.Interface, string, error) {
164166
overrides := &clientcmd.ConfigOverrides{Context: *context}
165167
clientConfig := clientcmd.NewDefaultClientConfig(*rawConfig, overrides)
166168
serverUrl := rawConfig.Clusters[context.Cluster].Server
167169
factory := osclientcmd.NewFactory(clientConfig)
168170
config, err := factory.ClientConfig()
169171
if err != nil {
170-
o.Logger.Debug("CED1006", fmt.Sprintf("Error creating client for context '%s':\n%v", contextName, err))
171-
return nil, nil, false, "", nil
172+
o.Logger().Debug("CED1006", fmt.Sprintf("Error creating client for context '%s':\n%v", contextName, err))
173+
return nil, nil, "", nil
172174
}
173-
o.Logger.Debug("CED1005", fmt.Sprintf("Checking if context is cluster-admin: '%s'", contextName))
175+
o.Logger().Debug("CED1005", fmt.Sprintf("Checking if context is cluster-admin: '%s'", contextName))
174176
if kubeClient, err := factory.ClientSet(); err != nil {
175-
o.Logger.Debug("CED1006", fmt.Sprintf("Error creating client for context '%s':\n%v", contextName, err))
176-
return nil, nil, false, "", nil
177+
o.Logger().Debug("CED1006", fmt.Sprintf("Error creating client for context '%s':\n%v", contextName, err))
178+
return nil, nil, "", nil
177179
} else {
178180
subjectAccessReview := &authorization.SelfSubjectAccessReview{
179181
Spec: authorization.SelfSubjectAccessReviewSpec{
@@ -187,17 +189,17 @@ func (o DiagnosticsOptions) makeClusterClients(rawConfig *clientcmdapi.Config, c
187189
}
188190
if resp, err := kubeClient.Authorization().SelfSubjectAccessReviews().Create(subjectAccessReview); err != nil {
189191
if regexp.MustCompile(`User "[\w:]+" cannot create \w+ at the cluster scope`).MatchString(err.Error()) {
190-
o.Logger.Debug("CED1007", fmt.Sprintf("Context '%s' does not have cluster-admin access:\n%v", contextName, err))
191-
return nil, nil, false, "", nil
192+
o.Logger().Debug("CED1007", fmt.Sprintf("Context '%s' does not have cluster-admin access:\n%v", contextName, err))
193+
return nil, nil, "", nil
192194
} else {
193-
o.Logger.Error("CED1008", fmt.Sprintf("Unknown error testing cluster-admin access for context '%s':\n%v", contextName, err))
194-
return nil, nil, false, "", err
195+
o.Logger().Error("CED1008", fmt.Sprintf("Unknown error testing cluster-admin access for context '%s':\n%v", contextName, err))
196+
return nil, nil, "", err
195197
}
196198
} else if resp.Status.Allowed {
197-
o.Logger.Info("CED1009", fmt.Sprintf("Using context for cluster-admin access: '%s'", contextName))
198-
return config, kubeClient, true, serverUrl, nil
199+
o.Logger().Info("CED1009", fmt.Sprintf("Using context for cluster-admin access: '%s'", contextName))
200+
return config, kubeClient, serverUrl, nil
199201
}
200202
}
201-
o.Logger.Debug("CED1010", fmt.Sprintf("Context does not have cluster-admin access: '%s'", contextName))
202-
return nil, nil, false, "", nil
203+
o.Logger().Debug("CED1010", fmt.Sprintf("Context does not have cluster-admin access: '%s'", contextName))
204+
return nil, nil, "", nil
203205
}

‎pkg/oc/admin/diagnostics/config.go

+124-16
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,17 @@ package diagnostics
22

33
import (
44
"errors"
5+
"fmt"
6+
"io/ioutil"
7+
"os"
58

9+
"k8s.io/client-go/tools/clientcmd"
610
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
711

812
"github.com/openshift/origin/pkg/client/config"
9-
clientdiagnostics "github.com/openshift/origin/pkg/oc/admin/diagnostics/diagnostics/client"
10-
"github.com/openshift/origin/pkg/oc/admin/diagnostics/diagnostics/types"
13+
"github.com/openshift/origin/pkg/oc/admin/diagnostics/diagnostics/util"
1114
)
1215

13-
// determine if we even have a client config
14-
func (o DiagnosticsOptions) detectClientConfig() (bool, bool, []types.DiagnosticError, []types.DiagnosticError) {
15-
if o.ClientFlags == nil {
16-
return false, false, []types.DiagnosticError{}, []types.DiagnosticError{}
17-
}
18-
diagnostic := &clientdiagnostics.ConfigLoading{ConfFlagName: config.OpenShiftConfigFlagName, ClientFlags: o.ClientFlags}
19-
o.Logger.Notice("CED2011", "Determining if client configuration exists for client/cluster diagnostics")
20-
result := diagnostic.Check()
21-
for _, entry := range result.Logs() {
22-
o.Logger.LogEntry(entry)
23-
}
24-
return true, diagnostic.SuccessfulLoad(), result.Warnings(), result.Errors()
25-
}
26-
2716
// use the base factory to return a raw config (not specific to a context)
2817
func (o DiagnosticsOptions) buildRawConfig() (*clientcmdapi.Config, error) {
2918
kubeConfig, configErr := o.Factory.OpenShiftClientConfig().RawConfig()
@@ -35,3 +24,122 @@ func (o DiagnosticsOptions) buildRawConfig() (*clientcmdapi.Config, error) {
3524
}
3625
return &kubeConfig, nil
3726
}
27+
28+
// determine if we even have a client config
29+
func (o DiagnosticsOptions) detectClientConfig() (expected bool, detected bool) {
30+
if o.ClientFlags == nil {
31+
// options for client not provided, so it must not be expected.
32+
return false, false
33+
}
34+
o.Logger().Notice("CED2011", "Determining if client configuration exists for client/cluster diagnostics")
35+
confFlagName := config.OpenShiftConfigFlagName
36+
confFlagValue := o.ClientFlags.Lookup(confFlagName).Value.String()
37+
successfulLoad := false
38+
39+
var foundPath string
40+
rules := config.NewOpenShiftClientConfigLoadingRules()
41+
paths := append([]string{confFlagValue}, rules.Precedence...)
42+
for index, path := range paths {
43+
errmsg := ""
44+
switch index {
45+
case 0:
46+
errmsg = fmt.Sprintf("--%s specified that client config should be at %s\n", confFlagName, path)
47+
case len(paths) - 1: // config in ~/.kube
48+
// no error message indicated if it is not there... user didn't say it would be
49+
default: // can be multiple paths from the env var in theory; all cases should go here
50+
if len(os.Getenv(config.OpenShiftConfigPathEnvVar)) != 0 {
51+
errmsg = fmt.Sprintf("Env var %s specified that client config could be at %s\n", config.OpenShiftConfigPathEnvVar, path)
52+
}
53+
}
54+
55+
if o.canOpenConfigFile(path, errmsg) && len(foundPath) == 0 {
56+
successfulLoad = true
57+
foundPath = path
58+
}
59+
}
60+
if len(foundPath) > 0 {
61+
if len(confFlagValue) > 0 && confFlagValue != foundPath {
62+
// found config but not where --config said
63+
o.Logger().Error("DCli1001", fmt.Sprintf(`
64+
The client configuration file was not found where the --%s flag indicated:
65+
%s
66+
A config file was found at the following location:
67+
%s
68+
If you wish to use this file for client configuration, you can specify it
69+
with the --%[1]s flag, or just not specify the flag.
70+
`, confFlagName, confFlagValue, foundPath))
71+
}
72+
} else { // not found, check for master-generated ones to recommend
73+
if len(confFlagValue) > 0 {
74+
o.Logger().Error("DCli1002", fmt.Sprintf("Did not find config file where --%s=%s indicated", confFlagName, confFlagValue))
75+
}
76+
adminWarningF := `
77+
No client config file was available; however, one exists at
78+
%[2]s
79+
which may have been generated automatically by the master.
80+
If you want to use this config, you should copy it to the
81+
standard location (%[3]s),
82+
or you can set the environment variable %[1]s:
83+
export %[1]s=%[2]s
84+
If not, obtain a config file and place it in the standard
85+
location for use by the client and diagnostics.
86+
`
87+
// look for it in auto-generated locations when not found properly
88+
for _, path := range util.AdminKubeConfigPaths {
89+
msg := fmt.Sprintf("Looking for a possible client config at %s\n", path)
90+
if o.canOpenConfigFile(path, msg) {
91+
o.Logger().Warn("DCli1003", fmt.Sprintf(adminWarningF, config.OpenShiftConfigPathEnvVar, path, config.RecommendedHomeFile))
92+
break
93+
}
94+
}
95+
}
96+
return true, successfulLoad
97+
}
98+
99+
// ----------------------------------------------------------
100+
// Attempt to open file at path as client config
101+
// If there is a problem and errmsg is set, log an error
102+
func (o DiagnosticsOptions) canOpenConfigFile(path string, errmsg string) bool {
103+
var (
104+
file *os.File
105+
err error
106+
)
107+
if len(path) == 0 { // empty param/envvar
108+
return false
109+
} else if file, err = os.Open(path); err == nil {
110+
o.Logger().Debug("DCli1004", fmt.Sprintf("Reading client config at %s", path))
111+
} else if len(errmsg) == 0 {
112+
o.Logger().Debug("DCli1005", fmt.Sprintf("Could not read client config at %s:\n%#v", path, err))
113+
} else if os.IsNotExist(err) {
114+
o.Logger().Debug("DCli1006", errmsg+"but that file does not exist.")
115+
} else if os.IsPermission(err) {
116+
o.Logger().Error("DCli1007", errmsg+"but lack permission to read that file.")
117+
} else {
118+
o.Logger().Error("DCli1008", fmt.Sprintf("%sbut there was an error opening it:\n%#v", errmsg, err))
119+
}
120+
if file == nil {
121+
return false
122+
}
123+
124+
// file is open for reading
125+
defer file.Close()
126+
if buffer, err := ioutil.ReadAll(file); err != nil {
127+
o.Logger().Error("DCli1009", fmt.Sprintf("Unexpected error while reading client config file (%s): %v", path, err))
128+
} else if _, err := clientcmd.Load(buffer); err != nil {
129+
o.Logger().Error("DCli1010", fmt.Sprintf(`
130+
Error reading YAML from client config file (%s):
131+
%v
132+
This file may have been truncated or mis-edited.
133+
Please fix, remove, or obtain a new client config`, file.Name(), err))
134+
} else {
135+
o.Logger().Info("DCli1011", fmt.Sprintf("Successfully read a client config file at '%s'", path))
136+
/* Note, we're not going to use this config file directly.
137+
* Instead, we'll defer to the openshift client code to assimilate
138+
* flags, env vars, and the potential hierarchy of config files
139+
* into an actual configuration that the client uses.
140+
* However, for diagnostic purposes, record the files we find.
141+
*/
142+
return true
143+
}
144+
return false
145+
}

‎pkg/oc/admin/diagnostics/diagnostics.go

+88-157
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"github.com/openshift/origin/pkg/oc/admin/diagnostics/diagnostics/log"
2121
"github.com/openshift/origin/pkg/oc/admin/diagnostics/diagnostics/types"
2222
"github.com/openshift/origin/pkg/oc/admin/diagnostics/options"
23+
"github.com/openshift/origin/pkg/oc/admin/diagnostics/util"
2324
osclientcmd "github.com/openshift/origin/pkg/oc/cli/util/clientcmd"
2425
)
2526

@@ -49,8 +50,8 @@ type DiagnosticsOptions struct {
4950
ClientClusterContext string
5051
// LogOptions determine globally what the user wants to see and how.
5152
LogOptions *log.LoggerOptions
52-
// The Logger is built with the options and should be used for all diagnostic output.
53-
Logger *log.Logger
53+
// The logger is built with the options and should be used for all diagnostic output.
54+
logger *log.Logger
5455
}
5556

5657
const (
@@ -104,7 +105,7 @@ var (
104105
`)
105106
)
106107

107-
// NewCmdDiagnostics is the base command for running any diagnostics.
108+
// NewCmdDiagnostics is the base command for running a standard set of diagnostics with generic options only.
108109
func NewCmdDiagnostics(name string, fullName string, out io.Writer) *cobra.Command {
109110
available := availableDiagnostics()
110111
o := &DiagnosticsOptions{
@@ -117,18 +118,7 @@ func NewCmdDiagnostics(name string, fullName string, out io.Writer) *cobra.Comma
117118
Use: name,
118119
Short: "Diagnose common cluster problems",
119120
Long: fmt.Sprintf(longDescription, fullName),
120-
Run: func(c *cobra.Command, args []string) {
121-
kcmdutil.CheckErr(o.Complete(c, args))
122-
123-
failed, err, warnCount, errorCount := o.RunDiagnostics()
124-
o.Logger.Summary(warnCount, errorCount)
125-
126-
kcmdutil.CheckErr(err)
127-
if failed {
128-
os.Exit(1)
129-
}
130-
131-
},
121+
Run: commandRunFunc(o),
132122
}
133123
cmd.SetOutput(out) // for output re: usage / help
134124
o.bindCommonFlags(cmd.Flags())
@@ -157,18 +147,7 @@ func NewCmdDiagnosticsAll(name string, fullName string, out io.Writer, available
157147
Use: name,
158148
Short: "Diagnose common cluster problems",
159149
Long: fmt.Sprintf(longDescriptionAll, fullName),
160-
Run: func(c *cobra.Command, args []string) {
161-
kcmdutil.CheckErr(o.Complete(c, args))
162-
163-
failed, err, warnCount, errorCount := o.RunDiagnostics()
164-
o.Logger.Summary(warnCount, errorCount)
165-
166-
kcmdutil.CheckErr(err)
167-
if failed {
168-
os.Exit(1)
169-
}
170-
171-
},
150+
Run: commandRunFunc(o),
172151
}
173152
cmd.SetOutput(out) // for output re: usage / help
174153
o.bindCommonFlags(cmd.Flags())
@@ -178,7 +157,7 @@ func NewCmdDiagnosticsAll(name string, fullName string, out io.Writer, available
178157
return cmd
179158
}
180159

181-
// NewCmdDiagnosticsIndividual is a generic subcommand providing a single diagnostic and its flags.
160+
// NewCmdDiagnosticsIndividual is a parameterized subcommand providing a single diagnostic and its flags.
182161
func NewCmdDiagnosticsIndividual(name string, fullName string, out io.Writer, diagnostic types.Diagnostic) *cobra.Command {
183162
o := &DiagnosticsOptions{
184163
RequestedDiagnostics: sets.NewString(diagnostic.Name()),
@@ -187,21 +166,10 @@ func NewCmdDiagnosticsIndividual(name string, fullName string, out io.Writer, di
187166
}
188167

189168
cmd := &cobra.Command{
190-
Use: name,
191-
Short: diagnostic.Description(),
192-
Long: fmt.Sprintf(longDescriptionIndividual, diagnostic.Name(), diagnostic.Description()),
193-
Run: func(c *cobra.Command, args []string) {
194-
kcmdutil.CheckErr(o.Complete(c, args))
195-
196-
failed, err, warnCount, errorCount := o.RunDiagnostics()
197-
o.Logger.Summary(warnCount, errorCount)
198-
199-
kcmdutil.CheckErr(err)
200-
if failed {
201-
os.Exit(1)
202-
}
203-
204-
},
169+
Use: name,
170+
Short: diagnostic.Description(),
171+
Long: fmt.Sprintf(longDescriptionIndividual, diagnostic.Name(), diagnostic.Description()),
172+
Run: commandRunFunc(o),
205173
Aliases: []string{diagnostic.Name()},
206174
}
207175
cmd.SetOutput(out) // for output re: usage / help
@@ -219,6 +187,32 @@ func NewCmdDiagnosticsIndividual(name string, fullName string, out io.Writer, di
219187
return cmd
220188
}
221189

190+
type optionsRunner interface {
191+
Logger() *log.Logger
192+
Complete(*cobra.Command, []string) error
193+
RunDiagnostics() error
194+
}
195+
196+
// returns a shared function that runs when one of these Commands actually executes
197+
func commandRunFunc(o optionsRunner) func(c *cobra.Command, args []string) {
198+
return func(c *cobra.Command, args []string) {
199+
kcmdutil.CheckErr(o.Complete(c, args))
200+
201+
if err := o.RunDiagnostics(); err != nil {
202+
o.Logger().Error("CED3001", fmt.Sprintf("Encountered fatal error while building diagnostics: %s", err.Error()))
203+
}
204+
o.Logger().Summary()
205+
if o.Logger().ErrorsSeen() {
206+
os.Exit(1)
207+
}
208+
}
209+
}
210+
211+
// returns the logger built according to options (must be Complete()ed)
212+
func (o *DiagnosticsOptions) Logger() *log.Logger {
213+
return o.logger
214+
}
215+
222216
// gather a list of all diagnostics that are available to be invoked by the main command
223217
func availableDiagnostics() types.DiagnosticList {
224218
available := availableClientDiagnostics()
@@ -266,7 +260,7 @@ func (o *DiagnosticsOptions) bindRequestedIndividualFlags(flags *flag.FlagSet) {
266260
}
267261
}
268262

269-
// bind flags for parameters on a single diagnostic
263+
// bind flags for parameters from a single diagnostic
270264
func bindIndividualFlags(diag types.ParameterizedDiagnostic, prefix string, flags *flag.FlagSet) {
271265
for _, param := range diag.AvailableParameters() {
272266
name := prefix + param.Name
@@ -286,7 +280,7 @@ func bindIndividualFlags(diag types.ParameterizedDiagnostic, prefix string, flag
286280
// Complete fills in DiagnosticsConfig needed if the command is actually invoked.
287281
func (o *DiagnosticsOptions) Complete(c *cobra.Command, args []string) error {
288282
var err error
289-
o.Logger, err = o.LogOptions.NewLogger()
283+
o.logger, err = o.LogOptions.NewLogger()
290284
if err != nil {
291285
return err
292286
}
@@ -312,130 +306,67 @@ func (o *DiagnosticsOptions) Complete(c *cobra.Command, args []string) error {
312306
return nil
313307
}
314308

315-
// RunDiagnostics builds diagnostics based on the options and executes them, returning a summary. Returns:
316-
// failure ("true" meaning there was not a clean diagnostic result),
317-
// error (raised during construction or execution of diagnostics; may be an aggregate error object),
318-
// number of warnings encountered in diagnostics results,
319-
// number of errors encountered (diagnostic results plus errors previously raised).
320-
func (o DiagnosticsOptions) RunDiagnostics() (bool, error, int, int) {
321-
failed := false
322-
warnings := []error{}
323-
errors := []error{}
324-
diagnostics := []types.Diagnostic{}
325-
326-
func() { // don't trust discovery/build of diagnostics; wrap panic nicely in case of developer error
327-
defer func() {
328-
if r := recover(); r != nil {
329-
failed = true
330-
stack := debug.Stack()
331-
errors = append(errors, fmt.Errorf("While building the diagnostics, a panic was encountered.\nThis is a bug in diagnostics. Error and stack trace follow: \n%v\n%s", r, stack))
332-
}
333-
}()
309+
// RunDiagnostics builds diagnostics based on the options and executes them. Returns:
310+
// error (raised during construction of diagnostics; may be an aggregate error object),
311+
func (o DiagnosticsOptions) RunDiagnostics() error {
312+
diagnostics, failure := o.buildDiagnostics()
313+
if failure != nil {
314+
return failure
315+
}
316+
return util.RunDiagnostics(o.Logger(), diagnostics)
317+
}
334318

335-
// build client/cluster diags if there is a client config for them to use
336-
expected, detected, detectWarnings, detectErrors := o.detectClientConfig() // may log and return problems
337-
for _, warn := range detectWarnings {
338-
warnings = append(warnings, warn)
339-
}
340-
for _, err := range detectErrors {
341-
errors = append(errors, err)
342-
}
343-
if !expected {
344-
// no diagnostic required a client config, nothing to do
345-
} else if !detected {
346-
// there just plain isn't any client config file available
347-
o.Logger.Notice("CED3014", "No client configuration specified; skipping client and cluster diagnostics.")
348-
} else if rawConfig, err := o.buildRawConfig(); err != nil { // client config is totally broken - won't parse etc (problems may have been detected and logged)
349-
o.Logger.Error("CED3015", fmt.Sprintf("Client configuration failed to load; skipping client and cluster diagnostics due to error: %s", err.Error()))
350-
errors = append(errors, err)
351-
} else {
352-
clientDiags, ok, err := o.buildClientDiagnostics(rawConfig)
353-
failed = failed || !ok
354-
if ok {
355-
diagnostics = append(diagnostics, clientDiags...)
356-
}
357-
if err != nil {
358-
errors = append(errors, err)
359-
}
319+
func (o DiagnosticsOptions) buildDiagnostics() (diags []types.Diagnostic, failure error) {
320+
diagnostics := []types.Diagnostic{}
360321

361-
clusterDiags, ok, err := o.buildClusterDiagnostics(rawConfig)
362-
failed = failed || !ok
363-
if ok {
364-
diagnostics = append(diagnostics, clusterDiags...)
365-
}
366-
if err != nil {
367-
errors = append(errors, err)
368-
}
322+
// don't trust discovery/build of diagnostics; wrap panic nicely in case of developer error
323+
defer func() {
324+
if r := recover(); r != nil {
325+
failure = fmt.Errorf("While building the diagnostics, a panic was encountered.\nThis is a bug in diagnostics. Error and stack trace follow: \n%v\n%s", r, debug.Stack())
369326
}
327+
}()
370328

371-
// build host diagnostics if config is available
372-
hostDiags, err := o.buildHostDiagnostics()
329+
// build client/cluster diags if there is a client config for them to use
330+
expected, detected := o.detectClientConfig() // may log and return problems
331+
if !expected {
332+
// no diagnostic required a client config, nothing to do
333+
} else if !detected {
334+
// there just plain isn't any client config file available
335+
o.Logger().Notice("CED3014", "No client configuration specified; skipping client and cluster diagnostics.")
336+
} else if rawConfig, err := o.buildRawConfig(); err != nil { // client config is totally broken - won't parse etc (problems may have been detected and logged)
337+
o.Logger().Error("CED3015", fmt.Sprintf("Client configuration failed to load; skipping client and cluster diagnostics due to error: %s", err.Error()))
338+
} else {
339+
clientDiags, err := o.buildClientDiagnostics(rawConfig)
373340
if err != nil {
374-
errors = append(errors, err)
375-
} else {
376-
diagnostics = append(diagnostics, hostDiags...)
341+
return diagnostics, err
377342
}
343+
diagnostics = append(diagnostics, clientDiags...)
378344

379-
// complete any diagnostics that require it
380-
for _, d := range diagnostics {
381-
if toComplete, ok := d.(types.IncompleteDiagnostic); ok {
382-
if err := toComplete.Complete(o.Logger); err != nil {
383-
errors = append(errors, err)
384-
failed = true
385-
}
386-
}
345+
clusterDiags, err := o.buildClusterDiagnostics(rawConfig)
346+
if err != nil {
347+
return diagnostics, err
387348
}
388-
}()
389-
390-
if failed {
391-
return failed, kutilerrors.NewAggregate(errors), len(warnings), len(errors)
349+
diagnostics = append(diagnostics, clusterDiags...)
392350
}
393351

394-
failed, err, numWarnings, numErrors := o.Run(diagnostics)
395-
numWarnings += len(warnings)
396-
numErrors += len(errors)
397-
return failed, err, numWarnings, numErrors
398-
}
399-
400-
// Run performs the actual execution of diagnostics once they're built.
401-
func (o DiagnosticsOptions) Run(diagnostics []types.Diagnostic) (bool, error, int, int) {
402-
warnCount := 0
403-
errorCount := 0
404-
runCount := 0
405-
for _, diagnostic := range diagnostics {
406-
func() { // wrap diagnostic panic nicely in case of developer error
407-
defer func() {
408-
if r := recover(); r != nil {
409-
errorCount += 1
410-
stack := debug.Stack()
411-
o.Logger.Error("CED3017",
412-
fmt.Sprintf("While running the %s diagnostic, a panic was encountered.\nThis is a bug in diagnostics. Error and stack trace follow: \n%s\n%s",
413-
diagnostic.Name(), fmt.Sprintf("%v", r), stack))
414-
}
415-
}()
416-
417-
if canRun, reason := diagnostic.CanRun(); !canRun {
418-
if reason == nil {
419-
o.Logger.Notice("CED3018", fmt.Sprintf("Skipping diagnostic: %s\nDescription: %s", diagnostic.Name(), diagnostic.Description()))
420-
} else {
421-
o.Logger.Notice("CED3019", fmt.Sprintf("Skipping diagnostic: %s\nDescription: %s\nBecause: %s", diagnostic.Name(), diagnostic.Description(), reason.Error()))
422-
}
423-
return
424-
}
352+
// build host diagnostics if config is available
353+
hostDiags, err := o.buildHostDiagnostics()
354+
if err != nil {
355+
return diagnostics, err
356+
}
357+
diagnostics = append(diagnostics, hostDiags...)
425358

426-
o.Logger.Notice("CED3020", fmt.Sprintf("Running diagnostic: %s\nDescription: %s", diagnostic.Name(), diagnostic.Description()))
427-
r := diagnostic.Check()
428-
for _, entry := range r.Logs() {
429-
o.Logger.LogEntry(entry)
359+
// complete any diagnostics that require it
360+
errors := []error{}
361+
for _, d := range diagnostics {
362+
if toComplete, ok := d.(types.IncompleteDiagnostic); ok {
363+
if err := toComplete.Complete(o.Logger()); err != nil {
364+
errors = append(errors, err)
430365
}
431-
warnCount += len(r.Warnings())
432-
errorCount += len(r.Errors())
433-
runCount += 1
434-
}()
366+
}
435367
}
436-
if runCount == 0 {
437-
o.Logger.Error("CED3016", "Requested diagnostic(s) skipped; nothing to run. See --help and consider setting flags or providing config to enable running.")
438-
return true, nil, 0, 1
368+
if len(errors) > 0 {
369+
return diagnostics, kutilerrors.NewAggregate(errors)
439370
}
440-
return errorCount > 0, nil, warnCount, errorCount
371+
return diagnostics, nil
441372
}

‎pkg/oc/admin/diagnostics/diagnostics/client/config_loading.go

-150
This file was deleted.

‎pkg/oc/admin/diagnostics/diagnostics/log/log.go

+16-6
Original file line numberDiff line numberDiff line change
@@ -93,23 +93,33 @@ var (
9393
)
9494

9595
// Provide a summary at the end
96-
func (l *Logger) Summary(warningsSeen int, errorsSeen int) {
96+
func (l *Logger) Summary() {
9797
l.Notice("DL0001", fmt.Sprintf("Summary of diagnostics execution (version %v):\n", version.Get()))
98-
if warningsSeen > 0 {
99-
l.Notice("DL0002", fmt.Sprintf("Warnings seen: %d", warningsSeen))
98+
if l.warningsSeen > 0 {
99+
l.Notice("DL0002", fmt.Sprintf("Warnings seen: %d", l.warningsSeen))
100100
}
101-
if errorsSeen > 0 {
102-
l.Notice("DL0003", fmt.Sprintf("Errors seen: %d", errorsSeen))
101+
if l.errorsSeen > 0 {
102+
l.Notice("DL0003", fmt.Sprintf("Errors seen: %d", l.errorsSeen))
103103
}
104-
if warningsSeen == 0 && errorsSeen == 0 {
104+
if l.warningsSeen == 0 && l.errorsSeen == 0 {
105105
l.Notice("DL0004", "Completed with no errors or warnings seen.")
106106
}
107107
}
108108

109+
func (l *Logger) ErrorsSeen() bool {
110+
return l.errorsSeen > 0
111+
}
112+
109113
func (l *Logger) LogEntry(entry Entry) {
110114
if l == nil { // if there's no logger, return silently
111115
return
112116
}
117+
if entry.Level == ErrorLevel {
118+
l.errorsSeen++
119+
}
120+
if entry.Level == WarnLevel {
121+
l.warningsSeen++
122+
}
113123
if entry.Level.Level < l.level.Level { // logging level says skip this entry
114124
return
115125
}

‎pkg/oc/admin/diagnostics/host.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ var (
1717
)
1818
)
1919

20-
// availableHostDiagnostics contains the names of host diagnostics that can be executed
20+
// availableHostDiagnostics returns host diagnostics that can be executed
2121
// during a single run of diagnostics. Add more diagnostics to the list as they are defined.
2222
func availableHostDiagnostics() types.DiagnosticList {
2323
return types.DiagnosticList{
@@ -30,7 +30,7 @@ func availableHostDiagnostics() types.DiagnosticList {
3030
}
3131

3232
// buildHostDiagnostics builds host Diagnostic objects based on the host environment.
33-
// Returns the Diagnostics built, and an error if any was encountered during the building of diagnostics.) {
33+
// Returns the Diagnostics built, and an error if any was encountered during the building of diagnostics.
3434
func (o DiagnosticsOptions) buildHostDiagnostics() ([]types.Diagnostic, error) {
3535
requestedDiagnostics := availableHostDiagnostics().Names().Intersection(sets.NewString(o.RequestedDiagnostics.List()...)).List()
3636
if len(requestedDiagnostics) == 0 { // no diagnostics to run here
@@ -47,7 +47,7 @@ func (o DiagnosticsOptions) buildHostDiagnostics() ([]types.Diagnostic, error) {
4747
}
4848

4949
diagnostics := []types.Diagnostic{}
50-
systemdUnits := systemddiags.GetSystemdUnits(o.Logger)
50+
systemdUnits := systemddiags.GetSystemdUnits(o.Logger())
5151
for _, diagnosticName := range requestedDiagnostics {
5252
var d types.Diagnostic
5353
switch diagnosticName {

‎pkg/oc/admin/diagnostics/network_pod.go

+26-45
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,13 @@ package diagnostics
33
import (
44
"fmt"
55
"io"
6-
"os"
76
"runtime/debug"
87

98
"github.com/spf13/cobra"
109
flag "github.com/spf13/pflag"
1110

12-
kutilerrors "k8s.io/apimachinery/pkg/util/errors"
1311
"k8s.io/apimachinery/pkg/util/sets"
1412
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
15-
kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
1613

1714
"github.com/openshift/origin/pkg/oc/admin/diagnostics/diagnostics/log"
1815
networkdiag "github.com/openshift/origin/pkg/oc/admin/diagnostics/diagnostics/networkpod"
@@ -30,7 +27,7 @@ type NetworkPodDiagnosticsOptions struct {
3027
// LogOptions determine globally what the user wants to see and how.
3128
LogOptions *log.LoggerOptions
3229
// The Logger is built with the options and should be used for all diagnostic output.
33-
Logger *log.Logger
30+
logger *log.Logger
3431
}
3532

3633
var longNetworkPodDiagDescription = templates.LongDesc(`
@@ -55,18 +52,7 @@ func NewCommandNetworkPodDiagnostics(name string, out io.Writer) *cobra.Command
5552
Use: name,
5653
Short: "Within a privileged pod, run network diagnostics",
5754
Long: fmt.Sprintf(longNetworkPodDiagDescription),
58-
Run: func(c *cobra.Command, args []string) {
59-
kcmdutil.CheckErr(o.Complete(args))
60-
61-
failed, err, warnCount, errorCount := o.BuildAndRunDiagnostics()
62-
o.Logger.Summary(warnCount, errorCount)
63-
64-
kcmdutil.CheckErr(err)
65-
if failed {
66-
os.Exit(255)
67-
}
68-
69-
},
55+
Run: commandRunFunc(o),
7056
}
7157
cmd.SetOutput(out) // for output re: usage / help
7258

@@ -75,9 +61,14 @@ func NewCommandNetworkPodDiagnostics(name string, out io.Writer) *cobra.Command
7561
return cmd
7662
}
7763

64+
// Logger returns the logger built according to options (must be Complete()ed)
65+
func (o *NetworkPodDiagnosticsOptions) Logger() *log.Logger {
66+
return o.logger
67+
}
68+
7869
// Complete fills in NetworkPodDiagnosticsOptions needed if the command is actually invoked.
79-
func (o *NetworkPodDiagnosticsOptions) Complete(args []string) (err error) {
80-
o.Logger, err = o.LogOptions.NewLogger()
70+
func (o *NetworkPodDiagnosticsOptions) Complete(c *cobra.Command, args []string) (err error) {
71+
o.logger, err = o.LogOptions.NewLogger()
8172
if err != nil {
8273
return err
8374
}
@@ -90,57 +81,47 @@ func (o *NetworkPodDiagnosticsOptions) Complete(args []string) (err error) {
9081
return nil
9182
}
9283

93-
// BuildAndRunDiagnostics builds diagnostics based on the options and executes them, returning a summary.
94-
func (o NetworkPodDiagnosticsOptions) BuildAndRunDiagnostics() (failed bool, err error, numWarnings, numErrors int) {
95-
failed = false
96-
errors := []error{}
84+
// RunDiagnostics builds diagnostics based on the options and executes them, returning a summary.
85+
func (o NetworkPodDiagnosticsOptions) RunDiagnostics() error {
86+
var fatal error
9787
diagnostics := []types.Diagnostic{}
9888

9989
func() { // don't trust discovery/build of diagnostics; wrap panic nicely in case of developer error
10090
defer func() {
10191
if r := recover(); r != nil {
102-
failed = true
103-
stack := debug.Stack()
104-
errors = append(errors, fmt.Errorf("While building the diagnostics, a panic was encountered.\nThis is a bug in diagnostics. Error and stack trace follow: \n%v\n%s", r, stack))
92+
fatal = fmt.Errorf("While building the diagnostics, a panic was encountered.\nThis is a bug in diagnostics. Error and stack trace follow: \n%v\n%s", r, debug.Stack())
10593
}
10694
}() // deferred panic handler
107-
networkPodDiags, ok, err := o.buildNetworkPodDiagnostics()
108-
failed = failed || !ok
109-
if ok {
110-
diagnostics = append(diagnostics, networkPodDiags...)
111-
}
112-
if err != nil {
113-
errors = append(errors, err...)
114-
}
95+
96+
diagnostics, fatal = o.buildNetworkPodDiagnostics()
11597
}()
11698

117-
if failed {
118-
return failed, kutilerrors.NewAggregate(errors), 0, len(errors)
99+
if fatal != nil {
100+
return fatal
119101
}
120102

121-
failed, err, numWarnings, numErrors = util.RunDiagnostics(o.Logger, diagnostics, 0, len(errors))
122-
return failed, err, numWarnings, numErrors
103+
return util.RunDiagnostics(o.Logger(), diagnostics)
123104
}
124105

125106
// buildNetworkPodDiagnostics builds network Diagnostic objects based on the host environment.
126-
// Returns the Diagnostics built, "ok" bool for whether to proceed or abort, and an error if any was encountered during the building of diagnostics.
127-
func (o NetworkPodDiagnosticsOptions) buildNetworkPodDiagnostics() ([]types.Diagnostic, bool, []error) {
107+
// Returns the Diagnostics built or any fatal error encountered during the building of diagnostics.
108+
func (o NetworkPodDiagnosticsOptions) buildNetworkPodDiagnostics() ([]types.Diagnostic, error) {
128109
diagnostics := []types.Diagnostic{}
129-
err, requestedDiagnostics := util.DetermineRequestedDiagnostics(availableNetworkPodDiagnostics.List(), o.RequestedDiagnostics, o.Logger)
110+
err, requestedDiagnostics := util.DetermineRequestedDiagnostics(availableNetworkPodDiagnostics.List(), o.RequestedDiagnostics, o.Logger())
130111
if err != nil {
131-
return diagnostics, false, []error{err} // don't waste time on discovery
112+
return nil, err // don't waste time on discovery
132113
}
133114

134115
clientFlags := flag.NewFlagSet("client", flag.ContinueOnError) // hide the extensive set of client flags
135116
factory := osclientcmd.New(clientFlags) // that would otherwise be added to this command
136117

137118
kubeClient, clientErr := factory.ClientSet()
138119
if clientErr != nil {
139-
return diagnostics, false, []error{clientErr}
120+
return nil, clientErr
140121
}
141122
networkClient, err := factory.OpenshiftInternalNetworkClient()
142123
if err != nil {
143-
return diagnostics, false, []error{err}
124+
return nil, err
144125
}
145126

146127
for _, diagnosticName := range requestedDiagnostics {
@@ -174,9 +155,9 @@ func (o NetworkPodDiagnosticsOptions) buildNetworkPodDiagnostics() ([]types.Diag
174155
})
175156

176157
default:
177-
return diagnostics, false, []error{fmt.Errorf("unknown diagnostic: %v", diagnosticName)}
158+
return diagnostics, fmt.Errorf("unknown diagnostic: %v", diagnosticName)
178159
}
179160
}
180161

181-
return diagnostics, true, nil
162+
return diagnostics, nil
182163
}

‎pkg/oc/admin/diagnostics/pod.go

+24-44
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,12 @@ package diagnostics
33
import (
44
"fmt"
55
"io"
6-
"os"
76
"runtime/debug"
87

98
"github.com/spf13/cobra"
109

11-
kutilerrors "k8s.io/apimachinery/pkg/util/errors"
1210
"k8s.io/apimachinery/pkg/util/sets"
1311
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
14-
kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
1512

1613
"github.com/openshift/origin/pkg/oc/admin/diagnostics/diagnostics/log"
1714
poddiag "github.com/openshift/origin/pkg/oc/admin/diagnostics/diagnostics/pod"
@@ -28,7 +25,12 @@ type PodDiagnosticsOptions struct {
2825
// LogOptions determine globally what the user wants to see and how.
2926
LogOptions *log.LoggerOptions
3027
// The Logger is built with the options and should be used for all diagnostic output.
31-
Logger *log.Logger
28+
logger *log.Logger
29+
}
30+
31+
// returns the logger built according to options (must be Complete()ed)
32+
func (o *PodDiagnosticsOptions) Logger() *log.Logger {
33+
return o.logger
3234
}
3335

3436
const (
@@ -53,18 +55,7 @@ func NewCommandPodDiagnostics(name string, out io.Writer) *cobra.Command {
5355
Use: name,
5456
Short: "Within a pod, run pod diagnostics",
5557
Long: fmt.Sprintf(longPodDiagDescription),
56-
Run: func(c *cobra.Command, args []string) {
57-
kcmdutil.CheckErr(o.Complete(args))
58-
59-
failed, err, warnCount, errorCount := o.BuildAndRunDiagnostics()
60-
o.Logger.Summary(warnCount, errorCount)
61-
62-
kcmdutil.CheckErr(err)
63-
if failed {
64-
os.Exit(255)
65-
}
66-
67-
},
58+
Run: commandRunFunc(o),
6859
}
6960
cmd.SetOutput(out) // for output re: usage / help
7061

@@ -74,9 +65,9 @@ func NewCommandPodDiagnostics(name string, out io.Writer) *cobra.Command {
7465
}
7566

7667
// Complete fills in PodDiagnosticsOptions needed if the command is actually invoked.
77-
func (o *PodDiagnosticsOptions) Complete(args []string) error {
68+
func (o *PodDiagnosticsOptions) Complete(c *cobra.Command, args []string) error {
7869
var err error
79-
o.Logger, err = o.LogOptions.NewLogger()
70+
o.logger, err = o.LogOptions.NewLogger()
8071
if err != nil {
8172
return err
8273
}
@@ -89,37 +80,26 @@ func (o *PodDiagnosticsOptions) Complete(args []string) error {
8980
return nil
9081
}
9182

92-
// BuildAndRunDiagnostics builds diagnostics based on the options and executes them, returning a summary.
93-
func (o PodDiagnosticsOptions) BuildAndRunDiagnostics() (bool, error, int, int) {
94-
failed := false
95-
errors := []error{}
96-
diagnostics := []types.Diagnostic{}
83+
// RunDiagnostics builds diagnostics based on the options and executes them, returning fatal error(s) only.
84+
func (o PodDiagnosticsOptions) RunDiagnostics() error {
85+
var fatal error
86+
var diagnostics []types.Diagnostic
9787

9888
func() { // don't trust discovery/build of diagnostics; wrap panic nicely in case of developer error
9989
defer func() {
10090
if r := recover(); r != nil {
101-
failed = true
102-
stack := debug.Stack()
103-
errors = append(errors, fmt.Errorf("While building the diagnostics, a panic was encountered.\nThis is a bug in diagnostics. Error and stack trace follow: \n%v\n%s", r, stack))
91+
fatal = fmt.Errorf("While building the diagnostics, a panic was encountered.\nThis is a bug in diagnostics. Error and stack trace follow: \n%v\n%s", r, debug.Stack())
10492
}
10593
}() // deferred panic handler
106-
podDiags, ok, err := o.buildPodDiagnostics()
107-
failed = failed || !ok
108-
if ok {
109-
diagnostics = append(diagnostics, podDiags...)
110-
}
111-
if err != nil {
112-
errors = append(errors, err...)
113-
}
11494

95+
diagnostics, fatal = o.buildPodDiagnostics()
11596
}()
11697

117-
if failed {
118-
return failed, kutilerrors.NewAggregate(errors), 0, len(errors)
98+
if fatal != nil {
99+
return fatal
119100
}
120101

121-
failed, err, numWarnings, numErrors := util.RunDiagnostics(o.Logger, diagnostics, 0, len(errors))
122-
return failed, err, numWarnings, numErrors
102+
return util.RunDiagnostics(o.Logger(), diagnostics)
123103
}
124104

125105
var (
@@ -129,12 +109,12 @@ var (
129109
)
130110

131111
// buildPodDiagnostics builds host Diagnostic objects based on the host environment.
132-
// Returns the Diagnostics built, "ok" bool for whether to proceed or abort, and an error if any was encountered during the building of diagnostics.
133-
func (o PodDiagnosticsOptions) buildPodDiagnostics() ([]types.Diagnostic, bool, []error) {
112+
// Returns the Diagnostics built, and any fatal error encountered during the building of diagnostics.
113+
func (o PodDiagnosticsOptions) buildPodDiagnostics() ([]types.Diagnostic, error) {
134114
diagnostics := []types.Diagnostic{}
135-
err, requestedDiagnostics := util.DetermineRequestedDiagnostics(availablePodDiagnostics.List(), o.RequestedDiagnostics, o.Logger)
115+
err, requestedDiagnostics := util.DetermineRequestedDiagnostics(availablePodDiagnostics.List(), o.RequestedDiagnostics, o.Logger())
136116
if err != nil {
137-
return diagnostics, false, []error{err} // don't waste time on discovery
117+
return diagnostics, err // don't waste time on discovery
138118
}
139119
// TODO: check we're actually in a container
140120

@@ -152,9 +132,9 @@ func (o PodDiagnosticsOptions) buildPodDiagnostics() ([]types.Diagnostic, bool,
152132
})
153133

154134
default:
155-
return diagnostics, false, []error{fmt.Errorf("unknown diagnostic: %v", diagnosticName)}
135+
return diagnostics, fmt.Errorf("unknown diagnostic: %v", diagnosticName)
156136
}
157137
}
158138

159-
return diagnostics, true, nil
139+
return diagnostics, nil
160140
}

‎pkg/oc/admin/diagnostics/util/util.go

+8-7
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,15 @@ func DetermineRequestedDiagnostics(available []string, requested []string, logge
3131
}
3232

3333
// RunDiagnostics performs the actual execution of diagnostics once they're built.
34-
func RunDiagnostics(logger *log.Logger, diagnostics []types.Diagnostic, warnCount int, errorCount int) (bool, error, int, int) {
34+
func RunDiagnostics(logger *log.Logger, diagnostics []types.Diagnostic) error {
35+
runCount := 0
3536
for _, diagnostic := range diagnostics {
3637
func() { // wrap diagnostic panic nicely in case of developer error
3738
defer func() {
3839
if r := recover(); r != nil {
39-
errorCount += 1
40-
stack := debug.Stack()
4140
logger.Error("CED7001",
4241
fmt.Sprintf("While running the %s diagnostic, a panic was encountered.\nThis is a bug in diagnostics. Error and stack trace follow: \n%s\n%s",
43-
diagnostic.Name(), fmt.Sprintf("%v", r), stack))
42+
diagnostic.Name(), fmt.Sprintf("%v", r), debug.Stack()))
4443
}
4544
}()
4645

@@ -52,16 +51,18 @@ func RunDiagnostics(logger *log.Logger, diagnostics []types.Diagnostic, warnCoun
5251
}
5352
return
5453
}
54+
runCount += 1
5555

5656
logger.Notice("CED7004", fmt.Sprintf("Running diagnostic: %s\nDescription: %s", diagnostic.Name(), diagnostic.Description()))
5757
r := diagnostic.Check()
5858
for _, entry := range r.Logs() {
5959
logger.LogEntry(entry)
6060
}
61-
warnCount += len(r.Warnings())
62-
errorCount += len(r.Errors())
6361
}()
6462
}
6563

66-
return errorCount > 0, nil, warnCount, errorCount
64+
if runCount == 0 {
65+
return fmt.Errorf("Requested diagnostic(s) skipped; nothing to run. See --help and consider setting flags or providing config to enable running.")
66+
}
67+
return nil
6768
}

0 commit comments

Comments
 (0)
Please sign in to comment.