Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(inputs.kube_inventory): Support using kubelet to get pods data #13996

Merged
merged 20 commits into from
Oct 4, 2023
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions plugins/inputs/kube_inventory/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ See the [CONFIGURATION.md][CONFIGURATION.md] for more details.
## If empty in-cluster config with POD's service account token will be used.
# url = ""

## URL for the kubelet, if set it will be used to collect the pods resource metrics
# url_kubelet = "http://127.0.0.1:10255"

## Namespace to use. Set to "" to use all namespaces.
# namespace = "default"

Expand Down Expand Up @@ -112,7 +115,6 @@ list "persistentvolumes" and "nodes". You will then need to make an [aggregated
ClusterRole][agg] that will eventually be bound to a user or group.

[rbac]: https://kubernetes.io/docs/reference/access-authn-authz/rbac/

[agg]: https://kubernetes.io/docs/reference/access-authn-authz/rbac/#aggregated-clusterroles

```yaml
Expand Down Expand Up @@ -365,11 +367,11 @@ tls_key = "/run/telegraf-kubernetes-key"

The node status ready can mean 3 different values.

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

### pv `phase_type`

Expand Down
30 changes: 24 additions & 6 deletions plugins/inputs/kube_inventory/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package kube_inventory

import (
"context"
"net/http"
"time"

appsv1 "k8s.io/api/apps/v1"
Expand All @@ -12,6 +13,7 @@ import (
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"

"github.com/influxdata/telegraf/config"
"github.com/influxdata/telegraf/plugins/common/tls"
)

Expand All @@ -22,16 +24,16 @@ type client struct {
}

func newClient(baseURL, namespace, bearerTokenFile string, bearerToken string, timeout time.Duration, tlsConfig tls.ClientConfig) (*client, error) {
var config *rest.Config
var clientConfig *rest.Config
var err error

if baseURL == "" {
config, err = rest.InClusterConfig()
clientConfig, err = rest.InClusterConfig()
if err != nil {
return nil, err
}
} else {
config = &rest.Config{
clientConfig = &rest.Config{
TLSClientConfig: rest.TLSClientConfig{
ServerName: tlsConfig.ServerName,
Insecure: tlsConfig.InsecureSkipVerify,
Expand All @@ -44,13 +46,13 @@ func newClient(baseURL, namespace, bearerTokenFile string, bearerToken string, t
}

if bearerTokenFile != "" {
config.BearerTokenFile = bearerTokenFile
clientConfig.BearerTokenFile = bearerTokenFile
} else if bearerToken != "" {
config.BearerToken = bearerToken
clientConfig.BearerToken = bearerToken
}
}

c, err := kubernetes.NewForConfig(config)
c, err := kubernetes.NewForConfig(clientConfig)
if err != nil {
return nil, err
}
Expand All @@ -62,6 +64,21 @@ func newClient(baseURL, namespace, bearerTokenFile string, bearerToken string, t
}, nil
}

func newHTTPClient(tlsConfig tls.ClientConfig, bearerTokenFile string, responseTimeout config.Duration) (*http.Client, error) {
tlsCfg, err := tlsConfig.TLSConfig()
if err != nil {
return nil, err
}
clientConfig := &rest.Config{
Transport: &http.Transport{
TLSClientConfig: tlsCfg,
},
ContentConfig: rest.ContentConfig{},
Timeout: time.Duration(responseTimeout),
BearerTokenFile: bearerTokenFile,
}
return rest.HTTPClientFor(clientConfig)
}
func (c *client) getDaemonSets(ctx context.Context) (*appsv1.DaemonSetList, error) {
ctx, cancel := context.WithTimeout(ctx, c.timeout)
defer cancel()
Expand Down Expand Up @@ -107,6 +124,7 @@ func (c *client) getPersistentVolumeClaims(ctx context.Context) (*corev1.Persist
func (c *client) getPods(ctx context.Context) (*corev1.PodList, error) {
ctx, cancel := context.WithTimeout(ctx, c.timeout)
defer cancel()

return c.CoreV1().Pods(c.namespace).List(ctx, metav1.ListOptions{})
}

Expand Down
45 changes: 40 additions & 5 deletions plugins/inputs/kube_inventory/kube_inventory.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ package kube_inventory
import (
"context"
_ "embed"
"encoding/json"
"fmt"
"net/http"
"strconv"
"sync"
"time"
Expand All @@ -28,6 +30,7 @@ const (
// KubernetesInventory represents the config object for the plugin.
type KubernetesInventory struct {
URL string `toml:"url"`
KubeletURL string `toml:"url_kubelet"`
BearerToken string `toml:"bearer_token"`
BearerTokenString string `toml:"bearer_token_string" deprecated:"1.24.0;use 'BearerToken' with a file instead"`
Namespace string `toml:"namespace"`
Expand All @@ -36,13 +39,13 @@ type KubernetesInventory struct {
ResourceInclude []string `toml:"resource_include"`
MaxConfigMapAge config.Duration `toml:"max_config_map_age"`

SelectorInclude []string `toml:"selector_include"`
SelectorExclude []string `toml:"selector_exclude"`

Log telegraf.Logger `toml:"-"`
SelectorInclude []string `toml:"selector_include"`
SelectorExclude []string `toml:"selector_exclude"`
Log telegraf.Logger `toml:"-"`

tls.ClientConfig
client *client
client *client
httpClient *http.Client

selectorFilter filter.Filter
}
Expand All @@ -67,7 +70,17 @@ func (ki *KubernetesInventory) Init() error {
if err != nil {
return err
}
if ki.ResponseTimeout < config.Duration(time.Second) {
ki.ResponseTimeout = config.Duration(time.Second * 5)
}
powersj marked this conversation as resolved.
Show resolved Hide resolved
// Only create an http client if we have a kubelet url
if ki.KubeletURL != "" {
ki.httpClient, err = newHTTPClient(ki.ClientConfig, ki.BearerToken, ki.ResponseTimeout)

if err != nil {
ki.Log.Warnf("unable to create http client: %v", err)
}
}
return nil
}

Expand Down Expand Up @@ -140,6 +153,28 @@ func (ki *KubernetesInventory) convertQuantity(s string, m float64) int64 {
}
return int64(f * m)
}
func (ki *KubernetesInventory) queryPodsFromKubelet(url string, v interface{}) error {
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return fmt.Errorf("creating new http request for url %s failed: %w", url, err)
}

req.Header.Add("Accept", "application/json")
resp, err := ki.httpClient.Do(req)
if err != nil {
return fmt.Errorf("error making HTTP request to %q: %w", url, err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("%s returned HTTP status %s", url, resp.Status)
}

if err := json.NewDecoder(resp.Body).Decode(v); err != nil {
return fmt.Errorf("error parsing response: %w", err)
}

return nil
}

func (ki *KubernetesInventory) createSelectorFilters() error {
selectorFilter, err := filter.NewIncludeExcludeFilter(ki.SelectorInclude, ki.SelectorExclude)
Expand Down
13 changes: 11 additions & 2 deletions plugins/inputs/kube_inventory/pod.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package kube_inventory

import (
"context"
"fmt"
"strings"

corev1 "k8s.io/api/core/v1"
Expand All @@ -10,12 +11,20 @@ import (
)

func collectPods(ctx context.Context, acc telegraf.Accumulator, ki *KubernetesInventory) {
list, err := ki.client.getPods(ctx)
var list corev1.PodList
listRef := &list
var err error

if ki.KubeletURL != "" {
err = ki.queryPodsFromKubelet(fmt.Sprintf("%s/pods", ki.KubeletURL), listRef)
} else {
listRef, err = ki.client.getPods(ctx)
}
if err != nil {
acc.AddError(err)
return
}
for _, p := range list.Items {
for _, p := range listRef.Items {
ki.gatherPod(p, acc)
}
}
Expand Down
3 changes: 3 additions & 0 deletions plugins/inputs/kube_inventory/sample.conf
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
## If empty in-cluster config with POD's service account token will be used.
# url = ""

## URL for the kubelet, if set it will be used to collect the pods resource metrics
# url_kubelet = "http://127.0.0.1:10255"

## Namespace to use. Set to "" to use all namespaces.
# namespace = "default"

Expand Down
Loading