Skip to content
Draft
Show file tree
Hide file tree
Changes from all 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
2 changes: 2 additions & 0 deletions cmd/kubectl-datadog/autoscaling/cluster/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

"github.com/DataDog/datadog-operator/cmd/kubectl-datadog/autoscaling/cluster/install"
"github.com/DataDog/datadog-operator/cmd/kubectl-datadog/autoscaling/cluster/uninstall"
"github.com/DataDog/datadog-operator/cmd/kubectl-datadog/autoscaling/cluster/upgrade"
)

// options provides information required by cluster command
Expand All @@ -33,6 +34,7 @@ func New(streams genericclioptions.IOStreams) *cobra.Command {

cmd.AddCommand(install.New(streams))
cmd.AddCommand(uninstall.New(streams))
cmd.AddCommand(upgrade.New(streams))

o := newOptions(streams)
o.configFlags.AddFlags(cmd.Flags())
Expand Down
38 changes: 38 additions & 0 deletions cmd/kubectl-datadog/autoscaling/cluster/common/helm/helm.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,44 @@ func upgrade(ctx context.Context, ac *action.Configuration, releaseName, namespa
return nil
}

// GetRelease retrieves the full Helm release including config values.
func GetRelease(_ context.Context, ac *action.Configuration, releaseName string) (*release.Release, error) {
getAction := action.NewGet(ac)
rel, err := getAction.Run(releaseName)
if err != nil {
return nil, fmt.Errorf("failed to get Helm release %s: %w", releaseName, err)
}
return rel, nil
}

// IsOurRelease checks whether a Karpenter Helm release in the given namespace
// was installed by the kubectl-datadog plugin. Returns the release for reuse
// by the caller.
func IsOurRelease(ctx context.Context, configFlags *genericclioptions.ConfigFlags, namespace, releaseName string) (bool, *release.Release, error) {
actionConfig, err := NewActionConfig(configFlags, namespace)
if err != nil {
return false, nil, fmt.Errorf("failed to create Helm action config for namespace %s: %w", namespace, err)
}

rel, err := GetRelease(ctx, actionConfig, releaseName)
if err != nil {
return false, nil, err
}

if rel == nil || rel.Config == nil {
return false, nil, nil
}

labels, ok := rel.Config["additionalLabels"].(map[string]any)
if !ok {
return false, rel, nil
}

managedBy, ok := labels["app.kubernetes.io/managed-by"].(string)
isOurs := ok && managedBy == "kubectl-datadog"
return isOurs, rel, nil
}

func Uninstall(ctx context.Context, ac *action.Configuration, releaseName string) error {
exist, err := Exists(ctx, ac, releaseName)
if err != nil {
Expand Down
94 changes: 94 additions & 0 deletions cmd/kubectl-datadog/autoscaling/cluster/common/k8s/karpenter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package k8s

import (
"context"
"fmt"

admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
)

var karpenterAPIGroups = map[string]bool{
"karpenter.sh": true,
"karpenter.k8s.aws": true,
}

const helmReleaseNameKarpenter = "karpenter"

// DetectActiveKarpenter checks for an active Karpenter installation by looking for
// webhook configurations that reference Karpenter API groups. Returns the namespace
// where Karpenter's webhook service is running.
func DetectActiveKarpenter(ctx context.Context, clientset kubernetes.Interface) (bool, string, error) {
vwcList, err := clientset.AdmissionregistrationV1().ValidatingWebhookConfigurations().List(ctx, metav1.ListOptions{})
if err != nil {
return false, "", fmt.Errorf("failed to list ValidatingWebhookConfigurations: %w", err)
}

for _, vwc := range vwcList.Items {
for _, webhook := range vwc.Webhooks {
if ns, ok := extractKarpenterNamespace(webhook.Rules, webhook.ClientConfig); ok {
return true, ns, nil
}
}
}

mwcList, err := clientset.AdmissionregistrationV1().MutatingWebhookConfigurations().List(ctx, metav1.ListOptions{})
if err != nil {
return false, "", fmt.Errorf("failed to list MutatingWebhookConfigurations: %w", err)
}

for _, mwc := range mwcList.Items {
for _, webhook := range mwc.Webhooks {
if ns, ok := extractKarpenterNamespace(webhook.Rules, webhook.ClientConfig); ok {
return true, ns, nil
}
}
}

return false, "", nil
}

func extractKarpenterNamespace(rules []admissionregistrationv1.RuleWithOperations, clientConfig admissionregistrationv1.WebhookClientConfig) (string, bool) {
for _, rule := range rules {
for _, group := range rule.APIGroups {
if karpenterAPIGroups[group] {
if clientConfig.Service != nil {
return clientConfig.Service.Namespace, true
}
// URL-based webhook — Karpenter is present but we can't
// determine the namespace (e.g. external/out-of-cluster).
return "", true
}
}
}
return "", false
}

// FindKarpenterHelmRelease searches for a deployed Helm release named "karpenter"
// across all namespaces by looking at Helm storage secrets. This is a fallback
// for when webhooks are absent (e.g. pods crashed) but the Helm release still exists.
func FindKarpenterHelmRelease(ctx context.Context, clientset kubernetes.Interface) (bool, []string, error) {
secrets, err := clientset.CoreV1().Secrets("").List(ctx, metav1.ListOptions{
LabelSelector: "owner=helm,name=" + helmReleaseNameKarpenter + ",status=deployed",
})
if err != nil {
return false, nil, fmt.Errorf("failed to list Helm release secrets: %w", err)
}

if len(secrets.Items) == 0 {
return false, nil, nil
}

// Deduplicate namespaces (multiple revisions may exist in the same namespace)
seen := map[string]bool{}
var namespaces []string
for _, s := range secrets.Items {
if !seen[s.Namespace] {
seen[s.Namespace] = true
namespaces = append(namespaces, s.Namespace)
}
}

return true, namespaces, nil
}
Loading
Loading