| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139 |
- package opencost
- import (
- "fmt"
- "github.com/opencost/opencost/core/pkg/filter/ast"
- kfilter "github.com/opencost/opencost/core/pkg/filter/k8sobject"
- "github.com/opencost/opencost/core/pkg/filter/matcher"
- "github.com/opencost/opencost/core/pkg/filter/transform"
- appsv1 "k8s.io/api/apps/v1"
- batchv1 "k8s.io/api/batch/v1"
- corev1 "k8s.io/api/core/v1"
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- "k8s.io/apimachinery/pkg/runtime"
- )
- // K8sObjectMatcher is a matcher implementation for Kubernetes runtime.Object
- // instances, compiled using the matcher.MatchCompiler.
- type K8sObjectMatcher matcher.Matcher[runtime.Object]
- // NewK8sObjectMatchCompiler creates a new instance of a
- // matcher.MatchCompiler[runtime.Object] which can be used to compile
- // filter.Filter ASTs into matcher.Matcher[runtime.Object] implementations.
- //
- // If the label config is nil, the compiler will fail to compile alias filters
- // if any are present in the AST.
- func NewK8sObjectMatchCompiler() *matcher.MatchCompiler[runtime.Object] {
- passes := []transform.CompilerPass{}
- return matcher.NewMatchCompiler(
- k8sObjectFieldMap,
- k8sObjectSliceFieldMap,
- k8sObjectMapFieldMap,
- passes...,
- )
- }
- func objectMetaFromObject(o runtime.Object) (metav1.ObjectMeta, error) {
- switch v := o.(type) {
- case *appsv1.Deployment:
- return v.ObjectMeta, nil
- case *appsv1.StatefulSet:
- return v.ObjectMeta, nil
- case *appsv1.DaemonSet:
- return v.ObjectMeta, nil
- case *corev1.Pod:
- return v.ObjectMeta, nil
- case *batchv1.CronJob:
- return v.ObjectMeta, nil
- }
- return metav1.ObjectMeta{}, fmt.Errorf("currently-unsupported runtime.Object type for filtering: %T", o)
- }
- // Maps fields from an allocation to a string value based on an identifier
- func k8sObjectFieldMap(o runtime.Object, identifier ast.Identifier) (string, error) {
- if identifier.Field == nil {
- return "", fmt.Errorf("cannot map field from identifier with nil field")
- }
- m, err := objectMetaFromObject(o)
- if err != nil {
- return "", fmt.Errorf("retrieving object meta: %w", err)
- }
- var controllerKind string
- var controllerName string
- var pod string
- switch v := o.(type) {
- case *appsv1.Deployment:
- controllerKind = "deployment"
- controllerName = v.Name
- case *appsv1.StatefulSet:
- controllerKind = "statefulset"
- controllerName = v.Name
- case *appsv1.DaemonSet:
- controllerKind = "daemonset"
- controllerName = v.Name
- case *corev1.Pod:
- pod = v.Name
- if len(v.OwnerReferences) == 0 {
- controllerKind = "pod"
- controllerName = v.Name
- }
- case *batchv1.CronJob:
- controllerKind = "cronjob"
- controllerName = v.Name
- default:
- return "", fmt.Errorf("currently-unsupported runtime.Object type for filtering: %T", o)
- }
- // For now, we will just do our best to implement Allocation fields because
- // most k8s-based queries are on Allocation data. The other we will
- // eventually want to support is Asset, but I'm not sure that I have time
- // for that right now.
- field := kfilter.K8sObjectField(identifier.Field.Name)
- switch field {
- case kfilter.FieldNamespace:
- return m.Namespace, nil
- case kfilter.FieldControllerName:
- return controllerName, nil
- case kfilter.FieldControllerKind:
- return controllerKind, nil
- case kfilter.FieldPod:
- return pod, nil
- case kfilter.FieldLabel:
- if m.Labels != nil {
- return m.Labels[identifier.Key], nil
- }
- return "", nil
- case kfilter.FieldAnnotation:
- if m.Annotations != nil {
- return m.Annotations[identifier.Key], nil
- }
- return "", nil
- }
- return "", fmt.Errorf("Failed to find string identifier on K8sObject: %s (consider adding support if this is an expected field)", identifier.Field.Name)
- }
- // Maps slice fields from an allocation to a []string value based on an identifier
- func k8sObjectSliceFieldMap(o runtime.Object, identifier ast.Identifier) ([]string, error) {
- return nil, fmt.Errorf("K8sObject filters current have no supported []string identifiers")
- }
- // Maps map fields from an allocation to a map[string]string value based on an identifier
- func k8sObjectMapFieldMap(o runtime.Object, identifier ast.Identifier) (map[string]string, error) {
- m, err := objectMetaFromObject(o)
- if err != nil {
- return nil, fmt.Errorf("retrieving object meta: %w", err)
- }
- switch kfilter.K8sObjectField(identifier.Field.Name) {
- case kfilter.FieldLabel:
- return m.Labels, nil
- case kfilter.FieldAnnotation:
- return m.Annotations, nil
- }
- return nil, fmt.Errorf("Failed to find map[string]string identifier on K8sObject: %s", identifier.Field.Name)
- }
|