Selaa lähdekoodia

Introduce kubemodel with core Kubernetes resources (Cluster, Namespace, Node, Pod, Container, Owner, Service) (#3472)

Signed-off-by: Sparsh <sparsh.raj30@gmail.com>
Co-authored-by: Alex Meijer <ameijer@users.noreply.github.com>
Sparsh Raj 5 kuukautta sitten
vanhempi
sitoutus
92af47617a

+ 22 - 0
core/pkg/clustercache/clustercache.go

@@ -171,6 +171,28 @@ type ResourceQuota struct {
 type Volume struct {
 }
 
+// GetPublicIPAddresses returns all external IP addresses associated with the node
+func (n *Node) GetPublicIPAddresses() []string {
+	var publicIPs []string
+	for _, addr := range n.Status.Addresses {
+		if addr.Type == v1.NodeExternalIP {
+			publicIPs = append(publicIPs, addr.Address)
+		}
+	}
+	return publicIPs
+}
+
+// GetPublicIPCount returns the count of external IP addresses associated with the node
+func (n *Node) GetPublicIPCount() int {
+	count := 0
+	for _, addr := range n.Status.Addresses {
+		if addr.Type == v1.NodeExternalIP {
+			count++
+		}
+	}
+	return count
+}
+
 // GetControllerOf returns a pointer to a copy of the controllerRef if controllee has a controller
 func GetControllerOf(pod *Pod) *metav1.OwnerReference {
 	ref := GetControllerOfNoCopy(pod)

+ 23 - 0
core/pkg/model/kubemodel/bingen.go

@@ -0,0 +1,23 @@
+package kubemodel
+
+////////////////////////////////////////////////////////////////////////////////
+// NOTE: If you add fields to _any_ struct that is serialized by bingen, please
+// make sure to add those fields to the END of the struct definition. This is
+// required for backwards-compatibility. So:
+//
+// type Foo struct {
+//     ExistingField1 string
+//     ExistingField2 int
+// }
+//
+// becomes:
+//
+// type Foo struct {
+//     ExistingField1 string
+//     ExistingField2 int
+//     NewField       float64 // @bingen: <- annotation ref: bingen README
+// }
+//
+////////////////////////////////////////////////////////////////////////////////
+
+//go:generate bingen -package=kubemodel -version=1 -buffer=github.com/opencost/opencost/core/pkg/util

+ 13 - 0
core/pkg/model/kubemodel/cluster.go

@@ -0,0 +1,13 @@
+package kubemodel
+
+import "time"
+
+// @bingen:generate:Cluster
+type Cluster struct {
+	UID      string    `json:"uid"`      // @bingen:field[version=1]
+	Provider Provider  `json:"provider"` // @bingen:field[version=1]
+	Account  string    `json:"account"`  // @bingen:field[version=1]
+	Name     string    `json:"name"`     // @bingen:field[version=1]
+	Start    time.Time `json:"start"`    // @bingen:field[version=1]
+	End      time.Time `json:"end"`      // @bingen:field[version=1]
+}

+ 23 - 0
core/pkg/model/kubemodel/container.go

@@ -0,0 +1,23 @@
+package kubemodel
+
+import "time"
+
+// @bingen:generate:Container
+type Container struct {
+	PodUID                              string    `json:"podUid"`                              // @bingen:field[version=1]
+	Name                                string    `json:"name"`                                // @bingen:field[version=1]
+	Start                               time.Time `json:"start"`                               // @bingen:field[version=1]
+	End                                 time.Time `json:"end"`                                 // @bingen:field[version=1]
+	CpuMillicoreSecondsAllocated        uint64    `json:"cpuMillicoreSecondsAllocated"`        // @bingen:field[version=1]
+	CpuMillicoreRequestAverageAllocated uint64    `json:"cpuMillicoreRequestAverageAllocated"` // @bingen:field[version=1]
+	CpuMillicoreUsageAverage            uint64    `json:"cpuMillicoreUsageAverage"`            // @bingen:field[version=1]
+	CpuMillicoreUsageMax                uint64    `json:"cpuMillicoreUsageMax"`                // @bingen:field[version=1]
+	RAMByteSecondsAllocated             uint64    `json:"ramByteSecondsAllocated"`             // @bingen:field[version=1]
+	RAMByteRequestAverageAllocated      uint64    `json:"ramByteRequestAverageAllocated"`      // @bingen:field[version=1]
+	RAMByteUsageAverage                 uint64    `json:"ramByteUsageAverage"`                 // @bingen:field[version=1]
+	RAMByteUsageMax                     uint64    `json:"ramByteUsageMax"`                     // @bingen:field[version=1]
+	StorageByteSecondsAllocated         uint64    `json:"storageByteSecondsAllocated"`         // @bingen:field[version=1]
+	StorageByteRequestAverageAllocated  uint64    `json:"storageByteRequestAverageAllocated"`  // @bingen:field[version=1]
+	StorageByteUsageAverage             uint64    `json:"storageByteUsageAverage"`             // @bingen:field[version=1]
+	StorageByteUsageMax                 uint64    `json:"storageByteUsageMax"`                 // @bingen:field[version=1]
+}

+ 14 - 0
core/pkg/model/kubemodel/diagnostic.go

@@ -0,0 +1,14 @@
+package kubemodel
+
+import "time"
+
+// @bingen:generate:DiagnosticResult
+type DiagnosticResult struct {
+	UID         string            `json:"uid"`               // @bingen:field[version=1]
+	Name        string            `json:"name"`              // @bingen:field[version=1]
+	Description string            `json:"description"`       // @bingen:field[version=1]
+	Category    string            `json:"category"`          // @bingen:field[version=1]
+	Timestamp   time.Time         `json:"timestamp"`         // @bingen:field[version=1]
+	Error       string            `json:"error,omitempty"`   // @bingen:field[version=1]
+	Details     map[string]string `json:"details,omitempty"` // @bingen:field[version=1]
+}

+ 199 - 0
core/pkg/model/kubemodel/kubemodel.go

@@ -0,0 +1,199 @@
+package kubemodel
+
+import (
+	"errors"
+	"fmt"
+	"time"
+)
+
+// @bingen:generate[stringtable]:KubeModelSet
+type KubeModelSet struct {
+	Metadata   *Metadata             `json:"meta"`                 // @bingen:field[version=1]
+	Window     Window                `json:"window"`               // @bingen:field[version=1]
+	Cluster    *Cluster              `json:"cluster"`              // @bingen:field[version=1]
+	Namespaces map[string]*Namespace `json:"namespaces"`           // @bingen:field[version=1]
+	Containers map[string]*Container `json:"containers,omitempty"` // @bingen:field[version=1]
+	Owners     map[string]*Owner     `json:"owners,omitempty"`     // @bingen:field[version=1]
+	Nodes      map[string]*Node      `json:"nodes,omitempty"`      // @bingen:field[version=1]
+	Pods       map[string]*Pod       `json:"pods,omitempty"`       // @bingen:field[version=1]
+	Services   map[string]*Service   `json:"services,omitempty"`   // @bingen:field[version=1]
+	idx        *kubeModelSetIndexes  // @bingen:field[ignore]
+}
+
+func (kms *KubeModelSet) MarshalBinary() (data []byte, err error) {
+	//TODO implement me
+	panic("implement me")
+}
+
+func NewKubeModelSet(start time.Time, end time.Time) *KubeModelSet {
+	return &KubeModelSet{
+		Metadata: &Metadata{
+			CreatedAt: time.Now().UTC(),
+		},
+		Window: Window{
+			Start: start,
+			End:   end,
+		},
+		Containers: map[string]*Container{},
+		Owners:     map[string]*Owner{},
+		Namespaces: map[string]*Namespace{},
+		Nodes:      map[string]*Node{},
+		Pods:       map[string]*Pod{},
+		Services:   map[string]*Service{},
+		idx: &kubeModelSetIndexes{
+			namespaceNameToID: map[string]string{},
+		},
+	}
+}
+
+func (kms *KubeModelSet) RegisterNamespace(id string, name string) error {
+	if _, ok := kms.Namespaces[id]; !ok {
+		if kms.Cluster == nil {
+			return errors.New("KubeModelSet missing Cluster")
+		}
+
+		kms.Namespaces[id] = &Namespace{
+			UID:        id,
+			ClusterUID: kms.Cluster.UID,
+			Name:       name,
+		}
+
+		// Index namespace name-to-ID for fast lookup
+		if name != "" {
+			kms.idx.namespaceNameToID[name] = id
+		}
+
+		kms.Metadata.ObjectCount++
+	}
+
+	return nil
+}
+
+// GetNamespaceByName retrieves a namespace by its name using the index
+func (kms *KubeModelSet) GetNamespaceByName(name string) (*Namespace, bool) {
+	if kms.idx == nil {
+		return nil, false
+	}
+
+	id, ok := kms.idx.namespaceNameToID[name]
+	if !ok {
+		return nil, false
+	}
+
+	ns, ok := kms.Namespaces[id]
+	return ns, ok
+}
+
+// IsEmpty returns true if the KubeModelSet is nil, has no cluster, or contains no resources
+func (kms *KubeModelSet) IsEmpty() bool {
+	if kms == nil || kms.Cluster == nil {
+		return true
+	}
+
+	// Check if all resource maps are empty
+	return len(kms.Containers) == 0 &&
+		len(kms.Owners) == 0 &&
+		len(kms.Namespaces) == 0 &&
+		len(kms.Nodes) == 0 &&
+		len(kms.Pods) == 0 &&
+		len(kms.Services) == 0
+}
+
+func (kms *KubeModelSet) RegisterPod(id, name, namespace string) error {
+	if _, ok := kms.Pods[id]; !ok {
+		nsID, ok := kms.idx.namespaceNameToID[namespace]
+		if !ok {
+			return fmt.Errorf("KubeModelSet missing namespace '%s'", namespace)
+		}
+
+		kms.Pods[id] = &Pod{
+			UID:          id,
+			Name:         name,
+			NamespaceUID: nsID,
+		}
+
+		kms.Metadata.ObjectCount++
+	}
+
+	return nil
+}
+
+func (kms *KubeModelSet) RegisterNode(id, name string) error {
+	if _, ok := kms.Nodes[id]; !ok {
+		if kms.Cluster == nil {
+			return errors.New("KubeModelSet missing Cluster")
+		}
+
+		kms.Nodes[id] = &Node{
+			UID:        id,
+			ClusterUID: kms.Cluster.UID,
+			Name:       name,
+		}
+
+		kms.Metadata.ObjectCount++
+	}
+
+	return nil
+}
+
+func (kms *KubeModelSet) RegisterOwner(id, name, namespace, kind string, isController bool) error {
+	if _, ok := kms.Owners[id]; !ok {
+		nsID, ok := kms.idx.namespaceNameToID[namespace]
+		if !ok {
+			return fmt.Errorf("KubeModelSet missing namespace '%s'", namespace)
+		}
+
+		kms.Owners[id] = &Owner{
+			UID:        id,
+			Name:       name,
+			OwnerUID:   nsID,
+			Kind:       OwnerKind(kind),
+			Controller: isController,
+		}
+
+		kms.Metadata.ObjectCount++
+	}
+
+	return nil
+}
+
+func (kms *KubeModelSet) RegisterService(id, name, namespace string) error {
+	if _, ok := kms.Services[id]; !ok {
+		if kms.Cluster == nil {
+			return errors.New("KubeModelSet missing Cluster")
+		}
+
+		nsID, ok := kms.idx.namespaceNameToID[namespace]
+		if !ok {
+			return fmt.Errorf("KubeModelSet missing namespace '%s'", namespace)
+		}
+
+		kms.Services[id] = &Service{
+			UID:          id,
+			ClusterUID:   kms.Cluster.UID,
+			NamespaceUID: nsID,
+			Name:         name,
+		}
+
+		kms.Metadata.ObjectCount++
+	}
+
+	return nil
+}
+
+func (kms *KubeModelSet) RegisterContainer(id, name, podID string) error {
+	if _, ok := kms.Containers[id]; !ok {
+		kms.Containers[id] = &Container{
+			PodUID: podID,
+			Name:   name,
+		}
+
+		kms.Metadata.ObjectCount++
+	}
+
+	return nil
+}
+
+type kubeModelSetIndexes struct {
+	namespaceNameToID map[string]string
+}

+ 237 - 0
core/pkg/model/kubemodel/kubemodel_test.go

@@ -0,0 +1,237 @@
+package kubemodel
+
+import (
+	"testing"
+	"time"
+
+	"github.com/stretchr/testify/require"
+)
+
+func TestKubeModel(t *testing.T) {
+	start := time.Now().Add(-1 * time.Hour)
+	end := time.Now()
+
+	t.Run("RegisterNamespace", func(t *testing.T) {
+		t.Run("register new namespace", func(t *testing.T) {
+			kms := NewKubeModelSet(start, end)
+			kms.Cluster = &Cluster{UID: "cluster-1"}
+
+			err := kms.RegisterNamespace("ns-1", "default")
+			require.NoError(t, err)
+
+			require.Len(t, kms.Namespaces, 1)
+			ns, ok := kms.Namespaces["ns-1"]
+			require.True(t, ok)
+			require.NotNil(t, ns)
+			require.Equal(t, "ns-1", ns.UID)
+			require.Equal(t, "default", ns.Name)
+			require.Equal(t, 1, kms.Metadata.ObjectCount)
+		})
+
+		t.Run("register duplicate namespace", func(t *testing.T) {
+			kms := NewKubeModelSet(start, end)
+			kms.Cluster = &Cluster{UID: "cluster-1"}
+
+			err := kms.RegisterNamespace("ns-1", "default")
+			require.NoError(t, err)
+			require.Equal(t, 1, kms.Metadata.ObjectCount)
+
+			err = kms.RegisterNamespace("ns-1", "default")
+			require.NoError(t, err)
+			require.Len(t, kms.Namespaces, 1)
+			require.Equal(t, 1, kms.Metadata.ObjectCount, "ObjectCount should not increment for duplicate")
+		})
+
+		t.Run("register multiple namespaces", func(t *testing.T) {
+			kms := NewKubeModelSet(start, end)
+			kms.Cluster = &Cluster{UID: "cluster-1"}
+
+			err := kms.RegisterNamespace("ns-1", "default")
+			require.NoError(t, err)
+
+			err = kms.RegisterNamespace("ns-2", "kube-system")
+			require.NoError(t, err)
+
+			require.Len(t, kms.Namespaces, 2)
+			require.Equal(t, 2, kms.Metadata.ObjectCount)
+		})
+	})
+
+	t.Run("RegisterPod", func(t *testing.T) {
+		t.Run("register new pod", func(t *testing.T) {
+			kms := NewKubeModelSet(start, end)
+			kms.Cluster = &Cluster{UID: "cluster-1"}
+			kms.RegisterNamespace("ns-1", "default")
+
+			err := kms.RegisterPod("pod-1", "nginx", "default")
+			require.NoError(t, err)
+
+			require.Len(t, kms.Pods, 1)
+			pod, ok := kms.Pods["pod-1"]
+			require.True(t, ok)
+			require.NotNil(t, pod)
+			require.Equal(t, "pod-1", pod.UID)
+			require.Equal(t, "nginx", pod.Name)
+			require.Equal(t, "ns-1", pod.NamespaceUID)
+		})
+
+		t.Run("register duplicate pod", func(t *testing.T) {
+			kms := NewKubeModelSet(start, end)
+			kms.Cluster = &Cluster{UID: "cluster-1"}
+			kms.RegisterNamespace("ns-1", "default")
+
+			err := kms.RegisterPod("pod-1", "nginx", "default")
+			require.NoError(t, err)
+
+			err = kms.RegisterPod("pod-1", "nginx", "default")
+			require.NoError(t, err)
+			require.Len(t, kms.Pods, 1)
+		})
+	})
+
+	t.Run("RegisterNode", func(t *testing.T) {
+		t.Run("register new node", func(t *testing.T) {
+			kms := NewKubeModelSet(start, end)
+			kms.Cluster = &Cluster{UID: "cluster-1"}
+
+			err := kms.RegisterNode("node-1", "worker-1")
+			require.NoError(t, err)
+
+			require.Len(t, kms.Nodes, 1)
+			node, ok := kms.Nodes["node-1"]
+			require.True(t, ok)
+			require.NotNil(t, node)
+			require.Equal(t, "node-1", node.UID)
+			require.Equal(t, "worker-1", node.Name)
+		})
+
+		t.Run("register duplicate node", func(t *testing.T) {
+			kms := NewKubeModelSet(start, end)
+			kms.Cluster = &Cluster{UID: "cluster-1"}
+
+			err := kms.RegisterNode("node-1", "worker-1")
+			require.NoError(t, err)
+
+			err = kms.RegisterNode("node-1", "worker-1")
+			require.NoError(t, err)
+			require.Len(t, kms.Nodes, 1)
+		})
+	})
+
+	t.Run("RegisterOwner", func(t *testing.T) {
+		t.Run("register new owner", func(t *testing.T) {
+			kms := NewKubeModelSet(start, end)
+			kms.Cluster = &Cluster{UID: "cluster-1"}
+			kms.RegisterNamespace("ns-1", "default")
+
+			err := kms.RegisterOwner("ctrl-1", "nginx-deployment", "default", "Deployment", true)
+			require.NoError(t, err)
+
+			require.Len(t, kms.Owners, 1)
+			owner, ok := kms.Owners["ctrl-1"]
+			require.True(t, ok)
+			require.NotNil(t, owner)
+			require.Equal(t, "ctrl-1", owner.UID)
+			require.Equal(t, "nginx-deployment", owner.Name)
+			require.Equal(t, OwnerKind("Deployment"), owner.Kind)
+			require.True(t, owner.Controller)
+		})
+
+		t.Run("register duplicate owner", func(t *testing.T) {
+			kms := NewKubeModelSet(start, end)
+			kms.Cluster = &Cluster{UID: "cluster-1"}
+			kms.RegisterNamespace("ns-1", "default")
+
+			err := kms.RegisterOwner("ctrl-1", "nginx-deployment", "default", "Deployment", true)
+			require.NoError(t, err)
+
+			err = kms.RegisterOwner("ctrl-1", "nginx-deployment", "default", "Deployment", true)
+			require.NoError(t, err)
+			require.Len(t, kms.Owners, 1)
+		})
+	})
+
+	t.Run("RegisterService", func(t *testing.T) {
+		t.Run("register new service", func(t *testing.T) {
+			kms := NewKubeModelSet(start, end)
+			kms.Cluster = &Cluster{UID: "cluster-1"}
+			kms.RegisterNamespace("ns-1", "default")
+
+			err := kms.RegisterService("svc-1", "nginx-service", "default")
+			require.NoError(t, err)
+
+			require.Len(t, kms.Services, 1)
+			svc, ok := kms.Services["svc-1"]
+			require.True(t, ok)
+			require.NotNil(t, svc)
+			require.Equal(t, "svc-1", svc.UID)
+			require.Equal(t, "nginx-service", svc.Name)
+		})
+
+		t.Run("register duplicate service", func(t *testing.T) {
+			kms := NewKubeModelSet(start, end)
+			kms.Cluster = &Cluster{UID: "cluster-1"}
+			kms.RegisterNamespace("ns-1", "default")
+
+			err := kms.RegisterService("svc-1", "nginx-service", "default")
+			require.NoError(t, err)
+
+			err = kms.RegisterService("svc-1", "nginx-service", "default")
+			require.NoError(t, err)
+			require.Len(t, kms.Services, 1)
+		})
+	})
+
+	t.Run("RegisterContainer", func(t *testing.T) {
+		t.Run("register new container", func(t *testing.T) {
+			kms := NewKubeModelSet(start, end)
+			kms.Cluster = &Cluster{UID: "cluster-1"}
+			kms.RegisterNamespace("ns-1", "default")
+			kms.RegisterPod("pod-1", "nginx", "default")
+
+			err := kms.RegisterContainer("container-1", "nginx-container", "pod-1")
+			require.NoError(t, err)
+
+			require.Len(t, kms.Containers, 1)
+			container, ok := kms.Containers["container-1"]
+			require.True(t, ok)
+			require.NotNil(t, container)
+			require.Equal(t, "nginx-container", container.Name)
+			require.Equal(t, "pod-1", container.PodUID)
+		})
+
+		t.Run("register duplicate container", func(t *testing.T) {
+			kms := NewKubeModelSet(start, end)
+			kms.Cluster = &Cluster{UID: "cluster-1"}
+			kms.RegisterNamespace("ns-1", "default")
+			kms.RegisterPod("pod-1", "nginx", "default")
+
+			err := kms.RegisterContainer("container-1", "nginx-container", "pod-1")
+			require.NoError(t, err)
+
+			err = kms.RegisterContainer("container-1", "nginx-container", "pod-1")
+			require.NoError(t, err)
+			require.Len(t, kms.Containers, 1)
+		})
+	})
+
+	t.Run("IsEmpty", func(t *testing.T) {
+		t.Run("empty KubeModelSet", func(t *testing.T) {
+			kms := NewKubeModelSet(start, end)
+			kms.Cluster = &Cluster{UID: "cluster-1"}
+
+			isEmpty := kms.IsEmpty()
+			require.True(t, isEmpty)
+		})
+
+		t.Run("KubeModelSet with namespace", func(t *testing.T) {
+			kms := NewKubeModelSet(start, end)
+			kms.Cluster = &Cluster{UID: "cluster-1"}
+			kms.RegisterNamespace("ns-1", "default")
+
+			isEmpty := kms.IsEmpty()
+			require.False(t, isEmpty)
+		})
+
+	})
+}

+ 13 - 0
core/pkg/model/kubemodel/metadata.go

@@ -0,0 +1,13 @@
+package kubemodel
+
+import (
+	"time"
+)
+
+// @bingen:generate:Metadata
+type Metadata struct {
+	CreatedAt   time.Time           `json:"createdAt"`             // @bingen:field[version=1]
+	CompletedAt time.Time           `json:"completedAt"`           // @bingen:field[version=1]
+	ObjectCount int                 `json:"objectCount"`           // @bingen:field[version=1]
+	Diagnostics []*DiagnosticResult `json:"diagnostics,omitempty"` // @bingen:field[version=1]
+}

+ 14 - 0
core/pkg/model/kubemodel/namespace.go

@@ -0,0 +1,14 @@
+package kubemodel
+
+import "time"
+
+// @bingen:generate:Namespace
+type Namespace struct {
+	UID         string            `json:"uid"`         // @bingen:field[version=1]
+	ClusterUID  string            `json:"clusterUID"`  // @bingen:field[version=1]
+	Name        string            `json:"name"`        // @bingen:field[version=1]
+	Labels      map[string]string `json:"labels"`      // @bingen:field[version=1]
+	Annotations map[string]string `json:"annotations"` // @bingen:field[version=1]
+	Start       time.Time         `json:"start"`       // @bingen:field[version=1]
+	End         time.Time         `json:"end"`         // @bingen:field[version=1]
+}

+ 25 - 0
core/pkg/model/kubemodel/node.go

@@ -0,0 +1,25 @@
+package kubemodel
+
+import "time"
+
+// @bingen:generate:Node
+type Node struct {
+	UID                          string            `json:"uid"`                          // @bingen:field[version=1]
+	ClusterUID                   string            `json:"clusterUid"`                   // @bingen:field[version=1]
+	ProviderResourceUID          string            `json:"providerResourceUid"`          // @bingen:field[version=1]
+	Name                         string            `json:"name"`                         // @bingen:field[version=1]
+	Labels                       map[string]string `json:"labels,omitempty"`             // @bingen:field[version=1]
+	Annotations                  map[string]string `json:"annotations,omitempty"`        // @bingen:field[version=1]
+	Start                        time.Time         `json:"start"`                        // @bingen:field[version=1]
+	End                          time.Time         `json:"end"`                          // @bingen:field[version=1]
+	CpuMillicoreSecondsAllocated uint64            `json:"cpuMillicoreSecondsAllocated"` // @bingen:field[version=1]
+	RAMByteSecondsAllocated      uint64            `json:"ramByteSecondsAllocated"`      // @bingen:field[version=1]
+	// PublicIPSeconds represents the cumulative public IP allocation (count × seconds) for this node.
+	// Calculated as: number of ExternalIP addresses from Kubernetes node Status.Addresses × window duration in seconds.
+	// Used for cost attribution of public IP addresses associated with the node.
+	PublicIPSecondsAllocated uint64 `json:"publicIpSecondsAllocated"` // @bingen:field[version=1]
+	CpuMillicoreUsageAverage uint64 `json:"cpuMillicoreUsageAverage"` // @bingen:field[version=1]
+	CpuMillicoreUsageMax     uint64 `json:"cpuMillicoreUsageMax"`     // @bingen:field[version=1]
+	RAMByteUsageAverage      uint64 `json:"ramByteUsageAverage"`      // @bingen:field[version=1]
+	RAMByteUsageMax          uint64 `json:"ramByteUsageMax"`          // @bingen:field[version=1]
+}

+ 29 - 0
core/pkg/model/kubemodel/owner.go

@@ -0,0 +1,29 @@
+package kubemodel
+
+import "time"
+
+// @bingen:generate:OwnerKind
+type OwnerKind string
+
+const (
+	OwnerKindDeployment  OwnerKind = "deployment"
+	OwnerKindStatefulSet OwnerKind = "statefulset"
+	OwnerKindDaemonSet   OwnerKind = "daemonset"
+	OwnerKindJob         OwnerKind = "job"
+	OwnerKindCronJob     OwnerKind = "cronjob"
+	OwnerKindReplicaSet  OwnerKind = "replicaset"
+)
+
+// Owner represents a Kubernetes resource owner
+// @bingen:generate:Owner
+type Owner struct {
+	UID         string            `json:"uid"`                   // @bingen:field[version=1]
+	OwnerUID    string            `json:"ownerUid"`              // @bingen:field[version=1]
+	Name        string            `json:"name"`                  // @bingen:field[version=1]
+	Kind        OwnerKind         `json:"kind"`                  // @bingen:field[version=1]
+	Controller  bool              `json:"controller"`            // @bingen:field[version=1]
+	Labels      map[string]string `json:"labels,omitempty"`      // @bingen:field[version=1]
+	Annotations map[string]string `json:"annotations,omitempty"` // @bingen:field[version=1]
+	Start       time.Time         `json:"start"`                 // @bingen:field[version=1]
+	End         time.Time         `json:"end"`                   // @bingen:field[version=1]
+}

+ 20 - 0
core/pkg/model/kubemodel/pod.go

@@ -0,0 +1,20 @@
+package kubemodel
+
+import "time"
+
+// @bingen:generate:Pod
+type Pod struct {
+	UID                        string            `json:"uid"`                        // @bingen:field[version=1]
+	NamespaceUID               string            `json:"namespaceUid"`               // @bingen:field[version=1]
+	OwnerUID                   string            `json:"ownerUid"`                   // @bingen:field[version=1]
+	NodeUID                    string            `json:"nodeUid"`                    // @bingen:field[version=1]
+	Name                       string            `json:"name"`                       // @bingen:field[version=1]
+	Labels                     map[string]string `json:"labels,omitempty"`           // @bingen:field[version=1]
+	Annotations                map[string]string `json:"annotations,omitempty"`      // @bingen:field[version=1]
+	Start                      time.Time         `json:"start"`                      // @bingen:field[version=1]
+	End                        time.Time         `json:"end"`                        // @bingen:field[version=1]
+	CpuMillicoreUsageMax       uint64            `json:"cpuMillicoreUsageMax"`       // @bingen:field[version=1]
+	RAMByteUsageMax            uint64            `json:"ramByteUsageMax"`            // @bingen:field[version=1]
+	NetworkTransferBytes       uint64            `json:"networkTransferBytes"`       // @bingen:field[version=1]
+	NetworkReceiveBytes        uint64            `json:"networkReceiveBytes"`        // @bingen:field[version=1]
+}

+ 14 - 0
core/pkg/model/kubemodel/provider.go

@@ -0,0 +1,14 @@
+package kubemodel
+
+// @bingen:generate:Provider
+type Provider string
+
+const (
+	ProviderAWS          Provider = "aws"
+	ProviderGCP          Provider = "gcp"
+	ProviderAzure        Provider = "azure"
+	ProviderOnPremises   Provider = "on_premises"
+	ProviderAlibaba      Provider = "alibaba"
+	ProviderDigitalOcean Provider = "digitalocean"
+	ProviderOracle       Provider = "oracle"
+)

+ 39 - 0
core/pkg/model/kubemodel/service.go

@@ -0,0 +1,39 @@
+package kubemodel
+
+import "time"
+
+// @bingen:generate:ServiceType
+type ServiceType string
+
+const (
+	ServiceTypeClusterIP    ServiceType = "ClusterIP"
+	ServiceTypeNodePort     ServiceType = "NodePort"
+	ServiceTypeLoadBalancer ServiceType = "LoadBalancer"
+	ServiceTypeExternalName ServiceType = "ExternalName"
+)
+
+// @bingen:generate:ServicePort
+type ServicePort struct {
+	Name       string `json:"name"`       // @bingen:field[version=1]
+	Port       uint16 `json:"port"`       // @bingen:field[version=1]
+	TargetPort uint16 `json:"targetPort"` // @bingen:field[version=1]
+	NodePort   uint16 `json:"nodePort"`   // @bingen:field[version=1]
+	Protocol   string `json:"protocol"`   // @bingen:field[version=1]
+}
+
+// @bingen:generate:Service
+type Service struct {
+	UID                  string            `json:"uid"`                   // @bingen:field[version=1]
+	ClusterUID           string            `json:"clusterUid"`            // @bingen:field[version=1]
+	NamespaceUID         string            `json:"namespaceUid"`          // @bingen:field[version=1]
+	Name                 string            `json:"name"`                  // @bingen:field[version=1]
+	Type                 ServiceType       `json:"type"`                  // @bingen:field[version=1]
+	Hostname             string            `json:"hostname,omitempty"`    // @bingen:field[version=1]
+	Labels               map[string]string `json:"labels,omitempty"`      // @bingen:field[version=1]
+	Annotations          map[string]string `json:"annotations,omitempty"` // @bingen:field[version=1]
+	Ports                []ServicePort     `json:"ports,omitempty"`       // @bingen:field[version=1]
+	Start                time.Time         `json:"start"`                 // @bingen:field[version=1]
+	End                  time.Time         `json:"end"`                   // @bingen:field[version=1]
+	NetworkTransferBytes uint64            `json:"networkTransferBytes"`  // @bingen:field[version=1]
+	NetworkReceiveBytes  uint64            `json:"networkReceiveBytes"`   // @bingen:field[version=1]
+}

+ 9 - 0
core/pkg/model/kubemodel/window.go

@@ -0,0 +1,9 @@
+package kubemodel
+
+import "time"
+
+// @bingen:generate:Window
+type Window struct {
+	Start time.Time `json:"start"` // @bingen:field[version=1]
+	End   time.Time `json:"end"`   // @bingen:field[version=1]
+}