Просмотр исходного кода

Scaffolding out ComputeKubeModel

Niko Kovacevic 6 месяцев назад
Родитель
Сommit
f1b6d3d82c

+ 22 - 8
core/pkg/model/kubemodel/kubemodel.go

@@ -6,16 +6,30 @@ import (
 )
 
 type KubeModelSet struct {
+	Metadata       *KubeModelSetMetadata
 	Window         Window
 	Cluster        *Cluster
-	Namespaces     []*Namespace
-	ResourceQuotas []*ResourceQuota
-	Metadata       *KubeModelSetMetadata
+	Namespaces     map[string]*Namespace
+	ResourceQuotas map[string]*ResourceQuota
+}
+
+func NewKubeModelSet(start, end time.Time) *KubeModelSet {
+	return &KubeModelSet{
+		Metadata: &KubeModelSetMetadata{
+			CreatedAt: time.Now().UTC(),
+		},
+		Window: Window{
+			Start: start,
+			End:   end,
+		},
+		Namespaces:     map[string]*Namespace{},
+		ResourceQuotas: map[string]*ResourceQuota{},
+	}
 }
 
 // TODO: determine what "IsEmpty()" should mean here
 func (kms *KubeModelSet) IsEmpty() bool {
-	return kms == nil
+	return kms == nil || kms.Cluster == nil
 }
 
 // TODO: generate bingen codec
@@ -24,8 +38,8 @@ func (kms *KubeModelSet) MarshalBinary() ([]byte, error) {
 }
 
 type KubeModelSetMetadata struct {
-	CreatedAt  time.Time
-	DataSource string
-	Warnings   []string
-	Errors     []error
+	CreatedAt   time.Time
+	ObjectCount int
+	Errors      []error
+	Warnings    []string
 }

+ 2 - 22
core/pkg/model/kubemodel/resource.go

@@ -1,7 +1,5 @@
 package kubemodel
 
-import "fmt"
-
 type Resource string
 
 const (
@@ -15,13 +13,13 @@ type Unit string
 const (
 	UnitCPUm       = "m"
 	UnitMemoryMi   = "Mi"
-	UnitGPU        = "gpu"
+	UnitGPU        = "GPU"
 	UnitByte       = "B"
 	UnitGB         = "GB"
 	UnitTimeHr     = "hr"
 	UnitCPUmHr     = "m-hr"
 	UnitMemoryMiHr = "Mi-hr"
-	UnitGPUHr      = "gpu-hr"
+	UnitGPUHr      = "GPU-hr"
 	UnitGBHr       = "GB-hr"
 )
 
@@ -30,21 +28,3 @@ type ResourceQuantity struct {
 	Unit     Unit
 	Quantity float64
 }
-
-type ResourcePrice struct {
-	Resource Resource
-	Unit     Unit
-	Price    float64
-}
-
-func (rq ResourceQuantity) ApplyPrice(rp ResourcePrice) (float64, error) {
-	if rp.Resource != rq.Resource {
-		return 0.0, fmt.Errorf("mismatched resources: %s != %s", rq.Unit, rp.Unit)
-	}
-
-	if rp.Unit != rq.Unit {
-		return 0.0, fmt.Errorf("mismatched units: %s != %s", rq.Unit, rp.Unit)
-	}
-
-	return rq.Quantity * rp.Price, nil
-}

+ 2 - 2
core/pkg/model/kubemodel/window.go

@@ -3,6 +3,6 @@ package kubemodel
 import "time"
 
 type Window struct {
-	Start      time.Time
-	Resolution time.Duration
+	Start time.Time
+	End   time.Time
 }

+ 59 - 3
pkg/costmodel/kubemodel.go

@@ -1,18 +1,74 @@
 package costmodel
 
 import (
-	"errors"
+	"fmt"
 	"time"
 
+	"github.com/opencost/opencost/core/pkg/env"
 	"github.com/opencost/opencost/core/pkg/model/kubemodel"
 )
 
+const logTimeFmt string = "2006-01-02T15:04:05"
+
 // ComputeKubeModel uses the CostModel instance to compute an KubeModelSet
 // for the window defined by the given start and end times. The KubeModels
 // returned are unaggregated (i.e. down to the container level).
 func (cm *CostModel) ComputeKubeModel(start, end time.Time) (*kubemodel.KubeModelSet, error) {
+	// Initialize new KubeModelSet for requested Window
+	kms := kubemodel.NewKubeModelSet(start, end)
+
+	// Query CostModel for each set of objects
+	var err error
+
+	kms.Cluster, err = cm.kmComputeCluster(start, end)
+	if err != nil {
+		kms.Metadata.Errors = append(kms.Metadata.Errors, err)
+		return kms, fmt.Errorf("error computing kubemodel.Cluster for (%s, %s): %w", start.Format(logTimeFmt), end.Format(logTimeFmt), err)
+	}
+	kms.Metadata.ObjectCount += 1
+
+	kms.Namespaces, err = cm.kmComputeNamespaces(start, end)
+	if err != nil {
+		kms.Metadata.Errors = append(kms.Metadata.Errors, err)
+	}
+	kms.Metadata.ObjectCount += len(kms.Namespaces)
+
+	kms.ResourceQuotas, err = cm.kmComputeResourceQuotas(start, end)
+	if err != nil {
+		kms.Metadata.Errors = append(kms.Metadata.Errors, err)
+	}
+	kms.Metadata.ObjectCount += len(kms.ResourceQuotas)
+
+	return kms, nil
+}
+
+func (cm *CostModel) kmComputeCluster(start, end time.Time) (*kubemodel.Cluster, error) {
+
+	// TODO: determine where Cluster data comes from
+	//  - Should it come from direct queries?
+	//  - Or should it come from pre-processed data from other objects?
+
+	return &kubemodel.Cluster{
+		ID:   env.GetClusterID(), // TODO: should we instead grab these from Metrics()?
+		Name: env.GetClusterID(), // TODO: do we still want to use this env var for Name?
+	}, nil
+}
+
+func (cm *CostModel) kmComputeNamespaces(start, end time.Time) (map[string]*kubemodel.Namespace, error) {
+
+	// TODO: determine where Namespace data comes from
+	//  - Should it come from direct queries?
+	//  - Or should it come from pre-processed data from other objects?
+
+	nsMap := map[string]*kubemodel.Namespace{}
+
+	return nsMap, nil
+}
+
+func (cm *CostModel) kmComputeResourceQuotas(start, end time.Time) (map[string]*kubemodel.ResourceQuota, error) {
+	rqMap := map[string]*kubemodel.ResourceQuota{}
 
-	// TODO: use cm.DataSource to query for metrics and hydrate a *kubemodel.KubeModelSet
+	// TODO: make ResourceQuota queries when they are available in OpenCost
 
-	return nil, errors.New("not implemented")
+	return rqMap, nil
 }