Compare commits

...

1 Commits

Author SHA1 Message Date
Matthias Bertschy
4e0328da43 wip - fix data retrieved from the host CRDs
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2026-03-22 22:03:21 +01:00

View File

@@ -4,7 +4,6 @@ import (
"context" "context"
stdjson "encoding/json" stdjson "encoding/json"
"fmt" "fmt"
"reflect"
"github.com/kubescape/go-logger" "github.com/kubescape/go-logger"
"github.com/kubescape/go-logger/helpers" "github.com/kubescape/go-logger/helpers"
@@ -25,6 +24,10 @@ func (hsh *HostSensorHandler) getCRDResources(ctx context.Context, resourceType
// List CRD resources // List CRD resources
items, err := hsh.listCRDResources(ctx, pluralName, resourceType.String()) items, err := hsh.listCRDResources(ctx, pluralName, resourceType.String())
if err != nil { if err != nil {
logger.L().Ctx(ctx).Error("failed to list CRD resources",
helpers.String("kind", resourceType.String()),
helpers.String("plural", pluralName),
helpers.Error(err))
return nil, err return nil, err
} }
@@ -42,7 +45,7 @@ func (hsh *HostSensorHandler) getCRDResources(ctx context.Context, resourceType
result = append(result, envelope) result = append(result, envelope)
} }
logger.L().Debug("Retrieved resources from CRDs", logger.L().Ctx(ctx).Info("Retrieved resources from CRDs",
helpers.String("kind", resourceType.String()), helpers.String("kind", resourceType.String()),
helpers.Int("count", len(result))) helpers.Int("count", len(result)))
@@ -54,6 +57,8 @@ func (hsh *HostSensorHandler) convertCRDToEnvelope(item unstructured.Unstructure
envelope := hostsensor.HostSensorDataEnvelope{} envelope := hostsensor.HostSensorDataEnvelope{}
// Set API version and kind // Set API version and kind
// The cluster CRDs use v1beta1, but the OPA policies expect v1beta0.
// We use hostsensor.Version which is "v1beta0".
envelope.SetApiVersion(k8sinterface.JoinGroupVersion(hostsensor.GroupHostSensor, hostsensor.Version)) envelope.SetApiVersion(k8sinterface.JoinGroupVersion(hostsensor.GroupHostSensor, hostsensor.Version))
envelope.SetKind(resourceType.String()) envelope.SetKind(resourceType.String())
@@ -61,29 +66,44 @@ func (hsh *HostSensorHandler) convertCRDToEnvelope(item unstructured.Unstructure
nodeName := item.GetName() nodeName := item.GetName()
envelope.SetName(nodeName) envelope.SetName(nodeName)
// Extract content from spec.content // The host sensor CRDs store the actual data in the 'spec' field.
content, found, err := unstructured.NestedString(item.Object, "spec", "content") // We need to map 'spec' to the envelope's 'Data' field.
spec, found, err := unstructured.NestedMap(item.Object, "spec")
if err != nil { if err != nil {
return envelope, fmt.Errorf("failed to extract spec.content: %w", err) return envelope, fmt.Errorf("failed to extract spec: %w", err)
} }
if !found { if !found {
// fallback to "spec" itself logger.L().Warning("spec not found in CRD item",
contentI, found, err := unstructured.NestedFieldNoCopy(item.Object, "spec") helpers.String("kind", resourceType.String()),
if err != nil { helpers.String("name", nodeName))
return envelope, fmt.Errorf("failed to extract spec: %w", err) return envelope, fmt.Errorf("spec not found in CRD")
}
if !found {
return envelope, fmt.Errorf("spec not found in CRD")
}
contentBytes, err := stdjson.Marshal(contentI)
if err != nil {
return envelope, fmt.Errorf("failed to marshal spec: %w", err)
}
content = string(contentBytes)
} }
// Remove nodeName as it's already in the envelope metadata
delete(spec, "nodeName")
// The Rego policies expect the data in a specific structure.
// For most resources (like ControlPlaneInfo), the 'spec' already contains the required fields:
// APIServerInfo, KubeProxyInfo, etc.
// However, for some resources like KubeletInfo, the CRD's 'spec' wraps the actual data
// in a field named after the resource type. We need to unwrap it if it exists.
var data interface{} = spec
if inner, ok := spec[resourceType.String()]; ok {
data = inner
}
contentBytes, err := stdjson.Marshal(data)
if err != nil {
return envelope, fmt.Errorf("failed to marshal spec: %w", err)
}
logger.L().Debug("Converted CRD to envelope",
helpers.String("kind", resourceType.String()),
helpers.String("name", nodeName),
helpers.Int("dataSize", len(contentBytes)))
// Set data as raw bytes // Set data as raw bytes
envelope.SetData([]byte(content)) envelope.SetData(contentBytes)
return envelope, nil return envelope, nil
} }
@@ -143,8 +163,14 @@ func (hsh *HostSensorHandler) getCNIInfo(ctx context.Context) ([]hostsensor.Host
// If information are found, then return true. Return false otherwise. // If information are found, then return true. Return false otherwise.
func hasCloudProviderInfo(cpi []hostsensor.HostSensorDataEnvelope) bool { func hasCloudProviderInfo(cpi []hostsensor.HostSensorDataEnvelope) bool {
for index := range cpi { for index := range cpi {
if !reflect.DeepEqual(cpi[index].GetData(), stdjson.RawMessage("{}\\n")) { var data map[string]interface{}
return true if err := stdjson.Unmarshal(cpi[index].GetData(), &data); err != nil {
continue
}
if val, ok := data["providerMetaDataAPIAccess"]; ok {
if b, ok := val.(bool); ok && b {
return true
}
} }
} }
@@ -156,9 +182,21 @@ func (hsh *HostSensorHandler) CollectResources(ctx context.Context) ([]hostsenso
res := make([]hostsensor.HostSensorDataEnvelope, 0) res := make([]hostsensor.HostSensorDataEnvelope, 0)
infoMap := make(map[string]apis.StatusInfo) infoMap := make(map[string]apis.StatusInfo)
logger.L().Debug("Collecting host sensor data from CRDs") logger.L().Info("Collecting host sensor data from CRDs")
var hasCloudProvider bool var hasCloudProvider bool
// We first query CloudProviderInfo to determine if we should skip ControlPlaneInfo
cloudProviderData, err := hsh.getCloudProviderInfo(ctx)
if err != nil {
addInfoToMap(k8shostsensor.CloudProviderInfo, infoMap, err)
logger.L().Ctx(ctx).Warning("Failed to get CloudProviderInfo from CRD", helpers.Error(err))
} else {
hasCloudProvider = hasCloudProviderInfo(cloudProviderData)
if len(cloudProviderData) > 0 {
res = append(res, cloudProviderData...)
}
}
for _, toPin := range []struct { for _, toPin := range []struct {
Resource k8shostsensor.HostSensorResource Resource k8shostsensor.HostSensorResource
Query func(context.Context) ([]hostsensor.HostSensorDataEnvelope, error) Query func(context.Context) ([]hostsensor.HostSensorDataEnvelope, error)
@@ -192,16 +230,11 @@ func (hsh *HostSensorHandler) CollectResources(ctx context.Context) ([]hostsenso
Resource: k8shostsensor.KubeProxyInfo, Resource: k8shostsensor.KubeProxyInfo,
Query: hsh.getKubeProxyInfo, Query: hsh.getKubeProxyInfo,
}, },
{
Resource: k8shostsensor.CloudProviderInfo,
Query: hsh.getCloudProviderInfo,
},
{ {
Resource: k8shostsensor.CNIInfo, Resource: k8shostsensor.CNIInfo,
Query: hsh.getCNIInfo, Query: hsh.getCNIInfo,
}, },
{ {
// ControlPlaneInfo is queried _after_ CloudProviderInfo.
Resource: k8shostsensor.ControlPlaneInfo, Resource: k8shostsensor.ControlPlaneInfo,
Query: hsh.getControlPlaneInfo, Query: hsh.getControlPlaneInfo,
}, },
@@ -209,6 +242,7 @@ func (hsh *HostSensorHandler) CollectResources(ctx context.Context) ([]hostsenso
k8sInfo := toPin k8sInfo := toPin
if k8sInfo.Resource == k8shostsensor.ControlPlaneInfo && hasCloudProvider { if k8sInfo.Resource == k8shostsensor.ControlPlaneInfo && hasCloudProvider {
logger.L().Info("Skipping ControlPlaneInfo collection due to cloud provider presence")
// we retrieve control plane info only if we are not using a cloud provider // we retrieve control plane info only if we are not using a cloud provider
continue continue
} }
@@ -221,15 +255,11 @@ func (hsh *HostSensorHandler) CollectResources(ctx context.Context) ([]hostsenso
helpers.Error(err)) helpers.Error(err))
} }
if k8sInfo.Resource == k8shostsensor.CloudProviderInfo {
hasCloudProvider = hasCloudProviderInfo(kcData)
}
if len(kcData) > 0 { if len(kcData) > 0 {
res = append(res, kcData...) res = append(res, kcData...)
} }
} }
logger.L().Debug("Done collecting information from CRDs", helpers.Int("totalResources", len(res))) logger.L().Info("Done collecting information from CRDs", helpers.Int("totalResources", len(res)))
return res, infoMap, nil return res, infoMap, nil
} }