Skip to content

Commit

Permalink
feat(inputs.kubernetes): Extend kube_inventory plugin to include and …
Browse files Browse the repository at this point in the history
…extend resourcequota, secrets, node, and pod measurement (#13040)
  • Loading branch information
varunjain0606 authored May 19, 2023
1 parent ad4df21 commit 1b74a25
Show file tree
Hide file tree
Showing 12 changed files with 773 additions and 15 deletions.
59 changes: 58 additions & 1 deletion plugins/inputs/kube_inventory/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Kubernetes resources:
- pods (containers)
- services
- statefulsets
- resourcequotas

Kubernetes is a fast moving project, with a new minor release every 3 months.
As such, we will aim to maintain support only for versions that are supported
Expand Down Expand Up @@ -240,6 +241,9 @@ tls_key = "/run/telegraf-kubernetes-key"
- kubernetes_node
- tags:
- node_name
- status
- condition
- cluster_namespace
- fields:
- capacity_cpu_cores
- capacity_millicpu_cores
Expand All @@ -249,6 +253,9 @@ tls_key = "/run/telegraf-kubernetes-key"
- allocatable_millicpu_cores
- allocatable_memory_bytes
- allocatable_pods
- status_condition
- spec_unschedulable
- node_count

- kubernetes_persistentvolume
- tags:
Expand Down Expand Up @@ -278,6 +285,7 @@ tls_key = "/run/telegraf-kubernetes-key"
- phase
- state
- readiness
- condition
- fields:
- restarts_total
- state_code
Expand All @@ -288,6 +296,7 @@ tls_key = "/run/telegraf-kubernetes-key"
- resource_requests_memory_bytes
- resource_limits_millicpu_units
- resource_limits_memory_bytes
- status_condition

- kubernetes_service
- tags:
Expand Down Expand Up @@ -319,6 +328,49 @@ tls_key = "/run/telegraf-kubernetes-key"
- spec_replicas
- observed_generation

- kubernetes_resourcequota
- tags:
- resource
- namespace
- fields:
- hard_cpu_limits
- hard_cpu_requests
- hard_memory_limit
- hard_memory_requests
- hard_pods
- used_cpu_limits
- used_cpu_requests
- used_memory_limits
- used_memory_requests
- used_pods

- kubernetes_certificate
- tags:
- common_name
- signature_algorithm
- public_key_algorithm
- issuer_common_name
- san
- verification
- name
- namespace
- fields:
- age
- expiry
- startdate
- enddate
- verification_code

### kuberntes node status `status`

The node status ready can mean 3 different values.

| Tag value | Corresponding field value | Meaning |
| --------- | ------------------------- | -------
| ready | 0 | NotReady|
| ready | 1 | Ready |
| ready | 2 | Unknown |

### pv `phase_type`

The persistentvolume "phase" is saved in the `phase` tag with a correlated
Expand Down Expand Up @@ -351,11 +403,16 @@ numeric field called `phase_type` corresponding with that tag value.
kubernetes_configmap,configmap_name=envoy-config,namespace=default,resource_version=56593031 created=1544103867000000000i 1547597616000000000
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
kubernetes_deployment,deployment_name=deployd,selector_select1=s1,namespace=default replicas_unavailable=0i,created=1544103082000000000i,replicas_available=1i 1547597616000000000
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
kubernetes_node,host=vjain node_count=8i 1628918652000000000
kubernetes_node,condition=Ready,host=vjain,node_name=ip-172-17-0-2.internal,status=True status_condition=1i 1629177980000000000
kubernetes_node,cluster_namespace=tools,condition=Ready,host=vjain,node_name=ip-172-17-0-2.internal,status=True allocatable_cpu_cores=4i,allocatable_memory_bytes=7186567168i,allocatable_millicpu_cores=4000i,allocatable_pods=110i,capacity_cpu_cores=4i,capacity_memory_bytes=7291424768i,capacity_millicpu_cores=4000i,capacity_pods=110i,spec_unschedulable=0i,status_condition=1i 1628918652000000000
kubernetes_resourcequota,host=vjain,namespace=default,resource=pods-high hard_cpu=1000i,hard_memory=214748364800i,hard_pods=10i,used_cpu=0i,used_memory=0i,used_pods=0i 1629110393000000000
kubernetes_resourcequota,host=vjain,namespace=default,resource=pods-low hard_cpu=5i,hard_memory=10737418240i,hard_pods=10i,used_cpu=0i,used_memory=0i,used_pods=0i 1629110393000000000
kubernetes_persistentvolume,phase=Released,pv_name=pvc-aaaaaaaa-bbbb-cccc-1111-222222222222,storageclass=ebs-1-retain phase_type=3i 1547597616000000000
kubernetes_persistentvolumeclaim,namespace=default,phase=Bound,pvc_name=data-etcd-0,selector_select1=s1,storageclass=ebs-1-retain phase_type=0i 1547597615000000000
kubernetes_pod,namespace=default,node_name=ip-172-17-0-2.internal,pod_name=tick1 last_transition_time=1547578322000000000i,ready="false" 1547597616000000000
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
kubernetes_pod_container,condition=Ready,host=vjain,pod_name=uefi-5997f76f69-xzljt,status=True status_condition=1i 1629177981000000000
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,phase=Running,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="",phase_reason="",resource_requests_memory_bytes=524288000 1547597616000000000
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
```
Expand Down
94 changes: 94 additions & 0 deletions plugins/inputs/kube_inventory/certificate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package kube_inventory

import (
"context"
"crypto/x509"
"encoding/pem"
"strings"
"time"

corev1 "k8s.io/api/core/v1"

"github.com/influxdata/telegraf"
)

func collectSecrets(ctx context.Context, acc telegraf.Accumulator, ki *KubernetesInventory) {
list, err := ki.client.getTLSSecrets(ctx)
if err != nil {
acc.AddError(err)
return
}
for _, i := range list.Items {
ki.gatherCertificates(i, acc)
}
}

func getFields(cert *x509.Certificate, now time.Time) map[string]interface{} {
age := int(now.Sub(cert.NotBefore).Seconds())
expiry := int(cert.NotAfter.Sub(now).Seconds())
startdate := cert.NotBefore.Unix()
enddate := cert.NotAfter.Unix()

fields := map[string]interface{}{
"age": age,
"expiry": expiry,
"startdate": startdate,
"enddate": enddate,
}

return fields
}

func getTags(cert *x509.Certificate) map[string]string {
tags := map[string]string{
"common_name": cert.Subject.CommonName,
"signature_algorithm": cert.SignatureAlgorithm.String(),
"public_key_algorithm": cert.PublicKeyAlgorithm.String(),
}
tags["issuer_common_name"] = cert.Issuer.CommonName

san := append(cert.DNSNames, cert.EmailAddresses...)
for _, ip := range cert.IPAddresses {
san = append(san, ip.String())
}
for _, uri := range cert.URIs {
san = append(san, uri.String())
}
tags["san"] = strings.Join(san, ",")

return tags
}

func (ki *KubernetesInventory) gatherCertificates(r corev1.Secret, acc telegraf.Accumulator) {
now := time.Now()

for resourceName, val := range r.Data {
if resourceName != "tls.crt" {
continue
}
block, _ := pem.Decode(val)
if block == nil {
return
}
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return
}
fields := getFields(cert, now)
tags := getTags(cert)
tags["name"] = r.Name
tags["namespace"] = r.Namespace
opts := x509.VerifyOptions{
KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny},
}
_, err = cert.Verify(opts)
if err == nil {
tags["verification"] = "valid"
fields["verification_code"] = 0
} else {
tags["verification"] = "invalid"
fields["verification_code"] = 1
}
acc.AddFields(certificateMeasurement, fields, tags)
}
}
16 changes: 16 additions & 0 deletions plugins/inputs/kube_inventory/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
corev1 "k8s.io/api/core/v1"
netv1 "k8s.io/api/networking/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"

Expand Down Expand Up @@ -120,3 +121,18 @@ func (c *client) getStatefulSets(ctx context.Context) (*appsv1.StatefulSetList,
defer cancel()
return c.AppsV1().StatefulSets(c.namespace).List(ctx, metav1.ListOptions{})
}

func (c *client) getResourceQuotas(ctx context.Context) (*corev1.ResourceQuotaList, error) {
ctx, cancel := context.WithTimeout(ctx, c.timeout)
defer cancel()
return c.CoreV1().ResourceQuotas(c.namespace).List(ctx, metav1.ListOptions{})
}

func (c *client) getTLSSecrets(ctx context.Context) (*corev1.SecretList, error) {
ctx, cancel := context.WithTimeout(ctx, c.timeout)
defer cancel()
labelSelector := metav1.LabelSelector{MatchLabels: map[string]string{"type": "kubernetes.io/tls"}}
return c.CoreV1().Secrets(c.namespace).List(ctx, metav1.ListOptions{
FieldSelector: labels.Set(labelSelector.MatchLabels).String(),
})
}
4 changes: 4 additions & 0 deletions plugins/inputs/kube_inventory/kube_inventory.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ var availableCollectors = map[string]func(ctx context.Context, acc telegraf.Accu
"statefulsets": collectStatefulSets,
"persistentvolumes": collectPersistentVolumes,
"persistentvolumeclaims": collectPersistentVolumeClaims,
"resourcequotas": collectResourceQuotas,
"secrets": collectSecrets,
}

func atoi(s string) int64 {
Expand Down Expand Up @@ -159,6 +161,8 @@ var (
podContainerMeasurement = "kubernetes_pod_container"
serviceMeasurement = "kubernetes_service"
statefulSetMeasurement = "kubernetes_statefulset"
resourcequotaMeasurement = "kubernetes_resourcequota"
certificateMeasurement = "kubernetes_certificate"
)

func init() {
Expand Down
47 changes: 46 additions & 1 deletion plugins/inputs/kube_inventory/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,27 @@ func collectNodes(ctx context.Context, acc telegraf.Accumulator, ki *KubernetesI
acc.AddError(err)
return
}

ki.gatherNodeCount(len(list.Items), acc)

for _, n := range list.Items {
ki.gatherNode(n, acc)
}
}

func (ki *KubernetesInventory) gatherNodeCount(count int, acc telegraf.Accumulator) {
fields := map[string]interface{}{"node_count": count}
tags := map[string]string{}

acc.AddFields(nodeMeasurement, fields, tags)
}

func (ki *KubernetesInventory) gatherNode(n corev1.Node, acc telegraf.Accumulator) {
fields := map[string]interface{}{}
tags := map[string]string{
"node_name": n.Name,
"node_name": n.Name,
"cluster_namespace": n.Annotations["cluster.x-k8s.io/cluster-namespace"],
"version": n.Status.NodeInfo.KubeletVersion,
}

for resourceName, val := range n.Status.Capacity {
Expand All @@ -49,5 +61,38 @@ func (ki *KubernetesInventory) gatherNode(n corev1.Node, acc telegraf.Accumulato
}
}

for _, val := range n.Status.Conditions {
conditionfields := map[string]interface{}{}
conditiontags := map[string]string{
"status": string(val.Status),
"condition": string(val.Type),
}
for k, v := range tags {
conditiontags[k] = v
}
running := 0
nodeready := 0
if val.Status == "True" {
if val.Type == "Ready" {
nodeready = 1
}
running = 1
} else if val.Status == "Unknown" {
if val.Type == "Ready" {
nodeready = 0
}
running = 2
}
conditionfields["status_condition"] = running
conditionfields["ready"] = nodeready
acc.AddFields(nodeMeasurement, conditionfields, conditiontags)
}

unschedulable := 0
if n.Spec.Unschedulable {
unschedulable = 1
}
fields["spec_unschedulable"] = unschedulable

acc.AddFields(nodeMeasurement, fields, tags)
}
Loading

0 comments on commit 1b74a25

Please sign in to comment.