Skip to content

Commit 5503e4c

Browse files
[Kubernetes] Check AddResourceMetadata and hints before starting namespace and node watchers (#4618)
* Check addresourcemetadata and hints before starting ns and node watchers * Add changelog. * Update changelog/fragments/1713946896-remove-mandatory-ns-node-permissions.yaml Co-authored-by: Blake Rouse <blake.rouse@elastic.co> --------- Co-authored-by: Blake Rouse <blake.rouse@elastic.co>
1 parent 6cdb8af commit 5503e4c

File tree

5 files changed

+235
-17
lines changed

5 files changed

+235
-17
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Kind can be one of:
2+
# - breaking-change: a change to previously-documented behavior
3+
# - deprecation: functionality that is being removed in a later release
4+
# - bug-fix: fixes a problem in a previous version
5+
# - enhancement: extends functionality but does not break or fix existing behavior
6+
# - feature: new functionality
7+
# - known-issue: problems that we are aware of in a given version
8+
# - security: impacts on the security of a product or a user’s deployment.
9+
# - upgrade: important information for someone upgrading from a prior version
10+
# - other: does not fit into any of the other categories
11+
kind: enhancement
12+
13+
# Change summary; a 80ish characters long description of the change.
14+
summary: Handle the starting of namespace and node watchers for metadata enrichment according to `add_resource_metadata` and hints configuration.
15+
16+
# Long description; in case the summary is not enough to describe the change
17+
# this field accommodate a description without length limits.
18+
# NOTE: This field will be rendered only for breaking-change and known-issue kinds at the moment.
19+
#description:
20+
21+
# Affected component; usually one of "elastic-agent", "fleet-server", "filebeat", "metricbeat", "auditbeat", "all", etc.
22+
component: elastic-agent
23+
24+
# PR URL; optional; the PR number that added the changeset.
25+
# If not present is automatically filled by the tooling finding the PR where this changelog fragment has been added.
26+
# NOTE: the tooling supports backports, so it's able to fill the original PR number instead of the backport PR number.
27+
# Please provide it if you are adding a fragment for a different PR.
28+
pr: https://github.com/elastic/elastic-agent/pull/4618
29+
30+
# Issue URL; optional; the GitHub issue related to this changeset (either closes or is part of).
31+
# If not present is automatically filled by the tooling with the issue linked to the PR number.
32+
#issue: https://github.com/owner/repo/1234

internal/pkg/composable/providers/kubernetes/pod.go

+14-9
Original file line numberDiff line numberDiff line change
@@ -74,23 +74,28 @@ func NewPodEventer(
7474
return nil, errors.New(err, "couldn't create kubernetes watcher")
7575
}
7676

77-
var replicaSetWatcher, jobWatcher kubernetes.Watcher
77+
var replicaSetWatcher, jobWatcher, namespaceWatcher, nodeWatcher kubernetes.Watcher
7878

7979
options := kubernetes.WatchOptions{
8080
SyncTimeout: cfg.SyncPeriod,
8181
Node: cfg.Node,
8282
}
8383
metaConf := cfg.AddResourceMetadata
8484

85-
nodeWatcher, err := kubernetes.NewNamedWatcher("agent-node", client, &kubernetes.Node{}, options, nil)
86-
if err != nil {
87-
logger.Errorf("couldn't create watcher for %T due to error %+v", &kubernetes.Node{}, err)
85+
if metaConf.Node.Enabled() || cfg.Hints.Enabled {
86+
nodeWatcher, err = kubernetes.NewNamedWatcher("agent-node", client, &kubernetes.Node{}, options, nil)
87+
if err != nil {
88+
logger.Errorf("couldn't create watcher for %T due to error %+v", &kubernetes.Node{}, err)
89+
}
8890
}
89-
namespaceWatcher, err := kubernetes.NewNamedWatcher("agent-namespace", client, &kubernetes.Namespace{}, kubernetes.WatchOptions{
90-
SyncTimeout: cfg.SyncPeriod,
91-
}, nil)
92-
if err != nil {
93-
logger.Errorf("couldn't create watcher for %T due to error %+v", &kubernetes.Namespace{}, err)
91+
92+
if metaConf.Namespace.Enabled() || cfg.Hints.Enabled {
93+
namespaceWatcher, err = kubernetes.NewNamedWatcher("agent-namespace", client, &kubernetes.Namespace{}, kubernetes.WatchOptions{
94+
SyncTimeout: cfg.SyncPeriod,
95+
}, nil)
96+
if err != nil {
97+
logger.Errorf("couldn't create watcher for %T due to error %+v", &kubernetes.Namespace{}, err)
98+
}
9499
}
95100

96101
// Resource is Pod so we need to create watchers for Replicasets and Jobs that it might belong to

internal/pkg/composable/providers/kubernetes/pod_test.go

+89
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,20 @@ import (
1212
v1 "k8s.io/api/core/v1"
1313
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1414
"k8s.io/apimachinery/pkg/types"
15+
k8sfake "k8s.io/client-go/kubernetes/fake"
1516

1617
"github.com/stretchr/testify/assert"
1718

1819
"github.com/elastic/elastic-agent-libs/logp"
20+
1921
"github.com/elastic/elastic-agent/pkg/core/logger"
2022

2123
"github.com/elastic/elastic-agent-autodiscover/kubernetes"
2224
"github.com/elastic/elastic-agent-autodiscover/kubernetes/metadata"
2325
"github.com/elastic/elastic-agent-libs/mapstr"
26+
27+
c "github.com/elastic/elastic-agent-libs/config"
28+
2429
"github.com/elastic/elastic-agent/internal/pkg/config"
2530
)
2631

@@ -370,6 +375,90 @@ func TestEphemeralContainers(t *testing.T) {
370375

371376
}
372377

378+
func TestPodEventer_Namespace_Node_Watcher(t *testing.T) {
379+
client := k8sfake.NewSimpleClientset()
380+
381+
log, err := logger.New("service-eventer-test", true)
382+
assert.NoError(t, err)
383+
384+
providerDataChan := make(chan providerData, 1)
385+
386+
comm := MockDynamicComm{
387+
context.TODO(),
388+
providerDataChan,
389+
}
390+
391+
tests := []struct {
392+
namespaceEnabled bool
393+
nodeEnabled bool
394+
hintsEnabled bool
395+
expectedNil bool
396+
name string
397+
msg string
398+
}{
399+
{
400+
namespaceEnabled: false,
401+
nodeEnabled: false,
402+
hintsEnabled: false,
403+
expectedNil: true,
404+
name: "add_resource_metadata.namespace and add_resource_metadata.node disabled and hints disabled.",
405+
msg: "Watcher should be nil.",
406+
},
407+
{
408+
namespaceEnabled: false,
409+
nodeEnabled: false,
410+
hintsEnabled: true,
411+
expectedNil: false,
412+
name: "add_resource_metadata.namespace and add_resource_metadata.node disabled and hints enabled.",
413+
msg: "Watcher should not be nil.",
414+
},
415+
{
416+
namespaceEnabled: true,
417+
nodeEnabled: true,
418+
hintsEnabled: false,
419+
expectedNil: false,
420+
name: "add_resource_metadata.namespace and add_resource_metadata.node enabled and hints disabled.",
421+
msg: "Watcher should not be nil.",
422+
},
423+
}
424+
425+
for _, test := range tests {
426+
t.Run(test.name, func(t *testing.T) {
427+
var cfg Config
428+
cfg.InitDefaults()
429+
430+
nsCfg, err := c.NewConfigFrom(map[string]interface{}{
431+
"enabled": test.namespaceEnabled,
432+
})
433+
assert.NoError(t, err)
434+
nodeCfg, err := c.NewConfigFrom(map[string]interface{}{
435+
"enabled": test.nodeEnabled,
436+
})
437+
assert.NoError(t, err)
438+
439+
cfg.AddResourceMetadata.Namespace = nsCfg
440+
cfg.AddResourceMetadata.Node = nodeCfg
441+
cfg.Hints.Enabled = test.hintsEnabled
442+
443+
eventer, err := NewPodEventer(&comm, &cfg, log, client, "cluster", false)
444+
if err != nil {
445+
t.Fatal(err)
446+
}
447+
448+
namespaceWatcher := eventer.(*pod).namespaceWatcher
449+
nodeWatcher := eventer.(*pod).nodeWatcher
450+
451+
if test.expectedNil {
452+
assert.Equalf(t, nil, namespaceWatcher, "Namespace "+test.msg)
453+
assert.Equalf(t, nil, nodeWatcher, "Node "+test.msg)
454+
} else {
455+
assert.NotEqualf(t, nil, namespaceWatcher, "Namespace "+test.msg)
456+
assert.NotEqualf(t, nil, nodeWatcher, "Node "+test.msg)
457+
}
458+
})
459+
}
460+
}
461+
373462
// MockDynamicComm is used in tests.
374463
type MockDynamicComm struct {
375464
context.Context

internal/pkg/composable/providers/kubernetes/service.go

+15-8
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"github.com/elastic/elastic-agent-libs/logp"
1717
"github.com/elastic/elastic-agent-libs/mapstr"
1818
"github.com/elastic/elastic-agent-libs/safemapstr"
19+
1920
"github.com/elastic/elastic-agent/internal/pkg/agent/errors"
2021
"github.com/elastic/elastic-agent/internal/pkg/composable"
2122
)
@@ -54,15 +55,21 @@ func NewServiceEventer(
5455
return nil, errors.New(err, "couldn't create kubernetes watcher")
5556
}
5657

57-
metaConf := metadata.GetDefaultResourceMetadataConfig()
58-
namespaceWatcher, err := kubernetes.NewNamedWatcher("agent-namespace", client, &kubernetes.Namespace{}, kubernetes.WatchOptions{
59-
SyncTimeout: cfg.SyncPeriod,
60-
Namespace: cfg.Namespace,
61-
}, nil)
62-
if err != nil {
63-
return nil, fmt.Errorf("couldn't create watcher for %T due to error %w", &kubernetes.Namespace{}, err)
58+
metaConf := cfg.AddResourceMetadata
59+
60+
var namespaceMeta metadata.MetaGen
61+
var namespaceWatcher kubernetes.Watcher
62+
63+
if metaConf.Namespace.Enabled() || cfg.Hints.Enabled {
64+
namespaceWatcher, err = kubernetes.NewNamedWatcher("agent-namespace", client, &kubernetes.Namespace{}, kubernetes.WatchOptions{
65+
SyncTimeout: cfg.SyncPeriod,
66+
Namespace: cfg.Namespace,
67+
}, nil)
68+
if err != nil {
69+
return nil, fmt.Errorf("couldn't create watcher for %T due to error %w", &kubernetes.Namespace{}, err)
70+
}
71+
namespaceMeta = metadata.NewNamespaceMetadataGenerator(metaConf.Namespace, namespaceWatcher.Store(), client)
6472
}
65-
namespaceMeta := metadata.NewNamespaceMetadataGenerator(metaConf.Namespace, namespaceWatcher.Store(), client)
6673

6774
rawConfig, err := config.NewConfigFrom(cfg)
6875
if err != nil {

internal/pkg/composable/providers/kubernetes/service_test.go

+85
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,13 @@
55
package kubernetes
66

77
import (
8+
"context"
89
"testing"
910

1011
"github.com/stretchr/testify/assert"
12+
k8sfake "k8s.io/client-go/kubernetes/fake"
13+
14+
"github.com/elastic/elastic-agent-libs/config"
1115

1216
"github.com/elastic/elastic-agent-autodiscover/kubernetes"
1317
"github.com/elastic/elastic-agent-autodiscover/kubernetes/metadata"
@@ -16,6 +20,8 @@ import (
1620
v1 "k8s.io/api/core/v1"
1721
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1822
"k8s.io/apimachinery/pkg/types"
23+
24+
"github.com/elastic/elastic-agent/pkg/core/logger"
1925
)
2026

2127
func TestGenerateServiceData(t *testing.T) {
@@ -104,6 +110,85 @@ func TestGenerateServiceData(t *testing.T) {
104110
}
105111
}
106112

113+
func TestServiceEventer_NamespaceWatcher(t *testing.T) {
114+
client := k8sfake.NewSimpleClientset()
115+
116+
log, err := logger.New("service-eventer-test", true)
117+
assert.NoError(t, err)
118+
119+
providerDataChan := make(chan providerData, 1)
120+
121+
comm := MockDynamicComm{
122+
context.TODO(),
123+
providerDataChan,
124+
}
125+
126+
tests := []struct {
127+
namespaceEnabled bool
128+
hintsEnabled bool
129+
expectedNil bool
130+
name string
131+
msg string
132+
}{
133+
{
134+
namespaceEnabled: false,
135+
hintsEnabled: false,
136+
expectedNil: true,
137+
name: "add_resource_metadata.namespace disabled and hints disabled.",
138+
msg: "Namespace watcher should be nil.",
139+
},
140+
{
141+
namespaceEnabled: false,
142+
hintsEnabled: true,
143+
expectedNil: false,
144+
name: "add_resource_metadata.namespace disabled and hints enabled.",
145+
msg: "Namespace watcher should not be nil.",
146+
},
147+
{
148+
namespaceEnabled: true,
149+
hintsEnabled: false,
150+
expectedNil: false,
151+
name: "add_resource_metadata.namespace enabled and hints disabled.",
152+
msg: "Namespace watcher should not be nil.",
153+
},
154+
{
155+
namespaceEnabled: true,
156+
hintsEnabled: false,
157+
expectedNil: false,
158+
name: "add_resource_metadata default and hints default.",
159+
msg: "Watcher should not be nil.",
160+
},
161+
}
162+
163+
for _, test := range tests {
164+
t.Run(test.name, func(t *testing.T) {
165+
var cfg Config
166+
cfg.InitDefaults()
167+
168+
nsCfg, err := config.NewConfigFrom(map[string]interface{}{
169+
"enabled": test.namespaceEnabled,
170+
})
171+
assert.NoError(t, err)
172+
173+
cfg.AddResourceMetadata.Namespace = nsCfg
174+
cfg.Hints.Enabled = test.hintsEnabled
175+
176+
eventer, err := NewServiceEventer(&comm, &cfg, log, client, "cluster", false)
177+
if err != nil {
178+
t.Fatal(err)
179+
}
180+
181+
namespaceWatcher := eventer.(*service).namespaceWatcher
182+
183+
if test.expectedNil {
184+
assert.Equalf(t, nil, namespaceWatcher, test.msg)
185+
} else {
186+
assert.NotEqualf(t, nil, namespaceWatcher, test.msg)
187+
}
188+
})
189+
}
190+
}
191+
107192
type svcMeta struct{}
108193

109194
// Generate generates svc metadata from a resource object

0 commit comments

Comments
 (0)