From 48d747b6bb44ac6c932751bcaaf7b27283ab5350 Mon Sep 17 00:00:00 2001 From: Aditya Thebe Date: Fri, 22 Nov 2024 08:39:04 +0545 Subject: [PATCH] feat: only set namespace for namespaced resources --- api/v1/checks.go | 17 ++++++++++--- checks/kubernetes_resource.go | 48 ++++++++++++++++++++++++++++++++--- 2 files changed, 59 insertions(+), 6 deletions(-) diff --git a/api/v1/checks.go b/api/v1/checks.go index 56ec70994..34e415cef 100644 --- a/api/v1/checks.go +++ b/api/v1/checks.go @@ -16,6 +16,7 @@ import ( "github.com/samber/lo" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime/schema" k8sTypes "k8s.io/apimachinery/pkg/types" ) @@ -978,17 +979,27 @@ type KubernetesResourceCheck struct { WaitFor KubernetesResourceCheckWaitFor `json:"waitFor,omitempty"` } +func (c *KubernetesResourceCheck) HasResourcesWithMissingNamespace() bool { + for _, r := range append(c.StaticResources, c.Resources...) { + if r.GetNamespace() == "" { + return true + } + } + + return false +} + // SetMissingNamespace will set the parent canaries name to resources whose namespace // is not explicitly specified. -func (c *KubernetesResourceCheck) SetMissingNamespace(parent Canary) { +func (c *KubernetesResourceCheck) SetMissingNamespace(parent Canary, namespacedResources map[schema.GroupVersionKind]bool) { for i, r := range c.StaticResources { - if r.GetNamespace() == "" { + if r.GetNamespace() == "" && namespacedResources[r.GroupVersionKind()] { c.StaticResources[i].SetNamespace(parent.GetNamespace()) } } for i, r := range c.Resources { - if r.GetNamespace() == "" { + if r.GetNamespace() == "" && namespacedResources[r.GroupVersionKind()] { c.Resources[i].SetNamespace(parent.GetNamespace()) } } diff --git a/checks/kubernetes_resource.go b/checks/kubernetes_resource.go index 83159836b..39e2d9acc 100644 --- a/checks/kubernetes_resource.go +++ b/checks/kubernetes_resource.go @@ -12,6 +12,7 @@ import ( "github.com/flanksource/gomplate/v3" "github.com/flanksource/is-healthy/pkg/health" "github.com/flanksource/is-healthy/pkg/lua" + "github.com/patrickmn/go-cache" "github.com/samber/lo" "github.com/sethvargo/go-retry" "golang.org/x/sync/errgroup" @@ -70,9 +71,6 @@ func (c *KubernetesResourceChecker) Check(ctx context.Context, check v1.Kubernet return results.Failf("validation: %v", err) } - check.SetMissingNamespace(ctx.Canary) - check.SetCanaryOwnerReference(ctx.Canary) - if check.Kubeconfig != nil { var err error ctx, err = ctx.WithKubeconfig(*check.Kubeconfig) @@ -81,6 +79,17 @@ func (c *KubernetesResourceChecker) Check(ctx context.Context, check v1.Kubernet } } + if check.HasResourcesWithMissingNamespace() { + namespacedResources, err := fetchNamespacedResources(ctx) + if err != nil { + return results.Failf("failed to get api resources: %w", err) + } + + check.SetMissingNamespace(ctx.Canary, namespacedResources) + } + + check.SetCanaryOwnerReference(ctx.Canary) + if err := templateKubernetesResourceCheck(ctx.Canary.GetPersistedID(), ctx.Canary.GetCheckID(check.GetName()), &check); err != nil { return results.Failf("templating error: %v", err) } @@ -478,3 +487,36 @@ func templateKubernetesResourceCheck(canaryID, checkID string, check *v1.Kuberne return nil } + +var apiResourceCache = cache.New(time.Hour*24, time.Hour*24) + +func fetchNamespacedResources(ctx context.Context) (map[schema.GroupVersionKind]bool, error) { + kubeconfigCacheKey := ctx.KubernetesRestConfig().Host + ctx.KubernetesRestConfig().APIPath + if val, ok := apiResourceCache.Get(kubeconfigCacheKey); ok { + return val.(map[schema.GroupVersionKind]bool), nil + } + + _, resourceList, err := ctx.Kubernetes().Discovery().ServerGroupsAndResources() + if err != nil { + return nil, fmt.Errorf("failed to get server groups & resources: %w", err) + } + + namespaceResources := make(map[schema.GroupVersionKind]bool) + for _, apiResourceList := range resourceList { + for _, resource := range apiResourceList.APIResources { + if resource.Namespaced { + gv, _ := schema.ParseGroupVersion(apiResourceList.GroupVersion) + key := schema.GroupVersionKind{ + Group: gv.Group, + Version: gv.Version, + Kind: resource.Kind, + } + + namespaceResources[key] = true + } + } + } + + apiResourceCache.SetDefault(kubeconfigCacheKey, namespaceResources) + return namespaceResources, nil +}