Skip to content

Commit f90f70f

Browse files
author
Takashi Kusumi
authored
Add --node flag to filter on a specific node (#243)
1 parent 12a55fa commit f90f70f

File tree

3 files changed

+61
-7
lines changed

3 files changed

+61
-7
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ Supported Kubernetes resources are `pod`, `replicationcontroller`, `service`, `d
8888
`--max-log-requests` | `-1` | Maximum number of concurrent logs to request. Defaults to 50, but 5 when specifying --no-follow
8989
`--namespace`, `-n` | | Kubernetes namespace to use. Default to namespace configured in kubernetes context. To specify multiple namespaces, repeat this or set comma-separated value.
9090
`--no-follow` | `false` | Exit when all logs have been shown.
91+
`--node` | | Node name to filter on.
9192
`--only-log-lines` | `false` | Print only log lines
9293
`--output`, `-o` | `default` | Specify predefined template. Currently support: [default, raw, json, extjson, ppextjson]
9394
`--prompt`, `-p` | `false` | Toggle interactive prompt for selecting 'app.kubernetes.io/instance' label values.

cmd/cmd.go

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ type options struct {
7373
verbosity int
7474
onlyLogLines bool
7575
maxLogRequests int
76+
node string
7677
}
7778

7879
func NewOptions(streams genericclioptions.IOStreams) *options {
@@ -190,12 +191,9 @@ func (o *options) sternConfig() (*stern.Config, error) {
190191
}
191192
}
192193

193-
fieldSelector := fields.Everything()
194-
if o.fieldSelector != "" {
195-
fieldSelector, err = fields.ParseSelector(o.fieldSelector)
196-
if err != nil {
197-
return nil, errors.Wrap(err, "failed to parse selector as field selector")
198-
}
194+
fieldSelector, err := o.generateFieldSelector()
195+
if err != nil {
196+
return nil, err
199197
}
200198

201199
var tailLines *int64
@@ -298,6 +296,7 @@ func (o *options) AddFlags(fs *pflag.FlagSet) {
298296
fs.StringVar(&o.kubeConfig, "kube-config", o.kubeConfig, "Path to kubeconfig file to use.")
299297
_ = fs.MarkDeprecated("kube-config", "Use --kubeconfig instead.")
300298
fs.StringSliceVarP(&o.namespaces, "namespace", "n", o.namespaces, "Kubernetes namespace to use. Default to namespace configured in kubernetes context. To specify multiple namespaces, repeat this or set comma-separated value.")
299+
fs.StringVar(&o.node, "node", o.node, "Node name to filter on.")
301300
fs.IntVar(&o.maxLogRequests, "max-log-requests", o.maxLogRequests, "Maximum number of concurrent logs to request. Defaults to 50, but 5 when specifying --no-follow")
302301
fs.StringVarP(&o.output, "output", "o", o.output, "Specify predefined template. Currently support: [default, raw, json, extjson, ppextjson]")
303302
fs.BoolVarP(&o.prompt, "prompt", "p", o.prompt, "Toggle interactive prompt for selecting 'app.kubernetes.io/instance' label values.")
@@ -432,6 +431,25 @@ func (o *options) generateTemplate() (*template.Template, error) {
432431
return template, err
433432
}
434433

434+
func (o *options) generateFieldSelector() (fields.Selector, error) {
435+
var queries []string
436+
if o.fieldSelector != "" {
437+
queries = append(queries, o.fieldSelector)
438+
}
439+
if o.node != "" {
440+
queries = append(queries, fmt.Sprintf("spec.nodeName=%s", o.node))
441+
}
442+
if len(queries) == 0 {
443+
return fields.Everything(), nil
444+
}
445+
446+
fieldSelector, err := fields.ParseSelector(strings.Join(queries, ","))
447+
if err != nil {
448+
return nil, errors.Wrap(err, "failed to parse selector as field selector")
449+
}
450+
return fieldSelector, nil
451+
}
452+
435453
func NewSternCmd(stream genericclioptions.IOStreams) (*cobra.Command, error) {
436454
o := NewOptions(stream)
437455

cmd/cmd_test.go

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,7 @@ func TestOptionsSternConfig(t *testing.T) {
376376
local, _ := time.LoadLocation("Local")
377377
utc, _ := time.LoadLocation("UTC")
378378
labelSelector, _ := labels.Parse("l=sel")
379-
fieldSelector, _ := fields.ParseSelector("f=field")
379+
fieldSelector, _ := fields.ParseSelector("f=field,spec.nodeName=node1")
380380

381381
re := regexp.MustCompile
382382

@@ -451,6 +451,7 @@ func TestOptionsSternConfig(t *testing.T) {
451451
o.maxLogRequests = 30
452452
o.resource = "res1"
453453
o.onlyLogLines = true
454+
o.node = "node1"
454455

455456
return o
456457
}(),
@@ -484,6 +485,40 @@ func TestOptionsSternConfig(t *testing.T) {
484485
}(),
485486
false,
486487
},
488+
{
489+
"fieldSelector without node",
490+
func() *options {
491+
o := NewOptions(streams)
492+
o.fieldSelector = "f=field"
493+
494+
return o
495+
}(),
496+
func() *stern.Config {
497+
c := defaultConfig()
498+
sel, _ := fields.ParseSelector("f=field")
499+
c.FieldSelector = sel
500+
501+
return c
502+
}(),
503+
false,
504+
},
505+
{
506+
"node without fieldSelector",
507+
func() *options {
508+
o := NewOptions(streams)
509+
o.node = "node1"
510+
511+
return o
512+
}(),
513+
func() *stern.Config {
514+
c := defaultConfig()
515+
sel, _ := fields.ParseSelector("spec.nodeName=node1")
516+
c.FieldSelector = sel
517+
518+
return c
519+
}(),
520+
false,
521+
},
487522
{
488523
"noFollow has the different default",
489524
func() *options {

0 commit comments

Comments
 (0)