Skip to content

Commit

Permalink
[receiver/kubeletstats] Support specifying context for kubeConfig `…
Browse files Browse the repository at this point in the history
…auth_type` (#26665) (#27240)

**Description:**
Support specifying context for `kubeConfig` `auth_type` when
communicating with a Kubernetes cluster.

**Link to tracking Issue:** 26665

**Testing:** unit test added

**Documentation:** kubeletstatsreceiver README updated

Signed-off-by: JACQUES Francois <hypnoce@donarproject.org>
  • Loading branch information
hypnoce authored Oct 6, 2023
1 parent 4796666 commit 484704b
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 34 deletions.
25 changes: 25 additions & 0 deletions .chloggen/kubelet_kubeconfig_context.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
change_type: enhancement

# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
component: kubeletstatsreceiver

# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: Support specifying context for `kubeConfig` `auth_type`

# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
issues: [26665]

# (Optional) One or more lines of additional information to render under the primary note.
# These lines will be padded with 2 spaces and then inserted directly into the document.
# Use pipe (|) for multiline entries.
subtext:

# If your change doesn't affect end users or the exported elements of any package,
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.
# Optional: The change log or logs in which this entry should be included.
# e.g. '[user]' or '[user, api]'
# Include 'user' if the change is relevant to end users.
# Include 'api' if there is a change to a library API.
# Default: '[user]'
change_logs: []
6 changes: 6 additions & 0 deletions internal/k8sconfig/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ type APIConfig struct {
// token provided to the agent pod), or `kubeConfig` to use credentials
// from `~/.kube/config`.
AuthType AuthType `mapstructure:"auth_type"`

// When using auth_type `kubeConfig`, override the current context.
Context string `mapstructure:"context"`
}

// Validate validates the K8s API config
Expand Down Expand Up @@ -85,6 +88,9 @@ func CreateRestConfig(apiConf APIConfig) (*rest.Config, error) {
case AuthTypeKubeConfig:
loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
configOverrides := &clientcmd.ConfigOverrides{}
if apiConf.Context != "" {
configOverrides.CurrentContext = apiConf.Context
}
authConf, err = clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
loadingRules, configOverrides).ClientConfig()

Expand Down
82 changes: 52 additions & 30 deletions internal/kubelet/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,37 +154,59 @@ func TestSAClientBadTLS(t *testing.T) {
}

func TestNewKubeConfigClient(t *testing.T) {
server := httptest.NewUnstartedServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
// Check if call is authenticated using provided kubeconfig
require.Equal(t, req.Header.Get("Authorization"), "Bearer my-token")
require.Equal(t, "/api/v1/nodes/nodename/proxy/", req.URL.EscapedPath())
// Send response to be tested
_, err := rw.Write([]byte(`OK`))
require.NoError(t, err)
}))
server.StartTLS()
defer server.Close()

kubeConfig, err := clientcmd.LoadFromFile("testdata/kubeconfig")
require.NoError(t, err)
kubeConfig.Clusters["my-cluster"].Server = "https://" + server.Listener.Addr().String()
tempKubeConfig := filepath.Join(t.TempDir(), "kubeconfig")
require.NoError(t, clientcmd.WriteToFile(*kubeConfig, tempKubeConfig))
t.Setenv("KUBECONFIG", tempKubeConfig)

p, err := NewClientProvider("nodename", &ClientConfig{
APIConfig: k8sconfig.APIConfig{
AuthType: k8sconfig.AuthTypeKubeConfig,
tests := []struct {
name string
cluster string
context string
}{
{
name: "current context",
cluster: "my-cluster-1",
context: "",
},
InsecureSkipVerify: true,
}, zap.NewNop())
require.NoError(t, err)
require.NotNil(t, p)
client, err := p.BuildClient()
require.NoError(t, err)
resp, err := client.Get("/")
require.NoError(t, err)
require.Equal(t, []byte(`OK`), resp)
{
name: "override context",
cluster: "my-cluster-2",
context: "my-context-2",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
server := httptest.NewUnstartedServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
// Check if call is authenticated using provided kubeconfig
require.Equal(t, req.Header.Get("Authorization"), "Bearer my-token")
require.Equal(t, "/api/v1/nodes/nodename/proxy/", req.URL.EscapedPath())
// Send response to be tested
_, err := rw.Write([]byte(`OK`))
require.NoError(t, err)
}))
server.StartTLS()
defer server.Close()

kubeConfig, err := clientcmd.LoadFromFile("testdata/kubeconfig")
require.NoError(t, err)
kubeConfig.Clusters[tt.cluster].Server = "https://" + server.Listener.Addr().String()
tempKubeConfig := filepath.Join(t.TempDir(), "kubeconfig")
require.NoError(t, clientcmd.WriteToFile(*kubeConfig, tempKubeConfig))
t.Setenv("KUBECONFIG", tempKubeConfig)

p, err := NewClientProvider("nodename", &ClientConfig{
APIConfig: k8sconfig.APIConfig{
AuthType: k8sconfig.AuthTypeKubeConfig,
Context: tt.context,
},
InsecureSkipVerify: true,
}, zap.NewNop())
require.NoError(t, err)
require.NotNil(t, p)
client, err := p.BuildClient()
require.NoError(t, err)
resp, err := client.Get("/")
require.NoError(t, err)
require.Equal(t, []byte(`OK`), resp)
})
}
}

func TestBuildEndpoint(t *testing.T) {
Expand Down
18 changes: 14 additions & 4 deletions internal/kubelet/testdata/kubeconfig
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,24 @@ clusters:
- cluster:
certificate-authority-data: Y2VydA==
server: https://my-cluster.address
name: my-cluster
name: my-cluster-1
- cluster:
certificate-authority-data: Y2VydA==
server: https://my-cluster.address
name: my-cluster-2
contexts:
- context:
cluster: my-cluster
cluster: my-cluster-1
namespace: my-namespace
user: my-user
name: my-context-1
- context:
cluster: my-cluster-2
namespace: my-namespace
user: my-user
name: my-context
current-context: my-context
name: my-context-2

current-context: my-context-1
users:
- name: my-user
user:
Expand Down
2 changes: 2 additions & 0 deletions receiver/kubeletstatsreceiver/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ receivers:
kubeletstats:
collection_interval: 20s
auth_type: "kubeConfig"
context: "my-context"
insecure_skip_verify: true
endpoint: "${env:K8S_NODE_NAME}"
exporters:
Expand All @@ -145,6 +146,7 @@ service:
```
Note that using `auth_type` `kubeConfig`, the endpoint should only be the node name as the communication to the kubelet is proxied by the API server configured in the `kubeConfig`.
`insecure_skip_verify` still applies by overriding the `kubeConfig` settings.
If no `context` is specified, the current context or the default context is used.

### Extra metadata labels

Expand Down

0 comments on commit 484704b

Please sign in to comment.