Skip to content

Commit c8b2423

Browse files
authored
Add ability to add selectors as tags in kube_inventory (influxdata#7267)
1 parent 8c01766 commit c8b2423

File tree

14 files changed

+1269
-34
lines changed

14 files changed

+1269
-34
lines changed

plugins/inputs/kube_inventory/README.md

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@ This plugin generates metrics derived from the state of the following Kubernetes
44

55
- daemonsets
66
- deployments
7+
- endpoints
8+
- ingress
79
- nodes
810
- persistentvolumes
911
- persistentvolumeclaims
1012
- pods (containers)
13+
- services
1114
- statefulsets
1215

1316
Kubernetes is a fast moving project, with a new minor release every 3 months. As
@@ -60,6 +63,12 @@ avoid cardinality issues:
6063
## Overrides resource_exclude if both set.
6164
# resource_include = [ "deployments", "nodes", "statefulsets" ]
6265

66+
## selectors to include and exclude as tags. Globs accepted.
67+
## Note that an empty array for both will include all selectors as tags
68+
## selector_exclude overrides selector_include if both set.
69+
selector_include = []
70+
selector_exclude = ["*"]
71+
6372
## Optional TLS Config
6473
# tls_ca = "/path/to/cafile"
6574
# tls_cert = "/path/to/certfile"
@@ -126,6 +135,7 @@ subjects:
126135
- tags:
127136
- daemonset_name
128137
- namespace
138+
- selector (\*varies)
129139
- fields:
130140
- generation
131141
- current_number_scheduled
@@ -140,6 +150,7 @@ subjects:
140150
- tags:
141151
- deployment_name
142152
- namespace
153+
- selector (\*varies)
143154
- fields:
144155
- replicas_available
145156
- replicas_unavailable
@@ -200,6 +211,7 @@ subjects:
200211
- namespace
201212
- phase
202213
- storageclass
214+
- selector (\*varies)
203215
- fields:
204216
- phase_type (int, [see below](#pvc-phase_type))
205217
@@ -209,6 +221,7 @@ subjects:
209221
- namespace
210222
- node_name
211223
- pod_name
224+
- node_selector (\*varies)
212225
- state
213226
- readiness
214227
- fields:
@@ -229,6 +242,7 @@ subjects:
229242
- port_protocol
230243
- external_name
231244
- cluster_ip
245+
- selector (\*varies)
232246
- fields
233247
- created
234248
- generation
@@ -239,6 +253,7 @@ subjects:
239253
- tags:
240254
- statefulset_name
241255
- namespace
256+
- selector (\*varies)
242257
- fields:
243258
- created
244259
- generation
@@ -277,14 +292,15 @@ The persistentvolumeclaim "phase" is saved in the `phase` tag with a correlated
277292

278293
```
279294
kubernetes_configmap,configmap_name=envoy-config,namespace=default,resource_version=56593031 created=1544103867000000000i 1547597616000000000
280-
kubernetes_daemonset,daemonset_name=telegraf,namespace=logging number_unavailable=0i,desired_number_scheduled=11i,number_available=11i,number_misscheduled=8i,number_ready=11i,updated_number_scheduled=11i,created=1527758699000000000i,generation=16i,current_number_scheduled=11i 1547597616000000000
281-
kubernetes_deployment,deployment_name=deployd,namespace=default replicas_unavailable=0i,created=1544103082000000000i,replicas_available=1i 1547597616000000000
295+
kubernetes_daemonset,daemonset_name=telegraf,selector_select1=s1,namespace=logging number_unavailable=0i,desired_number_scheduled=11i,number_available=11i,number_misscheduled=8i,number_ready=11i,updated_number_scheduled=11i,created=1527758699000000000i,generation=16i,current_number_scheduled=11i 1547597616000000000
296+
kubernetes_deployment,deployment_name=deployd,selector_select1=s1,namespace=default replicas_unavailable=0i,created=1544103082000000000i,replicas_available=1i 1547597616000000000
282297
kubernetes_node,node_name=ip-172-17-0-2.internal allocatable_pods=110i,capacity_memory_bytes=128837533696,capacity_pods=110i,capacity_cpu_cores=16i,allocatable_cpu_cores=16i,allocatable_memory_bytes=128732676096 1547597616000000000
283298
kubernetes_persistentvolume,phase=Released,pv_name=pvc-aaaaaaaa-bbbb-cccc-1111-222222222222,storageclass=ebs-1-retain phase_type=3i 1547597616000000000
284-
kubernetes_persistentvolumeclaim,namespace=default,phase=Bound,pvc_name=data-etcd-0,storageclass=ebs-1-retain phase_type=0i 1547597615000000000
299+
kubernetes_persistentvolumeclaim,namespace=default,phase=Bound,pvc_name=data-etcd-0,selector_select1=s1,storageclass=ebs-1-retain phase_type=0i 1547597615000000000
285300
kubernetes_pod,namespace=default,node_name=ip-172-17-0-2.internal,pod_name=tick1 last_transition_time=1547578322000000000i,ready="false" 1547597616000000000
286-
kubernetes_pod_container,container_name=telegraf,namespace=default,node_name=ip-172-17-0-2.internal,pod_name=tick1,state=running,readiness=ready resource_requests_cpu_units=0.1,resource_limits_memory_bytes=524288000,resource_limits_cpu_units=0.5,restarts_total=0i,state_code=0i,state_reason="",resource_requests_memory_bytes=524288000 1547597616000000000
287-
kubernetes_statefulset,namespace=default,statefulset_name=etcd replicas_updated=3i,spec_replicas=3i,observed_generation=1i,created=1544101669000000000i,generation=1i,replicas=3i,replicas_current=3i,replicas_ready=3i 1547597616000000000
301+
kubernetes_service,cluster_ip=172.29.61.80,namespace=redis-cache-0001,port_name=redis,port_protocol=TCP,selector_app=myapp,selector_io.kompose.service=redis,selector_role=slave,service_name=redis-slave created=1588690034000000000i,generation=0i,port=6379i,target_port=0i 1547597616000000000
302+
kubernetes_pod_container,container_name=telegraf,namespace=default,node_name=ip-172-17-0-2.internal,node_selector_node-role.kubernetes.io/compute=true,pod_name=tick1,state=running,readiness=ready resource_requests_cpu_units=0.1,resource_limits_memory_bytes=524288000,resource_limits_cpu_units=0.5,restarts_total=0i,state_code=0i,state_reason="",resource_requests_memory_bytes=524288000 1547597616000000000
303+
kubernetes_statefulset,namespace=default,selector_select1=s1,statefulset_name=etcd replicas_updated=3i,spec_replicas=3i,observed_generation=1i,created=1544101669000000000i,generation=1i,replicas=3i,replicas_current=3i,replicas_ready=3i 1547597616000000000
288304
```
289305
290306
[metric filtering]: https://github.com/influxdata/telegraf/blob/master/docs/CONFIGURATION.md#metric-filtering

plugins/inputs/kube_inventory/daemonset.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ func (ki *KubernetesInventory) gatherDaemonSet(d v1.DaemonSet, acc telegraf.Accu
3838
"daemonset_name": d.Metadata.GetName(),
3939
"namespace": d.Metadata.GetNamespace(),
4040
}
41+
for key, val := range d.GetSpec().GetSelector().GetMatchLabels() {
42+
if ki.selectorFilter.Match(key) {
43+
tags["selector_"+key] = val
44+
}
45+
}
4146

4247
if d.Metadata.CreationTimestamp.GetSeconds() != 0 {
4348
fields["created"] = time.Unix(d.Metadata.CreationTimestamp.GetSeconds(), int64(d.Metadata.CreationTimestamp.GetNanos())).UnixNano()

plugins/inputs/kube_inventory/daemonset_test.go

Lines changed: 187 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package kube_inventory
22

33
import (
4+
"reflect"
5+
"strings"
46
"testing"
57
"time"
68

@@ -12,6 +14,8 @@ import (
1214

1315
func TestDaemonSet(t *testing.T) {
1416
cli := &client{}
17+
selectInclude := []string{}
18+
selectExclude := []string{}
1519
now := time.Now()
1620
now = time.Date(now.Year(), now.Month(), now.Day(), now.Hour(), 1, 36, 0, now.Location())
1721
tests := []struct {
@@ -55,6 +59,14 @@ func TestDaemonSet(t *testing.T) {
5559
},
5660
CreationTimestamp: &metav1.Time{Seconds: toInt64Ptr(now.Unix())},
5761
},
62+
Spec: &v1.DaemonSetSpec{
63+
Selector: &metav1.LabelSelector{
64+
MatchLabels: map[string]string{
65+
"select1": "s1",
66+
"select2": "s2",
67+
},
68+
},
69+
},
5870
},
5971
},
6072
},
@@ -75,8 +87,10 @@ func TestDaemonSet(t *testing.T) {
7587
"created": now.UnixNano(),
7688
},
7789
Tags: map[string]string{
78-
"daemonset_name": "daemon1",
79-
"namespace": "ns1",
90+
"daemonset_name": "daemon1",
91+
"namespace": "ns1",
92+
"selector_select1": "s1",
93+
"selector_select2": "s2",
8094
},
8195
},
8296
},
@@ -87,8 +101,11 @@ func TestDaemonSet(t *testing.T) {
87101

88102
for _, v := range tests {
89103
ks := &KubernetesInventory{
90-
client: cli,
104+
client: cli,
105+
SelectorInclude: selectInclude,
106+
SelectorExclude: selectExclude,
91107
}
108+
ks.createSelectorFilters()
92109
acc := new(testutil.Accumulator)
93110
for _, dset := range ((v.handler.responseMap["/daemonsets/"]).(*v1.DaemonSetList)).Items {
94111
err := ks.gatherDaemonSet(*dset, acc)
@@ -121,3 +138,170 @@ func TestDaemonSet(t *testing.T) {
121138
}
122139
}
123140
}
141+
142+
func TestDaemonSetSelectorFilter(t *testing.T) {
143+
cli := &client{}
144+
now := time.Now()
145+
now = time.Date(now.Year(), now.Month(), now.Day(), now.Hour(), 1, 36, 0, now.Location())
146+
147+
responseMap := map[string]interface{}{
148+
"/daemonsets/": &v1.DaemonSetList{
149+
Items: []*v1.DaemonSet{
150+
{
151+
Status: &v1.DaemonSetStatus{
152+
CurrentNumberScheduled: toInt32Ptr(3),
153+
DesiredNumberScheduled: toInt32Ptr(5),
154+
NumberAvailable: toInt32Ptr(2),
155+
NumberMisscheduled: toInt32Ptr(2),
156+
NumberReady: toInt32Ptr(1),
157+
NumberUnavailable: toInt32Ptr(1),
158+
UpdatedNumberScheduled: toInt32Ptr(2),
159+
},
160+
Metadata: &metav1.ObjectMeta{
161+
Generation: toInt64Ptr(11221),
162+
Namespace: toStrPtr("ns1"),
163+
Name: toStrPtr("daemon1"),
164+
Labels: map[string]string{
165+
"lab1": "v1",
166+
"lab2": "v2",
167+
},
168+
CreationTimestamp: &metav1.Time{Seconds: toInt64Ptr(now.Unix())},
169+
},
170+
Spec: &v1.DaemonSetSpec{
171+
Selector: &metav1.LabelSelector{
172+
MatchLabels: map[string]string{
173+
"select1": "s1",
174+
"select2": "s2",
175+
},
176+
},
177+
},
178+
},
179+
},
180+
},
181+
}
182+
183+
tests := []struct {
184+
name string
185+
handler *mockHandler
186+
hasError bool
187+
include []string
188+
exclude []string
189+
expected map[string]string
190+
}{
191+
{
192+
name: "nil filters equals all selectors",
193+
handler: &mockHandler{
194+
responseMap: responseMap,
195+
},
196+
hasError: false,
197+
include: nil,
198+
exclude: nil,
199+
expected: map[string]string{
200+
"selector_select1": "s1",
201+
"selector_select2": "s2",
202+
},
203+
},
204+
{
205+
name: "empty filters equals all selectors",
206+
handler: &mockHandler{
207+
responseMap: responseMap,
208+
},
209+
hasError: false,
210+
include: []string{},
211+
exclude: []string{},
212+
expected: map[string]string{
213+
"selector_select1": "s1",
214+
"selector_select2": "s2",
215+
},
216+
},
217+
{
218+
name: "include filter equals only include-matched selectors",
219+
handler: &mockHandler{
220+
responseMap: responseMap,
221+
},
222+
hasError: false,
223+
include: []string{"select1"},
224+
exclude: []string{},
225+
expected: map[string]string{
226+
"selector_select1": "s1",
227+
},
228+
},
229+
{
230+
name: "exclude filter equals only non-excluded selectors (overrides include filter)",
231+
handler: &mockHandler{
232+
responseMap: responseMap,
233+
},
234+
hasError: false,
235+
include: []string{},
236+
exclude: []string{"select2"},
237+
expected: map[string]string{
238+
"selector_select1": "s1",
239+
},
240+
},
241+
{
242+
name: "include glob filter equals only include-matched selectors",
243+
handler: &mockHandler{
244+
responseMap: responseMap,
245+
},
246+
hasError: false,
247+
include: []string{"*1"},
248+
exclude: []string{},
249+
expected: map[string]string{
250+
"selector_select1": "s1",
251+
},
252+
},
253+
{
254+
name: "exclude glob filter equals only non-excluded selectors",
255+
handler: &mockHandler{
256+
responseMap: responseMap,
257+
},
258+
hasError: false,
259+
include: []string{},
260+
exclude: []string{"*2"},
261+
expected: map[string]string{
262+
"selector_select1": "s1",
263+
},
264+
},
265+
{
266+
name: "exclude glob filter equals only non-excluded selectors",
267+
handler: &mockHandler{
268+
responseMap: responseMap,
269+
},
270+
hasError: false,
271+
include: []string{},
272+
exclude: []string{"*2"},
273+
expected: map[string]string{
274+
"selector_select1": "s1",
275+
},
276+
},
277+
}
278+
for _, v := range tests {
279+
ks := &KubernetesInventory{
280+
client: cli,
281+
}
282+
ks.SelectorInclude = v.include
283+
ks.SelectorExclude = v.exclude
284+
ks.createSelectorFilters()
285+
acc := new(testutil.Accumulator)
286+
for _, dset := range ((v.handler.responseMap["/daemonsets/"]).(*v1.DaemonSetList)).Items {
287+
err := ks.gatherDaemonSet(*dset, acc)
288+
if err != nil {
289+
t.Errorf("Failed to gather daemonset - %s", err.Error())
290+
}
291+
}
292+
293+
// Grab selector tags
294+
actual := map[string]string{}
295+
for _, metric := range acc.Metrics {
296+
for key, val := range metric.Tags {
297+
if strings.Contains(key, "selector_") {
298+
actual[key] = val
299+
}
300+
}
301+
}
302+
303+
if !reflect.DeepEqual(v.expected, actual) {
304+
t.Fatalf("actual selector tags (%v) do not match expected selector tags (%v)", actual, v.expected)
305+
}
306+
}
307+
}

plugins/inputs/kube_inventory/deployment.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ func (ki *KubernetesInventory) gatherDeployment(d v1.Deployment, acc telegraf.Ac
3232
"deployment_name": d.Metadata.GetName(),
3333
"namespace": d.Metadata.GetNamespace(),
3434
}
35+
for key, val := range d.GetSpec().GetSelector().GetMatchLabels() {
36+
if ki.selectorFilter.Match(key) {
37+
tags["selector_"+key] = val
38+
}
39+
}
3540

3641
acc.AddFields(deploymentMeasurement, fields, tags)
3742

0 commit comments

Comments
 (0)