| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132 |
- package kubemodel
- import "github.com/opencost/opencost/core/pkg/stats"
- type ContainerCosts struct {
- Container
- CPUCost float64
- RAMCost float64
- StorageCost float64
- }
- // ApplyContainerCosts is a simplified example of a consumer of the
- // KubeModelSet data structure, which applies Node and PersistentVolume
- // pricing to container resources to assign container costs.
- func ApplyContainerCosts(kms *KubeModelSet, pm PricingModel, out chan map[string]*ContainerCosts) {
- // 1. Option that uses the flat structure
- flatAlgorithm(kms, pm, out)
- // 2. Option that uses the cost-priority hierarchical structure
- hierarchyAlgorithm(kms, pm, out)
- }
- // O(C * avg(PVC))
- // 42 lines
- // 2 loops
- // 3 max indentation
- func flatAlgorithm(kms *KubeModelSet, pm PricingModel, out chan map[string]*ContainerCosts) {
- // O(1) -- slight advantage in being able to allocate memory for the whole set of Containers
- ccs := make(map[string]*ContainerCosts, len(kms.Containers))
- // O(C)
- for _, container := range kms.Containers {
- // O(1)
- cc := &ContainerCosts{Container: *container}
- // O(1)
- pod := kms.Pods[container.PodUID]
- // O(1)
- node := kms.Nodes[pod.NodeUID]
- // O(1) -- presumably
- nodePricing := pm.GetNodePricing(node)
- // O(1)
- cc.CPUCost = float64(container.CPUAllocationMillicoreSeconds) * nodePricing[ResourceCPU].Price
- cc.RAMCost = float64(container.RAMAllocationByteSeconds) * nodePricing[ResourceMemory].Price
- // O(PVC)
- for pvcUID, mountedVolume := range container.PVCMounts {
- // O(1)
- pvc := kms.PersistentVolumeClaims[pvcUID]
- // O(1)
- pv := kms.PersistentVolumes[pvc.PersistentVolumeUID]
- // O(1)
- pvPricing := pm.GetPersistentVolumePricing(pv)
- // O(1)
- cc.StorageCost += float64(mountedVolume.StorageCapacityBytes) * pvPricing[ResourceStorage].Price
- }
- // O(1)
- ccs[container.UID] = cc
- }
- // O(1)
- out <- ccs
- }
- // O(N * avg(P) * avg(C) * avg(PVC)) == O(C * avg(PVC))
- // 42 lines
- // 4 loops
- // 5 max indentation
- func hierarchyAlgorithm(kms *KubeModelSet, pm PricingModel, out chan map[string]*ContainerCosts) {
- // O(1) -- total number of containers would have to be indexed / cached
- ccs := map[string]*ContainerCosts{}
- // O(N)
- for _, node := range kms.Cluster.Nodes {
- // O(avg(P))
- for _, pod := range node.Pods {
- // O(avg(C))
- for _, container := range pod.Containers {
- // O(1)
- cc := &ContainerCosts{Container: *container}
- // O(1) -- presumably
- nodePricing := pm.GetNodePricing(node)
- // O(1)
- cc.CPUCost = container.Resources[ResourceCPU].Values[stats.Val] * nodePricing[ResourceCPU].Price
- cc.RAMCost = container.Resources[ResourceMemory].Values[stats.Val] * nodePricing[ResourceMemory].Price
- // O(PVC) -- going to need to use the same access pattern as the
- // flat data structure, anyways, unless somehow PVCs / PVs will
- // be nested within Containers (e.g. under VolumeMounts?)
- for pvcUID, storage := range container.VolumeMounts {
- // O(1)
- pvc := kms.PersistentVolumeClaims[pvcUID]
- // O(1)
- pv := kms.PersistentVolumes[pvc.PersistentVolumeUID]
- // O(1)
- pvPricing := pm.GetPersistentVolumePricing(pv)
- // O(1)
- cc.StorageCost += storage.Values[stats.Val] * pvPricing[ResourceStorage].Price
- }
- ccs[container.UID] = cc
- }
- }
- }
- out <- ccs
- }
- type PricingModel interface {
- GetNodePricing(node *Node) Pricing
- GetPersistentVolumePricing(pv *PersistentVolume) Pricing
- }
- type Pricing map[Resource]ResourcePricing
- type ResourcePricing struct {
- Resource Resource
- Unit Unit
- Price float64
- }
|