Explorar el Código

Move PVBreakdown to Allocation and remove PVCost and PVByteHours

Sean Holcomb hace 5 años
padre
commit
30ba5cb49c

+ 18 - 10
pkg/costmodel/allocation.go

@@ -386,14 +386,11 @@ func (cm *CostModel) ComputeAllocation(start, end time.Time, resolution time.Dur
 
 					// Apply the size and cost of the PV to the allocation, each
 					// weighted by count (i.e. the number of containers in the pod)
-					alloc.PVByteHours += pvc.Bytes * hrs / count
-					alloc.PVCost += cost / count
-
 					// record the amount of total PVBytes Hours attributable to a given PV
-					if alloc.Properties.PVBreakdown == nil {
-						alloc.Properties.PVBreakdown = map[string]kubecost.PVUsage{}
+					if alloc.PVBreakdown == nil {
+						alloc.PVBreakdown = map[string]kubecost.PVUsage{}
 					}
-					alloc.Properties.PVBreakdown[pvc.Volume.Name] = kubecost.PVUsage{
+					alloc.PVBreakdown[pvc.Volume.Name] = kubecost.PVUsage{
 						ByteHours: pvc.Bytes * hrs / count,
 						Cost:      cost / count,
 					}
@@ -1673,8 +1670,13 @@ func applyUnmountedPVs(window kubecost.Window, podMap map[podKey]*Pod, pvMap map
 		podMap[key].Allocations[container].Properties.Namespace = namespace
 		podMap[key].Allocations[container].Properties.Pod = pod
 		podMap[key].Allocations[container].Properties.Container = container
-		podMap[key].Allocations[container].PVByteHours = unmountedPVBytes[cluster] * window.Minutes() / 60.0
-		podMap[key].Allocations[container].PVCost = amount
+		unmountedBreakDown := map[string]kubecost.PVUsage{
+			kubecost.UnmountedSuffix: {
+				ByteHours: unmountedPVBytes[cluster] * window.Minutes() / 60.0,
+				Cost:      amount,
+			},
+		}
+		podMap[key].Allocations[container].AddPVBreakDown(unmountedBreakDown)
 	}
 }
 
@@ -1716,8 +1718,14 @@ func applyUnmountedPVCs(window kubecost.Window, podMap map[podKey]*Pod, pvcMap m
 		podMap[podKey].Allocations[container].Properties.Namespace = namespace
 		podMap[podKey].Allocations[container].Properties.Pod = pod
 		podMap[podKey].Allocations[container].Properties.Container = container
-		podMap[podKey].Allocations[container].PVByteHours = unmountedPVCBytes[key] * window.Minutes() / 60.0
-		podMap[podKey].Allocations[container].PVCost = amount
+		unmountedBreakDown := map[string]kubecost.PVUsage{
+			kubecost.UnmountedSuffix: {
+				ByteHours: unmountedPVCBytes[key] * window.Minutes() / 60.0,
+				Cost:      amount,
+			},
+		}
+		podMap[podKey].Allocations[container].AddPVBreakDown(unmountedBreakDown)
+
 	}
 }
 

+ 72 - 17
pkg/kubecost/allocation.go

@@ -65,8 +65,7 @@ type Allocation struct {
 	GPUCostAdjustment      float64               `json:"gpuCostAdjustment"`
 	NetworkCost            float64               `json:"networkCost"`
 	LoadBalancerCost       float64               `json:"loadBalancerCost"`
-	PVByteHours            float64               `json:"pvByteHours"`
-	PVCost                 float64               `json:"pvCost"`
+	PVBreakdown            map[string]PVUsage    `json:"pvBreakDown"`
 	PVCostAdjustment       float64               `json:"pvCostAdjustment"`
 	RAMByteHours           float64               `json:"ramByteHours"`
 	RAMBytesRequestAverage float64               `json:"ramByteRequestAverage"`
@@ -108,6 +107,13 @@ type RawAllocationOnlyData struct {
 	RAMBytesUsageMax float64 `json:"ramByteUsageMax"`
 }
 
+// PVUsage contains the byte hour usage
+// and cost of an Allocation for a single PV
+type PVUsage struct {
+	ByteHours float64 `json:"byteHours"`
+	Cost      float64 `json:"cost"`
+}
+
 // AllocationMatchFunc is a function that can be used to match Allocations by
 // returning true for any given Allocation if a condition is met.
 type AllocationMatchFunc func(*Allocation) bool
@@ -137,6 +143,11 @@ func (a *Allocation) Clone() *Allocation {
 		return nil
 	}
 
+	pvBreakdown := make(map[string]PVUsage)
+	for k, v := range a.PVBreakdown {
+		pvBreakdown[k] = v
+	}
+
 	return &Allocation{
 		Name:                   a.Name,
 		Properties:             a.Properties.Clone(),
@@ -153,8 +164,7 @@ func (a *Allocation) Clone() *Allocation {
 		GPUCostAdjustment:      a.GPUCostAdjustment,
 		NetworkCost:            a.NetworkCost,
 		LoadBalancerCost:       a.LoadBalancerCost,
-		PVByteHours:            a.PVByteHours,
-		PVCost:                 a.PVCost,
+		PVBreakdown:            pvBreakdown,
 		PVCostAdjustment:       a.PVCostAdjustment,
 		RAMByteHours:           a.RAMByteHours,
 		RAMBytesRequestAverage: a.RAMBytesRequestAverage,
@@ -227,12 +237,7 @@ func (a *Allocation) Equal(that *Allocation) bool {
 	if !util.IsApproximately(a.LoadBalancerCost, that.LoadBalancerCost) {
 		return false
 	}
-	if !util.IsApproximately(a.PVByteHours, that.PVByteHours) {
-		return false
-	}
-	if !util.IsApproximately(a.PVCost, that.PVCost) {
-		return false
-	}
+
 	if !util.IsApproximately(a.PVCostAdjustment, that.PVCostAdjustment) {
 		return false
 	}
@@ -268,6 +273,19 @@ func (a *Allocation) Equal(that *Allocation) bool {
 		}
 	}
 
+	aPVBreakdown := a.PVBreakdown
+	thatPVBreakdown := that.PVBreakdown
+	if len(aPVBreakdown) == len(thatPVBreakdown) {
+		for k, pv := range aPVBreakdown {
+			tv, ok := thatPVBreakdown[k]
+			if !ok || tv != pv {
+				return false
+			}
+		}
+	} else {
+		return false
+	}
+
 	return true
 }
 
@@ -289,7 +307,23 @@ func (a *Allocation) RAMTotalCost() float64 {
 }
 
 func (a *Allocation) PVTotalCost() float64 {
-	return a.PVCost + a.PVCostAdjustment
+	return a.PVCost() + a.PVCostAdjustment
+}
+
+func (a *Allocation) PVCost() float64 {
+	cost := 0.0
+	for _, pv := range a.PVBreakdown {
+		cost += pv.Cost
+	}
+	return cost
+}
+
+func (a *Allocation) PVByteHours() float64 {
+	byteHours := 0.0
+	for _, pv := range a.PVBreakdown {
+		byteHours += pv.ByteHours
+	}
+	return byteHours
 }
 
 // CPUEfficiency is the ratio of usage to request. If there is no request and
@@ -363,7 +397,7 @@ func (a *Allocation) PVBytes() float64 {
 	if a.Minutes() <= 0.0 {
 		return 0.0
 	}
-	return a.PVByteHours / (a.Minutes() / 60.0)
+	return a.PVByteHours() / (a.Minutes() / 60.0)
 }
 
 // MarshalJSON implements json.Marshaler interface
@@ -389,8 +423,9 @@ func (a *Allocation) MarshalJSON() ([]byte, error) {
 	jsonEncodeFloat64(buffer, "networkCost", a.NetworkCost, ",")
 	jsonEncodeFloat64(buffer, "loadBalancerCost", a.LoadBalancerCost, ",")
 	jsonEncodeFloat64(buffer, "pvBytes", a.PVBytes(), ",")
-	jsonEncodeFloat64(buffer, "pvByteHours", a.PVByteHours, ",")
-	jsonEncodeFloat64(buffer, "pvCost", a.PVCost, ",")
+	jsonEncodeFloat64(buffer, "pvByteHours", a.PVByteHours(), ",")
+	jsonEncodeFloat64(buffer, "pvCost", a.PVCost(), ",")
+	jsonEncode(buffer, "pvBreakdown", a.PVBreakdown, ",")
 	jsonEncodeFloat64(buffer, "pvCostAdjustment", a.PVCostAdjustment, ",")
 	jsonEncodeFloat64(buffer, "ramBytes", a.RAMBytes(), ",")
 	jsonEncodeFloat64(buffer, "ramByteRequestAverage", a.RAMBytesRequestAverage, ",")
@@ -515,18 +550,19 @@ func (a *Allocation) add(that *Allocation) {
 	a.CPUCoreHours += that.CPUCoreHours
 	a.GPUHours += that.GPUHours
 	a.RAMByteHours += that.RAMByteHours
-	a.PVByteHours += that.PVByteHours
 
 	// Sum all cumulative cost fields
 	a.CPUCost += that.CPUCost
 	a.GPUCost += that.GPUCost
 	a.RAMCost += that.RAMCost
-	a.PVCost += that.PVCost
 	a.NetworkCost += that.NetworkCost
 	a.LoadBalancerCost += that.LoadBalancerCost
 	a.SharedCost += that.SharedCost
 	a.ExternalCost += that.ExternalCost
 
+	// Sum PV Breakdown
+	a.AddPVBreakDown(that.PVBreakdown)
+
 	// Sum all cumulative adjustment fields
 	a.CPUCostAdjustment += that.CPUCostAdjustment
 	a.RAMCostAdjustment += that.RAMCostAdjustment
@@ -538,6 +574,25 @@ func (a *Allocation) add(that *Allocation) {
 	a.RawAllocationOnly = nil
 }
 
+// AddPVBreakDown adds contents of pvBreakdown to Allocation PVBreakdown
+// Property, creating new PVUsage where necessary
+func (a *Allocation) AddPVBreakDown(pvBreakdown map[string]PVUsage) {
+	if pvBreakdown != nil {
+		if a.PVBreakdown == nil {
+			a.PVBreakdown = map[string]PVUsage{}
+		}
+		for pvName, pv := range pvBreakdown {
+			apv, ok := a.PVBreakdown[pvName]
+			if !ok {
+				apv = PVUsage{}
+			}
+			apv.Cost += pv.Cost
+			apv.ByteHours += pv.ByteHours
+			a.PVBreakdown[pvName] = apv
+		}
+	}
+}
+
 // AllocationSet stores a set of Allocations, each with a unique name, that share
 // a window. An AllocationSet is mutable, so treat it like a threadsafe map.
 type AllocationSet struct {
@@ -1615,7 +1670,7 @@ func (a *Allocation) reconcileNodes(nodeByProviderID map[string]*Node) {
 }
 
 func (a *Allocation) reconcileDisks(diskByName map[string]*Disk) {
-	pvBreakdown := a.Properties.PVBreakdown
+	pvBreakdown := a.PVBreakdown
 	if pvBreakdown == nil {
 		// No PV usage to reconcile
 		return

+ 102 - 90
pkg/kubecost/allocation_test.go

@@ -33,21 +33,25 @@ func NewUnitAllocation(name string, start time.Time, resolution time.Duration, p
 	end := start.Add(resolution)
 
 	alloc := &Allocation{
-		Name:                   name,
-		Properties:             properties,
-		Window:                 NewWindow(&start, &end).Clone(),
-		Start:                  start,
-		End:                    end,
-		CPUCoreHours:           1,
-		CPUCost:                1,
-		CPUCoreRequestAverage:  1,
-		CPUCoreUsageAverage:    1,
-		GPUHours:               1,
-		GPUCost:                1,
-		NetworkCost:            1,
-		LoadBalancerCost:       1,
-		PVByteHours:            1,
-		PVCost:                 1,
+		Name:                  name,
+		Properties:            properties,
+		Window:                NewWindow(&start, &end).Clone(),
+		Start:                 start,
+		End:                   end,
+		CPUCoreHours:          1,
+		CPUCost:               1,
+		CPUCoreRequestAverage: 1,
+		CPUCoreUsageAverage:   1,
+		GPUHours:              1,
+		GPUCost:               1,
+		NetworkCost:           1,
+		LoadBalancerCost:      1,
+		PVBreakdown: map[string]PVUsage{
+			"disk": {
+				ByteHours: 1,
+				Cost:      1,
+			},
+		},
 		RAMByteHours:           1,
 		RAMCost:                1,
 		RAMBytesRequestAverage: 1,
@@ -60,8 +64,7 @@ func NewUnitAllocation(name string, start time.Time, resolution time.Duration, p
 
 	// If idle allocation, remove non-idle costs, but maintain total cost
 	if alloc.IsIdle() {
-		alloc.PVByteHours = 0.0
-		alloc.PVCost = 0.0
+		alloc.PVBreakdown = nil
 		alloc.NetworkCost = 0.0
 		alloc.LoadBalancerCost = 0.0
 		alloc.CPUCoreHours += 1.0
@@ -105,19 +108,23 @@ func TestAllocation_Add(t *testing.T) {
 	e1 := time.Date(2021, time.January, 1, 12, 0, 0, 0, time.UTC)
 	hrs1 := e1.Sub(s1).Hours()
 	a1 := &Allocation{
-		Start:                  s1,
-		End:                    e1,
-		Properties:             &AllocationProperties{},
-		CPUCoreHours:           2.0 * hrs1,
-		CPUCoreRequestAverage:  2.0,
-		CPUCoreUsageAverage:    1.0,
-		CPUCost:                2.0 * hrs1 * cpuPrice,
-		CPUCostAdjustment:      3.0,
-		GPUHours:               1.0 * hrs1,
-		GPUCost:                1.0 * hrs1 * gpuPrice,
-		GPUCostAdjustment:      2.0,
-		PVByteHours:            100.0 * gib * hrs1,
-		PVCost:                 100.0 * hrs1 * pvPrice,
+		Start:                 s1,
+		End:                   e1,
+		Properties:            &AllocationProperties{},
+		CPUCoreHours:          2.0 * hrs1,
+		CPUCoreRequestAverage: 2.0,
+		CPUCoreUsageAverage:   1.0,
+		CPUCost:               2.0 * hrs1 * cpuPrice,
+		CPUCostAdjustment:     3.0,
+		GPUHours:              1.0 * hrs1,
+		GPUCost:               1.0 * hrs1 * gpuPrice,
+		GPUCostAdjustment:     2.0,
+		PVBreakdown: map[string]PVUsage{
+			"disk1": {
+				ByteHours: 100.0 * gib * hrs1,
+				Cost:      100.0 * hrs1 * pvPrice,
+			},
+		},
 		PVCostAdjustment:       4.0,
 		RAMByteHours:           8.0 * gib * hrs1,
 		RAMBytesRequestAverage: 8.0 * gib,
@@ -143,8 +150,6 @@ func TestAllocation_Add(t *testing.T) {
 		CPUCost:                1.0 * hrs2 * cpuPrice,
 		GPUHours:               0.0,
 		GPUCost:                0.0,
-		PVByteHours:            0,
-		PVCost:                 0,
 		RAMByteHours:           8.0 * gib * hrs2,
 		RAMBytesRequestAverage: 0.0,
 		RAMBytesUsageAverage:   8.0 * gib,
@@ -192,8 +197,8 @@ func TestAllocation_Add(t *testing.T) {
 	if !util.IsApproximately(a1.RAMCostAdjustment+a2.RAMCostAdjustment, act.RAMCostAdjustment) {
 		t.Fatalf("Allocation.Add: expected %f; actual %f", a1.RAMCostAdjustment+a2.RAMCostAdjustment, act.RAMCostAdjustment)
 	}
-	if !util.IsApproximately(a1.PVCost+a2.PVCost, act.PVCost) {
-		t.Fatalf("Allocation.Add: expected %f; actual %f", a1.PVCost+a2.PVCost, act.PVCost)
+	if !util.IsApproximately(a1.PVCost()+a2.PVCost(), act.PVCost()) {
+		t.Fatalf("Allocation.Add: expected %f; actual %f", a1.PVCost()+a2.PVCost(), act.PVCost())
 	}
 	if !util.IsApproximately(a1.NetworkCost+a2.NetworkCost, act.NetworkCost) {
 		t.Fatalf("Allocation.Add: expected %f; actual %f", a1.NetworkCost+a2.NetworkCost, act.NetworkCost)
@@ -215,8 +220,8 @@ func TestAllocation_Add(t *testing.T) {
 	if !util.IsApproximately(a1.RAMByteHours+a2.RAMByteHours, act.RAMByteHours) {
 		t.Fatalf("Allocation.Add: expected %f; actual %f", a1.RAMByteHours+a2.RAMByteHours, act.RAMByteHours)
 	}
-	if !util.IsApproximately(a1.PVByteHours+a2.PVByteHours, act.PVByteHours) {
-		t.Fatalf("Allocation.Add: expected %f; actual %f", a1.PVByteHours+a2.PVByteHours, act.PVByteHours)
+	if !util.IsApproximately(a1.PVByteHours()+a2.PVByteHours(), act.PVByteHours()) {
+		t.Fatalf("Allocation.Add: expected %f; actual %f", a1.PVByteHours()+a2.PVByteHours(), act.PVByteHours())
 	}
 
 	// Minutes should be the duration between min(starts) and max(ends)
@@ -275,19 +280,23 @@ func TestAllocation_Share(t *testing.T) {
 	e1 := time.Date(2021, time.January, 1, 12, 0, 0, 0, time.UTC)
 	hrs1 := e1.Sub(s1).Hours()
 	a1 := &Allocation{
-		Start:                  s1,
-		End:                    e1,
-		Properties:             &AllocationProperties{},
-		CPUCoreHours:           2.0 * hrs1,
-		CPUCoreRequestAverage:  2.0,
-		CPUCoreUsageAverage:    1.0,
-		CPUCost:                2.0 * hrs1 * cpuPrice,
-		CPUCostAdjustment:      3.0,
-		GPUHours:               1.0 * hrs1,
-		GPUCost:                1.0 * hrs1 * gpuPrice,
-		GPUCostAdjustment:      2.0,
-		PVByteHours:            100.0 * gib * hrs1,
-		PVCost:                 100.0 * hrs1 * pvPrice,
+		Start:                 s1,
+		End:                   e1,
+		Properties:            &AllocationProperties{},
+		CPUCoreHours:          2.0 * hrs1,
+		CPUCoreRequestAverage: 2.0,
+		CPUCoreUsageAverage:   1.0,
+		CPUCost:               2.0 * hrs1 * cpuPrice,
+		CPUCostAdjustment:     3.0,
+		GPUHours:              1.0 * hrs1,
+		GPUCost:               1.0 * hrs1 * gpuPrice,
+		GPUCostAdjustment:     2.0,
+		PVBreakdown: map[string]PVUsage{
+			"disk1": {
+				ByteHours: 100.0 * gib * hrs1,
+				Cost:      100.0 * hrs1 * pvPrice,
+			},
+		},
 		PVCostAdjustment:       4.0,
 		RAMByteHours:           8.0 * gib * hrs1,
 		RAMBytesRequestAverage: 8.0 * gib,
@@ -312,8 +321,6 @@ func TestAllocation_Share(t *testing.T) {
 		CPUCost:                1.0 * hrs2 * cpuPrice,
 		GPUHours:               0.0,
 		GPUCost:                0.0,
-		PVByteHours:            0,
-		PVCost:                 0,
 		RAMByteHours:           8.0 * gib * hrs2,
 		RAMBytesRequestAverage: 0.0,
 		RAMBytesUsageAverage:   8.0 * gib,
@@ -376,8 +383,8 @@ func TestAllocation_Share(t *testing.T) {
 	if !util.IsApproximately(a1.RAMByteHours, act.RAMByteHours) {
 		t.Fatalf("Allocation.Share: expected %f; actual %f", a1.RAMByteHours, act.RAMByteHours)
 	}
-	if !util.IsApproximately(a1.PVByteHours, act.PVByteHours) {
-		t.Fatalf("Allocation.Share: expected %f; actual %f", a1.PVByteHours, act.PVByteHours)
+	if !util.IsApproximately(a1.PVByteHours(), act.PVByteHours()) {
+		t.Fatalf("Allocation.Share: expected %f; actual %f", a1.PVByteHours(), act.PVByteHours())
 	}
 
 	// Minutes should match before
@@ -435,21 +442,25 @@ func TestAllocation_MarshalJSON(t *testing.T) {
 			Pod:       "pod1",
 			Container: "container1",
 		},
-		Window:                 NewWindow(&start, &end),
-		Start:                  start,
-		End:                    end,
-		CPUCoreHours:           2.0 * hrs,
-		CPUCoreRequestAverage:  2.0,
-		CPUCoreUsageAverage:    1.0,
-		CPUCost:                2.0 * hrs * cpuPrice,
-		CPUCostAdjustment:      3.0,
-		GPUHours:               1.0 * hrs,
-		GPUCost:                1.0 * hrs * gpuPrice,
-		GPUCostAdjustment:      2.0,
-		NetworkCost:            0.05,
-		LoadBalancerCost:       0.02,
-		PVByteHours:            100.0 * gib * hrs,
-		PVCost:                 100.0 * hrs * pvPrice,
+		Window:                NewWindow(&start, &end),
+		Start:                 start,
+		End:                   end,
+		CPUCoreHours:          2.0 * hrs,
+		CPUCoreRequestAverage: 2.0,
+		CPUCoreUsageAverage:   1.0,
+		CPUCost:               2.0 * hrs * cpuPrice,
+		CPUCostAdjustment:     3.0,
+		GPUHours:              1.0 * hrs,
+		GPUCost:               1.0 * hrs * gpuPrice,
+		GPUCostAdjustment:     2.0,
+		NetworkCost:           0.05,
+		LoadBalancerCost:      0.02,
+		PVBreakdown: map[string]PVUsage{
+			"disk1": {
+				ByteHours: 100.0 * gib * hrs,
+				Cost:      100.0 * hrs * pvPrice,
+			},
+		},
 		PVCostAdjustment:       4.0,
 		RAMByteHours:           8.0 * gib * hrs,
 		RAMBytesRequestAverage: 8.0 * gib,
@@ -696,14 +707,9 @@ func generateAllocationSet(start time.Time) *AllocationSet {
 	a23vwx9.Properties.Annotations = map[string]string{"team": "team1"}
 
 	// Services
-
 	a12jkl6.Properties.Services = []string{"service1"}
 	a22pqr6.Properties.Services = []string{"service1"}
 
-	// PV BreakDown
-	a22mno4.Properties.PVBreakdown = map[string]PVUsage{"disk1": {Cost: 2.5, ByteHours: 2.5 * gb}, "disk2": {Cost: 5, ByteHours: 5 * gb}}
-	a22mno5.Properties.PVBreakdown = map[string]PVUsage{"disk1": {Cost: 2.5, ByteHours: 2.5 * gb}, "disk2": {Cost: 5, ByteHours: 5 * gb}}
-
 	return NewAllocationSet(start, start.Add(day),
 		// idle
 		a1i, a2i,
@@ -959,7 +965,7 @@ func TestAllocationSet_AggregateBy(t *testing.T) {
 	//         container8: an[team=team2]          6.00   1.00   1.00   1.00   1.00   1.00   1.00
 	//         container9: an[team=team1]          6.00   1.00   1.00   1.00   1.00   1.00   1.00
 	// +----------------------------------------+------+------+------+------+------+------+------+
-	//   cluster2 subtotal                        46.00  11.00  11.00   6.00   6.00   6.00   6.00
+	//   cluster2 subtotal                        46.00  11.00  11.00   6.00  6.00   6.00   6.00
 	// +----------------------------------------+------+------+------+------+------+------+------+
 	//   total                                   112.00  22.00  42.00  12.00  12.00  12.00  12.00
 	// +----------------------------------------+------+------+------+------+------+------+------+
@@ -1772,6 +1778,12 @@ func TestAllocationSet_ReconcileAllocations(t *testing.T) {
 	for key := range as.idleKeys {
 		as.Delete(key)
 	}
+	// add reconcilable pvs to pod-mno
+	for _, a := range as.allocations {
+		if a.Properties.Pod == "pod-mno" {
+			a.AddPVBreakDown(map[string]PVUsage{"disk1": {Cost: 2.5, ByteHours: 2.5 * gb}, "disk2": {Cost: 5, ByteHours: 5 * gb}})
+		}
+	}
 
 	assetSets := generateAssetSets(start, end)
 
@@ -2161,11 +2173,11 @@ func TestAllocationSetRange_Accumulate(t *testing.T) {
 	if alloc.LoadBalancerCost != 2.0 {
 		t.Fatalf("accumulating AllocationSetRange: expected 2.0; actual %f", alloc.LoadBalancerCost)
 	}
-	if alloc.PVByteHours != 2.0 {
-		t.Fatalf("accumulating AllocationSetRange: expected 2.0; actual %f", alloc.PVByteHours)
+	if alloc.PVByteHours() != 2.0 {
+		t.Fatalf("accumulating AllocationSetRange: expected 2.0; actual %f", alloc.PVByteHours())
 	}
-	if alloc.PVCost != 2.0 {
-		t.Fatalf("accumulating AllocationSetRange: expected 2.0; actual %f", alloc.PVCost)
+	if alloc.PVCost() != 2.0 {
+		t.Fatalf("accumulating AllocationSetRange: expected 2.0; actual %f", alloc.PVCost())
 	}
 	if alloc.RAMByteHours != 2.0 {
 		t.Fatalf("accumulating AllocationSetRange: expected 2.0; actual %f", alloc.RAMByteHours)
@@ -2269,11 +2281,11 @@ func TestAllocationSetRange_InsertRange(t *testing.T) {
 			if !util.IsApproximately(a.GPUCost, unit.GPUCost) {
 				t.Fatalf("allocation %s: expected %f; got %f", k, unit.GPUCost, a.GPUCost)
 			}
-			if !util.IsApproximately(a.PVByteHours, unit.PVByteHours) {
-				t.Fatalf("allocation %s: expected %f; got %f", k, unit.PVByteHours, a.PVByteHours)
+			if !util.IsApproximately(a.PVByteHours(), unit.PVByteHours()) {
+				t.Fatalf("allocation %s: expected %f; got %f", k, unit.PVByteHours(), a.PVByteHours())
 			}
-			if !util.IsApproximately(a.PVCost, unit.PVCost) {
-				t.Fatalf("allocation %s: expected %f; got %f", k, unit.PVCost, a.PVCost)
+			if !util.IsApproximately(a.PVCost(), unit.PVCost()) {
+				t.Fatalf("allocation %s: expected %f; got %f", k, unit.PVCost(), a.PVCost())
 			}
 			if !util.IsApproximately(a.NetworkCost, unit.NetworkCost) {
 				t.Fatalf("allocation %s: expected %f; got %f", k, unit.NetworkCost, a.NetworkCost)
@@ -2320,11 +2332,11 @@ func TestAllocationSetRange_InsertRange(t *testing.T) {
 		if !util.IsApproximately(a.GPUCost, 2*unit.GPUCost) {
 			t.Fatalf("allocation %s: expected %f; got %f", k, unit.GPUCost, a.GPUCost)
 		}
-		if !util.IsApproximately(a.PVByteHours, 2*unit.PVByteHours) {
-			t.Fatalf("allocation %s: expected %f; got %f", k, unit.PVByteHours, a.PVByteHours)
+		if !util.IsApproximately(a.PVByteHours(), 2*unit.PVByteHours()) {
+			t.Fatalf("allocation %s: expected %f; got %f", k, unit.PVByteHours(), a.PVByteHours())
 		}
-		if !util.IsApproximately(a.PVCost, 2*unit.PVCost) {
-			t.Fatalf("allocation %s: expected %f; got %f", k, unit.PVCost, a.PVCost)
+		if !util.IsApproximately(a.PVCost(), 2*unit.PVCost()) {
+			t.Fatalf("allocation %s: expected %f; got %f", k, unit.PVCost(), a.PVCost())
 		}
 		if !util.IsApproximately(a.NetworkCost, 2*unit.NetworkCost) {
 			t.Fatalf("allocation %s: expected %f; got %f", k, unit.NetworkCost, a.NetworkCost)
@@ -2357,11 +2369,11 @@ func TestAllocationSetRange_InsertRange(t *testing.T) {
 		if !util.IsApproximately(a.GPUCost, unit.GPUCost) {
 			t.Fatalf("allocation %s: expected %f; got %f", k, unit.GPUCost, a.GPUCost)
 		}
-		if !util.IsApproximately(a.PVByteHours, unit.PVByteHours) {
-			t.Fatalf("allocation %s: expected %f; got %f", k, unit.PVByteHours, a.PVByteHours)
+		if !util.IsApproximately(a.PVByteHours(), unit.PVByteHours()) {
+			t.Fatalf("allocation %s: expected %f; got %f", k, unit.PVByteHours(), a.PVByteHours())
 		}
-		if !util.IsApproximately(a.PVCost, unit.PVCost) {
-			t.Fatalf("allocation %s: expected %f; got %f", k, unit.PVCost, a.PVCost)
+		if !util.IsApproximately(a.PVCost(), unit.PVCost()) {
+			t.Fatalf("allocation %s: expected %f; got %f", k, unit.PVCost(), a.PVCost())
 		}
 		if !util.IsApproximately(a.NetworkCost, unit.NetworkCost) {
 			t.Fatalf("allocation %s: expected %f; got %f", k, unit.NetworkCost, a.NetworkCost)

+ 0 - 27
pkg/kubecost/allocationprops.go

@@ -74,7 +74,6 @@ type AllocationProperties struct {
 	ProviderID     string                `json:"providerID,omitempty"`
 	Labels         AllocationLabels      `json:"allocationLabels,omitempty"`
 	Annotations    AllocationAnnotations `json:"allocationAnnotations,omitempty"`
-	PVBreakdown    map[string]PVUsage    `json:"pvBreakDown,omitempty"`
 }
 
 // AllocationLabels is a schema-free mapping of key/value pairs that can be
@@ -85,13 +84,6 @@ type AllocationLabels map[string]string
 // attributed to an Allocation
 type AllocationAnnotations map[string]string
 
-// PVUsage is a mapping between the name of the PV and the byte hour usage
-// and cost of an Allocation
-type PVUsage struct {
-	ByteHours float64
-	Cost      float64
-}
-
 func (p *AllocationProperties) Clone() *AllocationProperties {
 	if p == nil {
 		return nil
@@ -125,12 +117,6 @@ func (p *AllocationProperties) Clone() *AllocationProperties {
 	}
 	clone.Annotations = annotations
 
-	pvBreakdown := make(map[string]PVUsage)
-	for k, v := range p.PVBreakdown {
-		pvBreakdown[k] = v
-	}
-	clone.PVBreakdown = pvBreakdown
-
 	return clone
 }
 
@@ -197,19 +183,6 @@ func (p *AllocationProperties) Equal(that *AllocationProperties) bool {
 		return false
 	}
 
-	pPVBreakdown := p.PVBreakdown
-	thatPVBreakdown := that.PVBreakdown
-	if len(pPVBreakdown) == len(thatPVBreakdown) {
-		for k, pv := range pPVBreakdown {
-			tv, ok := thatPVBreakdown[k]
-			if !ok || tv != pv {
-				return false
-			}
-		}
-	} else {
-		return false
-	}
-
 	pServices := p.Services
 	thatServices := that.Services
 	if len(pServices) == len(thatServices) {

+ 85 - 93
pkg/kubecost/kubecost_codecs.go

@@ -161,18 +161,38 @@ func (target *Allocation) MarshalBinary() (data []byte, err error) {
 	buff.WriteBytes(d)
 	// --- [end][write][reference](time.Time) ---
 
-	buff.WriteFloat64(target.CPUCoreHours)           // write float64
-	buff.WriteFloat64(target.CPUCoreRequestAverage)  // write float64
-	buff.WriteFloat64(target.CPUCoreUsageAverage)    // write float64
-	buff.WriteFloat64(target.CPUCost)                // write float64
-	buff.WriteFloat64(target.CPUCostAdjustment)      // write float64
-	buff.WriteFloat64(target.GPUHours)               // write float64
-	buff.WriteFloat64(target.GPUCost)                // write float64
-	buff.WriteFloat64(target.GPUCostAdjustment)      // write float64
-	buff.WriteFloat64(target.NetworkCost)            // write float64
-	buff.WriteFloat64(target.LoadBalancerCost)       // write float64
-	buff.WriteFloat64(target.PVByteHours)            // write float64
-	buff.WriteFloat64(target.PVCost)                 // write float64
+	buff.WriteFloat64(target.CPUCoreHours)          // write float64
+	buff.WriteFloat64(target.CPUCoreRequestAverage) // write float64
+	buff.WriteFloat64(target.CPUCoreUsageAverage)   // write float64
+	buff.WriteFloat64(target.CPUCost)               // write float64
+	buff.WriteFloat64(target.CPUCostAdjustment)     // write float64
+	buff.WriteFloat64(target.GPUHours)              // write float64
+	buff.WriteFloat64(target.GPUCost)               // write float64
+	buff.WriteFloat64(target.GPUCostAdjustment)     // write float64
+	buff.WriteFloat64(target.NetworkCost)           // write float64
+	buff.WriteFloat64(target.LoadBalancerCost)      // write float64
+	if target.PVBreakdown == nil {
+		buff.WriteUInt8(uint8(0)) // write nil byte
+	} else {
+		buff.WriteUInt8(uint8(1)) // write non-nil byte
+
+		// --- [begin][write][map](map[string]PVUsage) ---
+		buff.WriteInt(len(target.PVBreakdown)) // map length
+		for v, z := range target.PVBreakdown {
+			buff.WriteString(v) // write string
+			// --- [begin][write][struct](PVUsage) ---
+			e, errE := z.MarshalBinary()
+			if errE != nil {
+				return nil, errE
+			}
+			buff.WriteInt(len(e))
+			buff.WriteBytes(e)
+			// --- [end][write][struct](PVUsage) ---
+
+		}
+		// --- [end][write][map](map[string]PVUsage) ---
+
+	}
 	buff.WriteFloat64(target.PVCostAdjustment)       // write float64
 	buff.WriteFloat64(target.RAMByteHours)           // write float64
 	buff.WriteFloat64(target.RAMBytesRequestAverage) // write float64
@@ -187,12 +207,12 @@ func (target *Allocation) MarshalBinary() (data []byte, err error) {
 		buff.WriteUInt8(uint8(1)) // write non-nil byte
 
 		// --- [begin][write][struct](RawAllocationOnlyData) ---
-		e, errE := target.RawAllocationOnly.MarshalBinary()
-		if errE != nil {
-			return nil, errE
+		f, errF := target.RawAllocationOnly.MarshalBinary()
+		if errF != nil {
+			return nil, errF
 		}
-		buff.WriteInt(len(e))
-		buff.WriteBytes(e)
+		buff.WriteInt(len(f))
+		buff.WriteBytes(f)
 		// --- [end][write][struct](RawAllocationOnlyData) ---
 
 	}
@@ -304,48 +324,70 @@ func (target *Allocation) UnmarshalBinary(data []byte) (err error) {
 	aa := buff.ReadFloat64() // read float64
 	target.LoadBalancerCost = aa
 
-	bb := buff.ReadFloat64() // read float64
-	target.PVByteHours = bb
-
-	cc := buff.ReadFloat64() // read float64
-	target.PVCost = cc
-
-	dd := buff.ReadFloat64() // read float64
-	target.PVCostAdjustment = dd
-
-	ee := buff.ReadFloat64() // read float64
-	target.RAMByteHours = ee
+	if buff.ReadUInt8() == uint8(0) {
+		target.PVBreakdown = nil
+	} else {
+		// --- [begin][read][map](map[string]PVUsage) ---
+		cc := buff.ReadInt() // map len
+		bb := make(map[string]PVUsage, cc)
+		for i := 0; i < cc; i++ {
+			var v string
+			dd := buff.ReadString() // read string
+			v = dd
 
-	ff := buff.ReadFloat64() // read float64
-	target.RAMBytesRequestAverage = ff
+			// --- [begin][read][struct](PVUsage) ---
+			ee := &PVUsage{}
+			ff := buff.ReadInt()     // byte array length
+			gg := buff.ReadBytes(ff) // byte array
+			errE := ee.UnmarshalBinary(gg)
+			if errE != nil {
+				return errE
+			}
+			z := *ee
+			// --- [end][read][struct](PVUsage) ---
 
-	gg := buff.ReadFloat64() // read float64
-	target.RAMBytesUsageAverage = gg
+			bb[v] = z
+		}
+		target.PVBreakdown = bb
+		// --- [end][read][map](map[string]PVUsage) ---
 
+	}
 	hh := buff.ReadFloat64() // read float64
-	target.RAMCost = hh
+	target.PVCostAdjustment = hh
 
 	kk := buff.ReadFloat64() // read float64
-	target.RAMCostAdjustment = kk
+	target.RAMByteHours = kk
 
 	ll := buff.ReadFloat64() // read float64
-	target.SharedCost = ll
+	target.RAMBytesRequestAverage = ll
 
 	mm := buff.ReadFloat64() // read float64
-	target.ExternalCost = mm
+	target.RAMBytesUsageAverage = mm
+
+	nn := buff.ReadFloat64() // read float64
+	target.RAMCost = nn
+
+	oo := buff.ReadFloat64() // read float64
+	target.RAMCostAdjustment = oo
+
+	pp := buff.ReadFloat64() // read float64
+	target.SharedCost = pp
+
+	qq := buff.ReadFloat64() // read float64
+	target.ExternalCost = qq
 
 	if buff.ReadUInt8() == uint8(0) {
 		target.RawAllocationOnly = nil
 	} else {
 		// --- [begin][read][struct](RawAllocationOnlyData) ---
-		nn := &RawAllocationOnlyData{}
-		oo := buff.ReadInt()     // byte array length
-		pp := buff.ReadBytes(oo) // byte array
-		errE := nn.UnmarshalBinary(pp)
-		if errE != nil {
-			return errE
+		rr := &RawAllocationOnlyData{}
+		ss := buff.ReadInt()     // byte array length
+		tt := buff.ReadBytes(ss) // byte array
+		errF := rr.UnmarshalBinary(tt)
+		if errF != nil {
+			return errF
 		}
-		target.RawAllocationOnly = nn
+		target.RawAllocationOnly = rr
 		// --- [end][read][struct](RawAllocationOnlyData) ---
 
 	}
@@ -430,28 +472,6 @@ func (target *AllocationProperties) MarshalBinary() (data []byte, err error) {
 	}
 	// --- [end][write][alias](AllocationAnnotations) ---
 
-	if target.PVBreakdown == nil {
-		buff.WriteUInt8(uint8(0)) // write nil byte
-	} else {
-		buff.WriteUInt8(uint8(1)) // write non-nil byte
-
-		// --- [begin][write][map](map[string]PVUsage) ---
-		buff.WriteInt(len(target.PVBreakdown)) // map length
-		for vvv, zzz := range target.PVBreakdown {
-			buff.WriteString(vvv) // write string
-			// --- [begin][write][struct](PVUsage) ---
-			a, errA := zzz.MarshalBinary()
-			if errA != nil {
-				return nil, errA
-			}
-			buff.WriteInt(len(a))
-			buff.WriteBytes(a)
-			// --- [end][write][struct](PVUsage) ---
-
-		}
-		// --- [end][write][map](map[string]PVUsage) ---
-
-	}
 	return buff.Bytes(), nil
 }
 
@@ -572,34 +592,6 @@ func (target *AllocationProperties) UnmarshalBinary(data []byte) (err error) {
 	target.Annotations = AllocationAnnotations(t)
 	// --- [end][read][alias](AllocationAnnotations) ---
 
-	if buff.ReadUInt8() == uint8(0) {
-		target.PVBreakdown = nil
-	} else {
-		// --- [begin][read][map](map[string]PVUsage) ---
-		bb := buff.ReadInt() // map len
-		aa := make(map[string]PVUsage, bb)
-		for jj := 0; jj < bb; jj++ {
-			var vvv string
-			cc := buff.ReadString() // read string
-			vvv = cc
-
-			// --- [begin][read][struct](PVUsage) ---
-			dd := &PVUsage{}
-			ee := buff.ReadInt()     // byte array length
-			ff := buff.ReadBytes(ee) // byte array
-			errA := dd.UnmarshalBinary(ff)
-			if errA != nil {
-				return errA
-			}
-			zzz := *dd
-			// --- [end][read][struct](PVUsage) ---
-
-			aa[vvv] = zzz
-		}
-		target.PVBreakdown = aa
-		// --- [end][read][map](map[string]PVUsage) ---
-
-	}
 	return nil
 }