diff --git a/api/v1alpha1/managedmetric_types.go b/api/v1alpha1/managedmetric_types.go index 98723e2..c3c5543 100644 --- a/api/v1alpha1/managedmetric_types.go +++ b/api/v1alpha1/managedmetric_types.go @@ -31,6 +31,10 @@ type ManagedMetricSpec struct { // Defines which managed resources to observe // +optional Target *GroupVersionKind `json:"target,omitempty"` + // Defines dimensions of the metric. All specified fields must be nested strings. Nested slices are not supported. + // If not specified, only status.conditions of the CR will be used as dimension. + // +optional + Dimensions map[string]string `json:"dimensions,omitempty"` // Define labels of your object to adapt filters of the query // +optional LabelSelector string `json:"labelSelector,omitempty"` diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index e775681..89b75c6 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -647,6 +647,13 @@ func (in *ManagedMetricSpec) DeepCopyInto(out *ManagedMetricSpec) { *out = new(GroupVersionKind) **out = **in } + if in.Dimensions != nil { + in, out := &in.Dimensions, &out.Dimensions + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } out.Interval = in.Interval if in.DataSinkRef != nil { in, out := &in.DataSinkRef, &out.DataSinkRef diff --git a/cmd/metrics-operator/embedded/crds/metrics.openmcp.cloud_managedmetrics.yaml b/cmd/metrics-operator/embedded/crds/metrics.openmcp.cloud_managedmetrics.yaml index c8afc89..a5bdc61 100644 --- a/cmd/metrics-operator/embedded/crds/metrics.openmcp.cloud_managedmetrics.yaml +++ b/cmd/metrics-operator/embedded/crds/metrics.openmcp.cloud_managedmetrics.yaml @@ -65,6 +65,13 @@ spec: description: Sets the description that will be used to identify the metric in Dynatrace(or other providers) type: string + dimensions: + additionalProperties: + type: string + description: |- + Defines dimensions of the metric. All specified fields must be nested strings. Nested slices are not supported. + If not specified, only status.conditions of the CR will be used as dimension. + type: object fieldSelector: description: Define fields of your object to adapt filters of the query diff --git a/internal/controller/managedmetric_controller.go b/internal/controller/managedmetric_controller.go index c1b4aed..0e48c5d 100644 --- a/internal/controller/managedmetric_controller.go +++ b/internal/controller/managedmetric_controller.go @@ -314,6 +314,7 @@ func getClusterInfo(config *rest.Config) (string, error) { clusterName := parts[0] return clusterName, nil + } // OrchestratorFactory is a function type for creating orchestrators diff --git a/internal/orchestrator/managedhandler.go b/internal/orchestrator/managedhandler.go index 9948a7a..4fd62a4 100644 --- a/internal/orchestrator/managedhandler.go +++ b/internal/orchestrator/managedhandler.go @@ -5,7 +5,6 @@ import ( "fmt" "slices" "strconv" - "strings" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -59,25 +58,27 @@ func (h *ManagedHandler) sendStatusBasedMetricValue(ctx context.Context) (string // Create a new data point for each resource dataPoint := clientoptl.NewDataPoint() - // Add GVK dimensions from resource - gv, err := schema.ParseGroupVersion(cr.MangedResource.APIVersion) + objMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&cr.MangedResource) if err != nil { return "", err } - dataPoint.AddDimension(KIND, cr.MangedResource.Kind) - dataPoint.AddDimension(GROUP, gv.Group) - dataPoint.AddDimension(VERSION, gv.Version) + + u := &unstructured.Unstructured{Object: objMap} + + for key, expr := range h.metric.Spec.Dimensions { + s, _, err := nestedPrimitiveValue(*u, expr) + if err != nil { + fmt.Printf("WARN: Could not parse expression '%s' for dimension field '%s'. Error: %v\n", key, expr, err) + continue + } + dataPoint.AddDimension(key, s) + } // Add cluster dimension if available if h.clusterName != nil { dataPoint.AddDimension(CLUSTER, *h.clusterName) } - // Add status conditions as dimensions - for typ, state := range cr.Status { - dataPoint.AddDimension(strings.ToLower(typ), strconv.FormatBool(state)) - } - // Set the value to 1 for each resource dataPoint.SetValue(1)