@@ -33,6 +33,11 @@ import (
33
33
type DiagnosticsOptions struct {
34
34
// list of diagnostic names to limit what is run
35
35
RequestedDiagnostics []string
36
+ // parameters to specify diagnostic-specific options (name => map of params)
37
+ Parameters map [string ][]string
38
+ // after building diagnostics, list their parameters and exit
39
+ ListParameters bool
40
+
36
41
// specify locations of host config files
37
42
MasterConfigLocation string
38
43
NodeConfigLocation string
@@ -102,9 +107,13 @@ var (
102
107
* If a client config file is not found, client and cluster diagnostics
103
108
are skipped.
104
109
105
- Diagnostics may be individually run by passing diagnostic name as arguments.
110
+ Diagnostics may be individually run by passing diagnostic name(s) as arguments.
111
+
112
+ %[1]s DiagnosticName
113
+
114
+ Diagnostic parameters may be given values similarly:
106
115
107
- %[1]s < DiagnosticName>
116
+ %[1]s DiagnosticName.parameter=value
108
117
109
118
The available diagnostic names are: %[2]s.` )
110
119
)
@@ -113,6 +122,7 @@ var (
113
122
func NewCmdDiagnostics (name string , fullName string , out io.Writer ) * cobra.Command {
114
123
o := & DiagnosticsOptions {
115
124
RequestedDiagnostics : []string {},
125
+ Parameters : map [string ][]string {},
116
126
LogOptions : & log.LoggerOptions {Out : out },
117
127
ImageTemplate : variable .NewDefaultImageTemplate (),
118
128
NetworkOptions : & NetworkDiagnosticsOptions {},
@@ -141,6 +151,7 @@ func NewCmdDiagnostics(name string, fullName string, out io.Writer) *cobra.Comma
141
151
142
152
o .ClientFlags = flag .NewFlagSet ("client" , flag .ContinueOnError ) // hide the extensive set of client flags
143
153
o .Factory = osclientcmd .New (o .ClientFlags ) // that would otherwise be added to this command
154
+ cmd .Flags ().BoolVar (& o .ListParameters , options .FlagListParametersName , false , "List parameters for specified diagnostic(s) and exit" )
144
155
cmd .Flags ().AddFlag (o .ClientFlags .Lookup (config .OpenShiftConfigFlagName ))
145
156
cmd .Flags ().AddFlag (o .ClientFlags .Lookup ("context" )) // TODO: find k8s constant
146
157
cmd .Flags ().StringVar (& o .ClientClusterContext , options .FlagClusterContextName , "" , "Client context to use for cluster administrator" )
@@ -182,6 +193,13 @@ func (o *DiagnosticsOptions) Complete(args []string) error {
182
193
}
183
194
}
184
195
196
+ if err = o .completeNetworkOptions (); err != nil {
197
+ return err
198
+ }
199
+ return o .processArgs (args )
200
+ }
201
+
202
+ func (o * DiagnosticsOptions ) completeNetworkOptions () error {
185
203
if len (o .NetworkOptions .LogDir ) == 0 {
186
204
o .NetworkOptions .LogDir = netutil .NetworkDiagDefaultLogDir
187
205
} else {
@@ -208,10 +226,39 @@ func (o *DiagnosticsOptions) Complete(args []string) error {
208
226
if kvalidation .IsValidPortNum (o .NetworkOptions .TestPodPort ) != nil {
209
227
return fmt .Errorf ("invalid port for network diagnostic test pod. Must be in the range 1 to 65535." )
210
228
}
229
+ return nil
230
+ }
211
231
212
- o .RequestedDiagnostics = append (o .RequestedDiagnostics , args ... )
213
- if len (o .RequestedDiagnostics ) == 0 {
214
- o .RequestedDiagnostics = availableDiagnostics ().Difference (defaultSkipDiagnostics ()).List ()
232
+ // Sort out the diagnostic names and any of their parameters.
233
+ // oc adm diagnostics # all standard diagnostics
234
+ // oc adm diagnostics Foo # only Foo
235
+ // oc adm diagnostics Foo Foo Foo # only Foo (and only once)
236
+ // oc adm diagnostics Foo.bar=baz # all standard diagnostics and Foo, with param for Foo
237
+ // oc adm diagnostics Foo Foo.bar=baz # only Foo with param
238
+ // oc adm diagnostics Foo Spam.eggs=ugh # only Foo and Spam with param
239
+ func (o * DiagnosticsOptions ) processArgs (args []string ) error {
240
+ explicitDiagnostics := map [string ]bool {} // names by themselves, not from a parameter
241
+ requestedDiagnostics := map [string ]bool {} // names by themselves or from a parameter
242
+ for _ , arg := range args {
243
+ // if arg includes a . then it is a param; otherwise just a name
244
+ argsplit := strings .SplitN (arg , "." , 2 )
245
+ name := argsplit [0 ]
246
+ if len (argsplit ) == 1 { // name by itself
247
+ explicitDiagnostics [name ] = true
248
+ requestedDiagnostics [name ] = true
249
+ } else { // name.parameter
250
+ requestedDiagnostics [name ] = true // note, not counted as explicit
251
+ o .Parameters [name ] = append (o .Parameters [name ], argsplit [1 ])
252
+ }
253
+ }
254
+ if len (explicitDiagnostics ) == 0 {
255
+ // if none were explicitly given, include all available (except default skipped)
256
+ for _ , name := range availableDiagnostics ().Difference (defaultSkipDiagnostics ()).List () {
257
+ requestedDiagnostics [name ] = true
258
+ }
259
+ }
260
+ for name := range requestedDiagnostics {
261
+ o .RequestedDiagnostics = append (o .RequestedDiagnostics , name )
215
262
}
216
263
217
264
return nil
@@ -320,6 +367,27 @@ func (o DiagnosticsOptions) RunDiagnostics() (bool, error, int, int) {
320
367
if err != nil {
321
368
errors = append (errors , err )
322
369
}
370
+
371
+ // this has to run after the diagnostics are built to know what params they take
372
+ if o .ListParameters {
373
+ paramList := []string {}
374
+ for _ , diag := range diagnostics {
375
+ if pd , ok := diag .(types.ParameterizedDiagnostic ); ok {
376
+ paramList = append (paramList , listDiagnosticParameters (pd )... )
377
+ }
378
+ }
379
+ if len (paramList ) > 0 {
380
+ o .Logger .Info ("CED3021" , strings .Join (paramList , "\n " ))
381
+ } else {
382
+ o .Logger .Info ("CED3022" , "There are no parameters for the selected diagnostics." )
383
+ }
384
+ os .Exit (0 )
385
+ }
386
+ // log parameter errors as user errors and fail
387
+ if err := o .parameterizeDiagnostics (diagnostics ); err != nil {
388
+ failed = true
389
+ o .Logger .Error ("CED3023" , err .Error ())
390
+ }
323
391
}()
324
392
325
393
if failed {
@@ -332,6 +400,63 @@ func (o DiagnosticsOptions) RunDiagnostics() (bool, error, int, int) {
332
400
return failed , err , numWarnings , numErrors
333
401
}
334
402
403
+ func (o DiagnosticsOptions ) parameterizeDiagnostics (diagnostics []types.Diagnostic ) error {
404
+ errors := []string {}
405
+ for _ , diag := range diagnostics {
406
+ if pd , ok := diag .(types.ParameterizedDiagnostic ); ok {
407
+ available := pd .AvailableParameters ()
408
+ paramList := o .Parameters [pd .Name ()]
409
+ paramMap := map [string ]string {}
410
+ diagErrors := []string {}
411
+ for _ , param := range paramList {
412
+ if split := strings .SplitN (param , "=" , 2 ); len (split ) == 2 {
413
+ name := split [0 ]
414
+ if available [name ] == "" {
415
+ diagErrors = append (diagErrors , fmt .Sprintf ("Diagnostic %s has no parameter '%s'." , pd .Name (), name ))
416
+ continue
417
+ }
418
+ paramMap [name ] = split [1 ]
419
+ } else { // Diagnostic.foo with no =value
420
+ diagErrors = append (errors , fmt .Sprintf ("Diagnostic parameter sets no value: %s.%s" , pd .Name (), param ))
421
+ }
422
+
423
+ }
424
+ if len (diagErrors ) > 0 {
425
+ errors = append (errors , diagErrors ... )
426
+ errors = append (errors , listDiagnosticParameters (pd )... )
427
+ continue
428
+ }
429
+
430
+ if err := pd .SetParameters (paramMap ); err != nil {
431
+ errors = append (errors , fmt .Sprintf ("Error setting %s parameters: %v" , pd .Name (), err ))
432
+ errors = append (errors , listDiagnosticParameters (pd )... )
433
+ continue
434
+ }
435
+ } else {
436
+ if o .Parameters [diag .Name ()] != nil {
437
+ errors = append (errors , fmt .Sprintf ("Diagnostic %s accepts no parameters." , diag .Name ()))
438
+ continue
439
+ }
440
+ }
441
+ }
442
+ if len (errors ) == 0 {
443
+ return nil
444
+ }
445
+ return fmt .Errorf ("Error with diagnostic parameters:\n %s" , strings .Join (errors , "\n " ))
446
+ }
447
+
448
+ func listDiagnosticParameters (pd types.ParameterizedDiagnostic ) []string {
449
+ params := pd .AvailableParameters ()
450
+ paramlist := []string {fmt .Sprintf ("Valid parameters for %s are:" , pd .Name ())}
451
+ if params == nil {
452
+ return append (paramlist , " None" )
453
+ }
454
+ for _ , key := range sets .StringKeySet (params ).List () {
455
+ paramlist = append (paramlist , fmt .Sprintf (" %s: %s" , key , params [key ]))
456
+ }
457
+ return paramlist
458
+ }
459
+
335
460
// Run performs the actual execution of diagnostics once they're built.
336
461
func (o DiagnosticsOptions ) Run (diagnostics []types.Diagnostic ) (bool , error , int , int ) {
337
462
warnCount := 0
0 commit comments