diff --git a/test/integration/prometheus/network_costs_test.go b/test/integration/prometheus/network_costs_test.go new file mode 100644 index 0000000..1c53cf1 --- /dev/null +++ b/test/integration/prometheus/network_costs_test.go @@ -0,0 +1,195 @@ +package prometheus + +// Description - Compares Network Costs from Prometheus and Allocation + +import ( + // "fmt" + "github.com/opencost/opencost-integration-tests/pkg/api" + "github.com/opencost/opencost-integration-tests/pkg/prometheus" + "github.com/opencost/opencost-integration-tests/pkg/utils" + "slices" + "testing" + "time" +) + +const tolerance = 0.05 + +func TestNetworkCosts(t *testing.T) { + apiObj := api.NewAPI() + + testCases := []struct { + name string + window string + aggregate string + accumulate string + }{ + { + name: "Yesterday", + window: "24h", + aggregate: "namespace", + accumulate: "false", + }, + } + + t.Logf("testCases: %v", testCases) + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + + // Any data that is in a "raw allocation only" is not valid in any + // sort of cumulative Allocation (like one that is added). + + type NetworkCostsAggregate struct { + PromNetworkTransferBytes float64 + PromNetworkReceiveBytes float64 + Pods []string + AllocNetworkTransferBytes float64 + AllocNetworkReceiveBytes float64 + } + networkCostsNamespaceMap := make(map[string]*NetworkCostsAggregate) + + queryEnd := time.Now().UTC().Truncate(time.Hour).Add(time.Hour) + endTime := queryEnd.Unix() + // Collect Namespace results from Prometheus + client := prometheus.NewClient() + + //////////////////////////////////////////////////////////////////////////// + // Network Receive Bytes + + // sum(increase(container_network_receive_bytes_total{pod!=""}[24h:5m])) by (pod, namespace) + //////////////////////////////////////////////////////////////////////////// + + promNetworkReceiveInput := prometheus.PrometheusInput{ + Metric: "container_network_receive_bytes_total", + } + promNetworkReceiveInput.IgnoreFilters = map[string][]string{ + "pod": {""}, + } + promNetworkReceiveInput.Function = []string{"increase", "sum"} + promNetworkReceiveInput.QueryWindow = tc.window + promNetworkReceiveInput.QueryResolution = "5m" + promNetworkReceiveInput.AggregateBy = []string{"pod", "namespace"} + promNetworkReceiveInput.Time = &endTime + + promNetworkReceiveResponse, err := client.RunPromQLQuery(promNetworkReceiveInput) + if err != nil { + t.Fatalf("Error while calling Prometheus API %v", err) + } + + //////////////////////////////////////////////////////////////////////////// + // Network Transfer Bytes + + // sum(increase(container_network_transmit_bytes_total{pod!="", %s}[%s:%dm])) by (pod_name, pod, namespace, %s) + //////////////////////////////////////////////////////////////////////////// + + promNetworkTransferInput := prometheus.PrometheusInput{ + Metric: "container_network_transmit_bytes_total", + } + promNetworkTransferInput.IgnoreFilters = map[string][]string{ + "pod": {""}, + } + promNetworkTransferInput.Function = []string{"increase", "sum"} + promNetworkTransferInput.QueryWindow = tc.window + promNetworkTransferInput.QueryResolution = "5m" + promNetworkTransferInput.AggregateBy = []string{"pod", "namespace"} + promNetworkTransferInput.Time = &endTime + + promNetworkTransferResponse, err := client.RunPromQLQuery(promNetworkTransferInput) + if err != nil { + t.Fatalf("Error while calling Prometheus API %v", err) + } + + // Network Receive Bytes + for _, promNetworkReceiveResponse := range promNetworkReceiveResponse.Data.Result { + namespace := promNetworkReceiveResponse.Metric.Namespace + pod := promNetworkReceiveResponse.Metric.Pod + networkReceiveBytesPod := promNetworkReceiveResponse.Value.Value + networkCostsNamespace, ok := networkCostsNamespaceMap[namespace] + if !ok { + networkCostsNamespaceMap[namespace] = &NetworkCostsAggregate{ + PromNetworkReceiveBytes: networkReceiveBytesPod, + PromNetworkTransferBytes: 0.0, + AllocNetworkReceiveBytes: 0.0, + AllocNetworkTransferBytes: 0.0, + Pods: []string{pod}, + } + continue + } + + networkCostsNamespace.Pods = append(networkCostsNamespace.Pods, pod) + networkCostsNamespace.PromNetworkReceiveBytes += networkReceiveBytesPod + } + + // Network Transfer Bytes + for _, promNetworkTransferResponse := range promNetworkTransferResponse.Data.Result { + namespace := promNetworkTransferResponse.Metric.Namespace + pod := promNetworkTransferResponse.Metric.Pod + networkTransferBytesPod := promNetworkTransferResponse.Value.Value + networkCostsNamespace, ok := networkCostsNamespaceMap[namespace] + if !ok { + networkCostsNamespaceMap[namespace] = &NetworkCostsAggregate{ + PromNetworkReceiveBytes: networkTransferBytesPod, + PromNetworkTransferBytes: networkTransferBytesPod, + AllocNetworkReceiveBytes: 0.0, + AllocNetworkTransferBytes: 0.0, + Pods: []string{pod}, + } + continue + } + if !slices.Contains(networkCostsNamespace.Pods, pod) { + networkCostsNamespace.Pods = append(networkCostsNamespace.Pods, pod) + } + networkCostsNamespace.PromNetworkTransferBytes += networkTransferBytesPod + } + + + ///////////////////////////////////////////// + // API Client + ///////////////////////////////////////////// + + // Why doesn't allocation work on Namespace aggregate? + apiResponse, err := apiObj.GetAllocation(api.AllocationRequest{ + Window: tc.window, + Aggregate: tc.aggregate, + Accumulate: tc.accumulate, + }) + + if err != nil { + t.Fatalf("Error while calling Allocation API %v", err) + } + if apiResponse.Code != 200 { + t.Errorf("API returned non-200 code") + } + + for namespace, allocationResponseItem := range apiResponse.Data[0] { + networkCostsNamespace, ok := networkCostsNamespaceMap[namespace] + if !ok { + networkCostsNamespaceMap[namespace] = &NetworkCostsAggregate{ + PromNetworkReceiveBytes: 0.0, + PromNetworkTransferBytes: 0.0, + AllocNetworkReceiveBytes: allocationResponseItem.NetworkReceiveBytes, + AllocNetworkTransferBytes: allocationResponseItem.NetworkTransferBytes, + } + continue + } + networkCostsNamespace.AllocNetworkReceiveBytes = allocationResponseItem.NetworkReceiveBytes + networkCostsNamespace.AllocNetworkTransferBytes = allocationResponseItem.NetworkTransferBytes + } + + for namespace, networkCostValues := range networkCostsNamespaceMap { + t.Logf("Namespace %s", namespace) + withinRange, diff_percent := utils.AreWithinPercentage(networkCostValues.AllocNetworkTransferBytes, networkCostValues.PromNetworkTransferBytes, tolerance) + if !withinRange { + t.Errorf(" - NetworkTransferBytes[Fail]: DifferencePercent: %0.2f, Prometheus: %0.2f, /allocation: %0.2f", diff_percent, networkCostValues.PromNetworkTransferBytes, networkCostValues.AllocNetworkTransferBytes) + } else { + t.Logf(" - NetworkTransferBytes[Pass]: ~ %0.2f", networkCostValues.PromNetworkTransferBytes) + } + if !withinRange { + t.Errorf(" - NetworkReceiveBytes[Fail]: DifferencePercent: %0.2f, Prometheus: %0.2f, /allocation: %0.2f", diff_percent, networkCostValues.PromNetworkReceiveBytes, networkCostValues.AllocNetworkReceiveBytes) + } else { + t.Logf(" - NetworkReceiveBytes[Pass]: ~ %0.2f", networkCostValues.PromNetworkReceiveBytes) + } + } + }) + } +} diff --git a/test/integration/prometheus/network_internet_costs_test.go b/test/integration/prometheus/network_internet_costs_test.go new file mode 100644 index 0000000..73a828a --- /dev/null +++ b/test/integration/prometheus/network_internet_costs_test.go @@ -0,0 +1,180 @@ +package prometheus + +// Description - Compares Network Internet Costs from Prometheus and Allocation + +import ( + // "fmt" + "github.com/opencost/opencost-integration-tests/pkg/api" + "github.com/opencost/opencost-integration-tests/pkg/prometheus" + "github.com/opencost/opencost-integration-tests/pkg/utils" + "testing" + "time" +) + +const tolerance = 0.05 + +func TestNetworkInternetCosts(t *testing.T) { + apiObj := api.NewAPI() + + testCases := []struct { + name string + window string + aggregate string + accumulate string + }{ + { + name: "Yesterday", + window: "24h", + aggregate: "pod", + accumulate: "false", + }, + } + + t.Logf("testCases: %v", testCases) + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + + // Any data that is in a "raw allocation only" is not valid in any + // sort of cumulative Allocation (like one that is added). + + type NetworkCostsAggregate struct { + PromNetworkInternetGiB float64 + AllocNetworkInternetGiB float64 + } + + networkCostsPodMap := make(map[string]*NetworkCostsAggregate) + + queryEnd := time.Now().UTC().Truncate(time.Hour).Add(time.Hour) + endTime := queryEnd.Unix() + // Collect Namespace results from Prometheus + client := prometheus.NewClient() + + //////////////////////////////////////////////////////////////////////////// + // Network Internet GiB + + // sum(increase(kubecost_pod_network_egress_bytes_total{internet="true"}[24h:5m])) by (pod_name, namespace) / 1024 / 1024 / 1024 + // Apply Division by 1024^3 when you are looping over the response + //////////////////////////////////////////////////////////////////////////// + + promNetworkInternetInput := prometheus.PrometheusInput{ + Metric: "kubecost_pod_network_egress_bytes_total", + } + promNetworkInternetInput.Filters = map[string]string{ + "internet": "true", + } + promNetworkInternetInput.Function = []string{"increase", "sum"} + promNetworkInternetInput.QueryWindow = tc.window + promNetworkInternetInput.QueryResolution = "5m" + promNetworkInternetInput.AggregateBy = []string{"pod_name", "namespace"} + promNetworkInternetInput.Time = &endTime + + promNetworkInternetResponse, err := client.RunPromQLQuery(promNetworkInternetInput) + if err != nil { + t.Fatalf("Error while calling Prometheus API %v", err) + } + + //////////////////////////////////////////////////////////////////////////// + // Network Internet price per GiB + + // avg(avg_over_time(kubecost_network_internet_egress_cost{%s}[%s])) by (%s) + //////////////////////////////////////////////////////////////////////////// + + promNetworkInternetCostInput := prometheus.PrometheusInput{ + Metric: "kubecost_network_internet_egress_cost", + } + promNetworkInternetCostInput.Function = []string{"avg_over_time", "avg"} + promNetworkInternetCostInput.QueryWindow = tc.window + promNetworkInternetCostInput.Time = &endTime + + promNetworkInternetCostResponse, err := client.RunPromQLQuery(promNetworkInternetCostInput) + if err != nil { + t.Fatalf("Error while calling Prometheus API %v", err) + } + + // -------------------------------- + // Network Internet Cost for all Pods + // -------------------------------- + + networkInternetCost := promNetworkInternetCostResponse.Data.Result[0].Value.Value + + // -------------------------------- + // Assign Network Costs to Pods and Cumulate based on Namespace + // -------------------------------- + + // Form a key based on namespace and pod name + + for _, promNetworkInternetItem := range promNetworkInternetResponse.Data.Result { + // namespace := promNetworkInternetItem.Metric.Namespace + pod := promNetworkInternetItem.Metric.PodName + gib := promNetworkInternetItem.Value.Value + + + networkCostsPodMap[pod] = &NetworkCostsAggregate{ + PromNetworkInternetGiB: (gib / 1024 / 1024 / 1024) * networkInternetCost, + AllocNetworkInternetGiB: 0.0, + } + + // networkCostsNamespace, ok := networkCostsPodMap[namespace] + // if !ok { + // networkCostsPodMap[pod] = &NetworkCostsAggregate{ + // PromNetworkInternetGiB: (gib / 1024 / 1024 / 1024) * networkInternetCost, + // AllocNetworkInternetGiB: 0.0, + // } + // continue + // } + } + + + ///////////////////////////////////////////// + // API Client + ///////////////////////////////////////////// + + // Why doesn't allocation work on Namespace aggregate? + apiResponse, err := apiObj.GetAllocation(api.AllocationRequest{ + Window: tc.window, + Aggregate: tc.aggregate, + Accumulate: tc.accumulate, + }) + + if err != nil { + t.Fatalf("Error while calling Allocation API %v", err) + } + if apiResponse.Code != 200 { + t.Errorf("API returned non-200 code") + } + + for pod, allocationResponseItem := range apiResponse.Data[0] { + networkCostsPod, ok := networkCostsPodMap[pod] + if !ok { + networkCostsPodMap[pod] = &NetworkCostsAggregate{ + PromNetworkInternetGiB: 0.0, + AllocNetworkInternetGiB: allocationResponseItem.NetworkInternetCost, + } + continue + } + networkCostsPod.AllocNetworkInternetGiB = allocationResponseItem.NetworkInternetCost + } + + validCostsSeen := false + negligilbleCost := 0.01 // 1 Cent of a Dollar + for pod, networkCostValues := range networkCostsPodMap { + if networkCostValues.AllocNetworkInternetGiB < negligilbleCost { + continue + } else { + validCostsSeen = true + } + t.Logf("Pod %s", pod) + withinRange, diff_percent := utils.AreWithinPercentage(networkCostValues.AllocNetworkInternetGiB, networkCostValues.PromNetworkInternetGiB, tolerance) + if !withinRange { + t.Errorf(" - NetworkInternetCost[Fail]: DifferencePercent: %0.2f, Prometheus: %0.9f, /allocation: %0.9f", diff_percent, networkCostValues.PromNetworkInternetGiB, networkCostValues.AllocNetworkInternetGiB) + } else { + t.Logf(" - NetworkInternetCost[Pass]: ~ %0.5f", networkCostValues.PromNetworkInternetGiB) + } + } + if !validCostsSeen { + t.Errorf("NetWork Internet Costs for all Pods are below 1 cent and hence cannot be considered as costs from resource usage and validated.") + } + }) + } +} diff --git a/test/integration/prometheus/network_region_costs_test.go b/test/integration/prometheus/network_region_costs_test.go new file mode 100644 index 0000000..6d0f6f3 --- /dev/null +++ b/test/integration/prometheus/network_region_costs_test.go @@ -0,0 +1,182 @@ +package prometheus + +// Description - Compares Network Region Costs from Prometheus and Allocation + +import ( + // "fmt" + "github.com/opencost/opencost-integration-tests/pkg/api" + "github.com/opencost/opencost-integration-tests/pkg/prometheus" + "github.com/opencost/opencost-integration-tests/pkg/utils" + "testing" + "time" +) + +const tolerance = 0.05 + +func TestNetworkRegionCosts(t *testing.T) { + apiObj := api.NewAPI() + + testCases := []struct { + name string + window string + aggregate string + accumulate string + }{ + { + name: "Yesterday", + window: "24h", + aggregate: "pod", + accumulate: "false", + }, + } + + t.Logf("testCases: %v", testCases) + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + + // Any data that is in a "raw allocation only" is not valid in any + // sort of cumulative Allocation (like one that is added). + + type NetworkCostsAggregate struct { + PromNetworkRegionGiB float64 + AllocNetworkRegionGiB float64 + } + + networkCostsPodMap := make(map[string]*NetworkCostsAggregate) + + queryEnd := time.Now().UTC().Truncate(time.Hour).Add(time.Hour) + endTime := queryEnd.Unix() + // Collect Namespace results from Prometheus + client := prometheus.NewClient() + + //////////////////////////////////////////////////////////////////////////// + // Network Region GiB + + // sum(increase(kubecost_pod_network_egress_bytes_total{internet="false", same_zone="false", same_region="false", %s}[%s:%dm])) by (pod_name, namespace, %s) / 1024 / 1024 / 1024 + // Apply Division by 1024^3 when you are looping over the response + //////////////////////////////////////////////////////////////////////////// + + promNetworkRegionInput := prometheus.PrometheusInput{ + Metric: "kubecost_pod_network_egress_bytes_total", + } + promNetworkRegionInput.Filters = map[string]string{ + "internet": "false", + "same_Region": "false", + "same_region": "false", + } + promNetworkRegionInput.Function = []string{"increase", "sum"} + promNetworkRegionInput.QueryWindow = tc.window + promNetworkRegionInput.QueryResolution = "5m" + promNetworkRegionInput.AggregateBy = []string{"pod_name", "namespace"} + promNetworkRegionInput.Time = &endTime + + promNetworkRegionResponse, err := client.RunPromQLQuery(promNetworkRegionInput) + if err != nil { + t.Fatalf("Error while calling Prometheus API %v", err) + } + + //////////////////////////////////////////////////////////////////////////// + // Network Region price per GiB + + // avg(avg_over_time(kubecost_network_region_egress_cost{%s}[%s])) by (%s) + //////////////////////////////////////////////////////////////////////////// + + promNetworkRegionCostInput := prometheus.PrometheusInput{ + Metric: "kubecost_network_region_egress_cost", + } + promNetworkRegionCostInput.Function = []string{"avg_over_time", "avg"} + promNetworkRegionCostInput.QueryWindow = tc.window + promNetworkRegionCostInput.Time = &endTime + + promNetworkRegionCostResponse, err := client.RunPromQLQuery(promNetworkRegionCostInput) + if err != nil { + t.Fatalf("Error while calling Prometheus API %v", err) + } + + // -------------------------------- + // Network Region Cost for all Pods + // -------------------------------- + + networkRegionCost := promNetworkRegionCostResponse.Data.Result[0].Value.Value + + // -------------------------------- + // Assign Network Costs to Pods and Cumulate based on Namespace + // -------------------------------- + + // Form a key based on namespace and pod name + + for _, promNetworkRegionItem := range promNetworkRegionResponse.Data.Result { + // namespace := promNetworkRegionItem.Metric.Namespace + pod := promNetworkRegionItem.Metric.PodName + gib := promNetworkRegionItem.Value.Value + + + networkCostsPodMap[pod] = &NetworkCostsAggregate{ + PromNetworkRegionGiB: (gib / 1024 / 1024 / 1024) * networkRegionCost, + AllocNetworkRegionGiB: 0.0, + } + + // networkCostsNamespace, ok := networkCostsPodMap[namespace] + // if !ok { + // networkCostsPodMap[pod] = &NetworkCostsAggregate{ + // PromNetworkRegionGiB: (gib / 1024 / 1024 / 1024) * networkRegionCost, + // AllocNetworkRegionGiB: 0.0, + // } + // continue + // } + } + + + ///////////////////////////////////////////// + // API Client + ///////////////////////////////////////////// + + // Why doesn't allocation work on Namespace aggregate? + apiResponse, err := apiObj.GetAllocation(api.AllocationRequest{ + Window: tc.window, + Aggregate: tc.aggregate, + Accumulate: tc.accumulate, + }) + + if err != nil { + t.Fatalf("Error while calling Allocation API %v", err) + } + if apiResponse.Code != 200 { + t.Errorf("API returned non-200 code") + } + + for pod, allocationResponseItem := range apiResponse.Data[0] { + networkCostsPod, ok := networkCostsPodMap[pod] + if !ok { + networkCostsPodMap[pod] = &NetworkCostsAggregate{ + PromNetworkRegionGiB: 0.0, + AllocNetworkRegionGiB: allocationResponseItem.NetworkCrossRegionCost, + } + continue + } + networkCostsPod.AllocNetworkRegionGiB = allocationResponseItem.NetworkCrossRegionCost + } + + validCostsSeen := false + negligilbleCost := 0.01 // 1 Cent of a Dollar + for pod, networkCostValues := range networkCostsPodMap { + if networkCostValues.AllocNetworkRegionGiB < negligilbleCost { + continue + } else { + validCostsSeen = true + } + t.Logf("Pod %s", pod) + withinRange, diff_percent := utils.AreWithinPercentage(networkCostValues.AllocNetworkRegionGiB, networkCostValues.PromNetworkRegionGiB, tolerance) + if !withinRange { + t.Errorf(" - NetworkRegionCost[Fail]: DifferencePercent: %0.2f, Prometheus: %0.9f, /allocation: %0.9f", diff_percent, networkCostValues.PromNetworkRegionGiB, networkCostValues.AllocNetworkRegionGiB) + } else { + t.Logf(" - NetworkRegionCost[Pass]: ~ %0.5f", networkCostValues.PromNetworkRegionGiB) + } + } + if !validCostsSeen { + t.Errorf("NetWork Region Costs for all Pods are below 1 cent and hence cannot be considered as costs from resource usage and validated.") + } + }) + } +} diff --git a/test/integration/prometheus/network_zone_costs_test.go b/test/integration/prometheus/network_zone_costs_test.go new file mode 100644 index 0000000..841bd81 --- /dev/null +++ b/test/integration/prometheus/network_zone_costs_test.go @@ -0,0 +1,182 @@ +package prometheus + +// Description - Compares Network Zone Costs from Prometheus and Allocation + +import ( + // "fmt" + "github.com/opencost/opencost-integration-tests/pkg/api" + "github.com/opencost/opencost-integration-tests/pkg/prometheus" + "github.com/opencost/opencost-integration-tests/pkg/utils" + "testing" + "time" +) + +const tolerance = 0.05 + +func TestNetworkZoneCosts(t *testing.T) { + apiObj := api.NewAPI() + + testCases := []struct { + name string + window string + aggregate string + accumulate string + }{ + { + name: "Yesterday", + window: "24h", + aggregate: "pod", + accumulate: "false", + }, + } + + t.Logf("testCases: %v", testCases) + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + + // Any data that is in a "raw allocation only" is not valid in any + // sort of cumulative Allocation (like one that is added). + + type NetworkCostsAggregate struct { + PromNetworkZoneGiB float64 + AllocNetworkZoneGiB float64 + } + + networkCostsPodMap := make(map[string]*NetworkCostsAggregate) + + queryEnd := time.Now().UTC().Truncate(time.Hour).Add(time.Hour) + endTime := queryEnd.Unix() + // Collect Namespace results from Prometheus + client := prometheus.NewClient() + + //////////////////////////////////////////////////////////////////////////// + // Network Zone GiB + + // sum(increase(kubecost_pod_network_egress_bytes_total{internet="false", same_zone="false", same_region="true", %s}[%s:%dm])) by (pod_name, namespace, %s) / 1024 / 1024 / 1024 + // Apply Division by 1024^3 when you are looping over the response + //////////////////////////////////////////////////////////////////////////// + + promNetworkZoneInput := prometheus.PrometheusInput{ + Metric: "kubecost_pod_network_egress_bytes_total", + } + promNetworkZoneInput.Filters = map[string]string{ + "internet": "false", + "same_zone": "false", + "same_region": "true", + } + promNetworkZoneInput.Function = []string{"increase", "sum"} + promNetworkZoneInput.QueryWindow = tc.window + promNetworkZoneInput.QueryResolution = "5m" + promNetworkZoneInput.AggregateBy = []string{"pod_name", "namespace"} + promNetworkZoneInput.Time = &endTime + + promNetworkZoneResponse, err := client.RunPromQLQuery(promNetworkZoneInput) + if err != nil { + t.Fatalf("Error while calling Prometheus API %v", err) + } + + //////////////////////////////////////////////////////////////////////////// + // Network Zone price per GiB + + // avg(avg_over_time(kubecost_network_zone_egress_cost{%s}[%s])) by (%s) + //////////////////////////////////////////////////////////////////////////// + + promNetworkZoneCostInput := prometheus.PrometheusInput{ + Metric: "kubecost_network_zone_egress_cost", + } + promNetworkZoneCostInput.Function = []string{"avg_over_time", "avg"} + promNetworkZoneCostInput.QueryWindow = tc.window + promNetworkZoneCostInput.Time = &endTime + + promNetworkZoneCostResponse, err := client.RunPromQLQuery(promNetworkZoneCostInput) + if err != nil { + t.Fatalf("Error while calling Prometheus API %v", err) + } + + // -------------------------------- + // Network Zone Cost for all Pods + // -------------------------------- + + networkZoneCost := promNetworkZoneCostResponse.Data.Result[0].Value.Value + + // -------------------------------- + // Assign Network Costs to Pods and Cumulate based on Namespace + // -------------------------------- + + // Form a key based on namespace and pod name + + for _, promNetworkZoneItem := range promNetworkZoneResponse.Data.Result { + // namespace := promNetworkZoneItem.Metric.Namespace + pod := promNetworkZoneItem.Metric.PodName + gib := promNetworkZoneItem.Value.Value + + + networkCostsPodMap[pod] = &NetworkCostsAggregate{ + PromNetworkZoneGiB: (gib / 1024 / 1024 / 1024) * networkZoneCost, + AllocNetworkZoneGiB: 0.0, + } + + // networkCostsNamespace, ok := networkCostsPodMap[namespace] + // if !ok { + // networkCostsPodMap[pod] = &NetworkCostsAggregate{ + // PromNetworkZoneGiB: (gib / 1024 / 1024 / 1024) * networkZoneCost, + // AllocNetworkZoneGiB: 0.0, + // } + // continue + // } + } + + + ///////////////////////////////////////////// + // API Client + ///////////////////////////////////////////// + + // Why doesn't allocation work on Namespace aggregate? + apiResponse, err := apiObj.GetAllocation(api.AllocationRequest{ + Window: tc.window, + Aggregate: tc.aggregate, + Accumulate: tc.accumulate, + }) + + if err != nil { + t.Fatalf("Error while calling Allocation API %v", err) + } + if apiResponse.Code != 200 { + t.Errorf("API returned non-200 code") + } + + for pod, allocationResponseItem := range apiResponse.Data[0] { + networkCostsPod, ok := networkCostsPodMap[pod] + if !ok { + networkCostsPodMap[pod] = &NetworkCostsAggregate{ + PromNetworkZoneGiB: 0.0, + AllocNetworkZoneGiB: allocationResponseItem.NetworkCrossZoneCost, + } + continue + } + networkCostsPod.AllocNetworkZoneGiB = allocationResponseItem.NetworkCrossZoneCost + } + + validCostsSeen := false + negligilbleCost := 0.01 // 1 Cent of a Dollar + for pod, networkCostValues := range networkCostsPodMap { + if networkCostValues.AllocNetworkZoneGiB < negligilbleCost { + continue + } else { + validCostsSeen = true + } + t.Logf("Pod %s", pod) + withinRange, diff_percent := utils.AreWithinPercentage(networkCostValues.AllocNetworkZoneGiB, networkCostValues.PromNetworkZoneGiB, tolerance) + if !withinRange { + t.Errorf(" - NetworkZoneCost[Fail]: DifferencePercent: %0.2f, Prometheus: %0.9f, /allocation: %0.9f", diff_percent, networkCostValues.PromNetworkZoneGiB, networkCostValues.AllocNetworkZoneGiB) + } else { + t.Logf(" - NetworkZoneCost[Pass]: ~ %0.5f", networkCostValues.PromNetworkZoneGiB) + } + } + if !validCostsSeen { + t.Errorf("NetWork Zone Costs for all Pods are below 1 cent and hence cannot be considered as costs from resource usage and validated.") + } + }) + } +} diff --git a/test/integration/prometheus/test.bats b/test/integration/prometheus/test.bats index 5da07af..771e36a 100644 --- a/test/integration/prometheus/test.bats +++ b/test/integration/prometheus/test.bats @@ -73,6 +73,26 @@ teardown() { @test "prometheus: GPU Info" { go test ./test/integration/prometheus/gpu_info_test.go } +# ------------------------------------------------------ + + +# ------------------------------------------------------ +# Network Costs +@test "prometheus: Network Transfer and Receive Bytes" { + go test ./test/integration/prometheus/network_costs_test.go +} + +@test "prometheus: Network Internet Cost" { + go test ./test/integration/prometheus/network_internet_costs_test.go +} + +@test "prometheus: Network Zone Cost" { + go test ./test/integration/prometheus/network_zone_costs_test.go +} + +@test "prometheus: Network Region Cost" { + go test ./test/integration/prometheus/network_region_costs_test.go +} @test "prometheus: GPU Count" { go test ./test/integration/prometheus/gpu_count_test.go