From 836b279feeb62c82b57e09a69d711f9fa443e78d Mon Sep 17 00:00:00 2001 From: Rao Yunkun Date: Wed, 31 Mar 2021 21:18:56 +0800 Subject: [PATCH] Update metering UT. Signed-off-by: Rao Yunkun --- pkg/kapis/monitoring/v1alpha3/helper_test.go | 8 +- pkg/models/monitoring/utils.go | 24 +- pkg/models/monitoring/utils_test.go | 403 +++++++++++++++++++ pkg/models/tenant/metering_test.go | 193 +++++++++ 4 files changed, 617 insertions(+), 11 deletions(-) create mode 100644 pkg/models/monitoring/utils_test.go create mode 100644 pkg/models/tenant/metering_test.go diff --git a/pkg/kapis/monitoring/v1alpha3/helper_test.go b/pkg/kapis/monitoring/v1alpha3/helper_test.go index 9ddc6015e..77b8fd7a8 100644 --- a/pkg/kapis/monitoring/v1alpha3/helper_test.go +++ b/pkg/kapis/monitoring/v1alpha3/helper_test.go @@ -274,7 +274,7 @@ func TestParseRequestParams(t *testing.T) { { params: reqParams{ namespaceName: "default", - openpitrixs: "op1|op2", + openpitrixs: "op1|op2", }, lvl: monitoring.LevelOpenpitrix, expectedErr: true, @@ -287,10 +287,8 @@ func TestParseRequestParams(t *testing.T) { expectedErr: true, }, { - params: reqParams{ - - }, - lvl: monitoring.LevelOpenpitrix, + params: reqParams{}, + lvl: monitoring.LevelOpenpitrix, expectedErr: true, }, } diff --git a/pkg/models/monitoring/utils.go b/pkg/models/monitoring/utils.go index 200ef527f..4aae8e989 100644 --- a/pkg/models/monitoring/utils.go +++ b/pkg/models/monitoring/utils.go @@ -4,6 +4,7 @@ import ( "fmt" "math/big" "os" + "path/filepath" "k8s.io/apimachinery/pkg/util/yaml" "k8s.io/klog" @@ -18,7 +19,8 @@ const ( METER_RESOURCE_TYPE_NET_EGRESS METER_RESOURCE_TYPE_PVC - meteringConfig = "/etc/kubesphere/metering/ks-metering.yaml" + meteringConfigDir = "/etc/kubesphere/metering/" + meteringConfigName = "ks-metering.yaml" meteringDefaultPrecision = 10 meteringCorePrecision = 3 @@ -103,11 +105,21 @@ func (mc MeterConfig) GetPriceInfo() PriceInfo { func LoadYaml() (*MeterConfig, error) { var meterConfig MeterConfig + var mf *os.File + var err error - mf, err := os.Open(meteringConfig) - if err != nil { - klog.Error(err) - return nil, err + if _, err := os.Stat(meteringConfigName); os.IsNotExist(err) { + mf, err = os.Open(filepath.Join(meteringConfigDir, meteringConfigName)) + if err != nil { + klog.Error(err) + return nil, err + } + } else { + mf, err = os.Open(meteringConfigName) + if err != nil { + klog.Error(err) + return nil, err + } } if err = yaml.NewYAMLOrJSONDecoder(mf, 1024).Decode(&meterConfig); err != nil { @@ -170,7 +182,7 @@ func getAvgPointValue(points []monitoring.Point) string { length := new(big.Float).SetFloat64(float64(len(points))) - return fmt.Sprintf("%.3f", sum.Quo(sum, length)) + return fmt.Sprintf(generateFloatFormat(meteringDefaultPrecision), sum.Quo(sum, length)) } func getCurrencyUnit() string { diff --git a/pkg/models/monitoring/utils_test.go b/pkg/models/monitoring/utils_test.go new file mode 100644 index 000000000..29ecc47f4 --- /dev/null +++ b/pkg/models/monitoring/utils_test.go @@ -0,0 +1,403 @@ +package monitoring + +import ( + "fmt" + "io/ioutil" + "os" + "testing" + + "github.com/google/go-cmp/cmp" + "gopkg.in/yaml.v2" + + "kubesphere.io/kubesphere/pkg/simple/client/monitoring" +) + +func TestGetMaxPointValue(t *testing.T) { + tests := []struct { + actualPoints []monitoring.Point + expectedValue string + }{ + { + actualPoints: []monitoring.Point{ + {1.0, 2.0}, + {3.0, 4.0}, + }, + expectedValue: "4.0000000000", + }, + { + actualPoints: []monitoring.Point{ + {2, 1}, + {4, 3.1}, + }, + expectedValue: "3.1000000000", + }, + { + actualPoints: []monitoring.Point{ + {5, 100}, + {6, 100000.001}, + }, + expectedValue: "100000.0010000000", + }, + } + + for i, tt := range tests { + t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { + max := getMaxPointValue(tt.actualPoints) + if max != tt.expectedValue { + t.Fatal("max point value caculation is wrong.") + } + }) + } +} + +func TestGetMinPointValue(t *testing.T) { + tests := []struct { + actualPoints []monitoring.Point + expectedValue string + }{ + { + actualPoints: []monitoring.Point{ + {1.0, 2.0}, + {3.0, 4.0}, + }, + expectedValue: "2.0000000000", + }, + { + actualPoints: []monitoring.Point{ + {2, 1}, + {4, 3.1}, + }, + expectedValue: "1.0000000000", + }, + { + actualPoints: []monitoring.Point{ + {5, 100}, + {6, 100000.001}, + }, + expectedValue: "100.0000000000", + }, + } + + for i, tt := range tests { + t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { + max := getMinPointValue(tt.actualPoints) + if max != tt.expectedValue { + t.Fatal("min point value caculation is wrong.") + } + }) + } +} + +func TestGetSumPointValue(t *testing.T) { + tests := []struct { + actualPoints []monitoring.Point + expectedValue string + }{ + { + actualPoints: []monitoring.Point{ + {1.0, 2.0}, + {3.0, 4.0}, + }, + expectedValue: "6.0000000000", + }, + { + actualPoints: []monitoring.Point{ + {2, 1}, + {4, 3.1}, + }, + expectedValue: "4.1000000000", + }, + { + actualPoints: []monitoring.Point{ + {5, 100}, + {6, 100000.001}, + }, + expectedValue: "100100.0010000000", + }, + } + + for i, tt := range tests { + t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { + max := getSumPointValue(tt.actualPoints) + if max != tt.expectedValue { + t.Fatal("sum point value caculation is wrong.") + } + }) + } +} + +func TestGetAvgPointValue(t *testing.T) { + tests := []struct { + actualPoints []monitoring.Point + expectedValue string + }{ + { + actualPoints: []monitoring.Point{ + {1.0, 2.0}, + {3.0, 4.0}, + }, + expectedValue: "3.0000000000", + }, + { + actualPoints: []monitoring.Point{ + {2, 1}, + {4, 3.1}, + }, + expectedValue: "2.0500000000", + }, + { + actualPoints: []monitoring.Point{ + {5, 100}, + {6, 100000.001}, + }, + expectedValue: "50050.0005000000", + }, + } + + for i, tt := range tests { + t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { + max := getAvgPointValue(tt.actualPoints) + if max != tt.expectedValue { + t.Fatal("avg point value caculattion is wrong.") + } + }) + } +} + +func saveTestConfig(t *testing.T, conf *MeterConfig) { + content, err := yaml.Marshal(conf) + if err != nil { + t.Fatalf("error marshal config. %v", err) + } + + err = ioutil.WriteFile(meteringConfigName, content, 0640) + if err != nil { + t.Fatalf("error write configuration file, %v", err) + } +} + +func cleanTestConfig(t *testing.T) { + + if _, err := os.Stat(meteringConfigName); os.IsNotExist(err) { + t.Log("file not exists, skipping") + return + } + + err := os.Remove(meteringConfigName) + if err != nil { + t.Fatalf("remove %s file failed", meteringConfigName) + } + +} + +func TestGetCurrencyUnit(t *testing.T) { + if getCurrencyUnit() != "" { + t.Fatal("currency unit should be empty") + } + + saveTestConfig(t, &MeterConfig{ + Billing: Billing{ + PriceInfo: PriceInfo{ + IngressNetworkTrafficPerMegabytesPerHour: 1, + EgressNetworkTrafficPerMegabytesPerHour: 2, + CpuPerCorePerHour: 3, + MemPerGigabytesPerHour: 4, + PvcPerGigabytesPerHour: 5, + CurrencyUnit: "CNY", + }, + }, + }) + defer cleanTestConfig(t) + + if getCurrencyUnit() != "CNY" { + t.Fatal("failed to get currency unit from config") + } +} + +func TestGenerateFloatFormat(t *testing.T) { + format := generateFloatFormat(10) + if format != "%.10f" { + t.Fatalf("get currency float format failed, %s", format) + } +} + +func TestGetResourceUnit(t *testing.T) { + + tests := []struct { + meterName string + expectedValue string + }{ + { + meterName: "no-exist", + expectedValue: "", + }, + { + meterName: "meter_cluster_cpu_usage", + expectedValue: "cores", + }, + } + for _, tt := range tests { + if getResourceUnit(tt.meterName) != tt.expectedValue { + t.Fatal("get resource unit failed") + } + } + +} + +func TestSquashPoints(t *testing.T) { + + tests := []struct { + input []monitoring.Point + factor int + expected []monitoring.Point + }{ + { + input: []monitoring.Point{ + {1, 1}, + {2, 2}, + {3, 3}, + {4, 4}, + {5, 5}, + {6, 6}, + {7, 7}, + {8, 8}, + }, + factor: 1, + expected: []monitoring.Point{ + {1, 1}, + {2, 2}, + {3, 3}, + {4, 4}, + {5, 5}, + {6, 6}, + {7, 7}, + {8, 8}, + }, + }, + { + input: []monitoring.Point{ + {1, 1}, + {2, 2}, + {3, 3}, + {4, 4}, + {5, 5}, + {6, 6}, + {7, 7}, + {8, 8}, + }, + factor: 2, + expected: []monitoring.Point{ + {2, 3}, + {4, 7}, + {6, 11}, + {8, 15}, + }, + }, + } + + for _, tt := range tests { + got := squashPoints(tt.input, tt.factor) + if diff := cmp.Diff(got, tt.expected); diff != "" { + t.Errorf("%T differ (-got, +want): %s", tt.expected, diff) + } + } +} + +func TestGetFeeWithMeterName(t *testing.T) { + + saveTestConfig(t, &MeterConfig{ + Billing: Billing{ + PriceInfo: PriceInfo{ + IngressNetworkTrafficPerMegabytesPerHour: 1, + EgressNetworkTrafficPerMegabytesPerHour: 2, + CpuPerCorePerHour: 3, + MemPerGigabytesPerHour: 4, + PvcPerGigabytesPerHour: 5, + CurrencyUnit: "CNY", + }, + }, + }) + defer cleanTestConfig(t) + + tests := []struct { + metric monitoring.Metric + scalingMap map[string]float64 + expected monitoring.MetricData + }{ + { + metric: monitoring.Metric{ + MetricName: "test", + MetricData: monitoring.MetricData{ + MetricType: monitoring.MetricTypeMatrix, + MetricValues: []monitoring.MetricValue{ + { + Metadata: map[string]string{}, + Series: []monitoring.Point{ + {1, 1}, + {2, 2}, + }, + }, + }, + }, + }, + scalingMap: map[string]float64{ + "test": 1, + }, + expected: monitoring.MetricData{ + MetricType: monitoring.MetricTypeMatrix, + MetricValues: []monitoring.MetricValue{ + { + Metadata: map[string]string{}, + Series: []monitoring.Point{ + {1, 1}, + {2, 2}, + }, + MinValue: "1.0000000000", + MaxValue: "2.0000000000", + AvgValue: "1.5000000000", + SumValue: "3.0000000000", + CurrencyUnit: "CNY", + }, + }, + }, + }, + { + metric: monitoring.Metric{ + MetricName: "test", + MetricData: monitoring.MetricData{ + MetricType: monitoring.MetricTypeVector, + MetricValues: []monitoring.MetricValue{ + { + Metadata: map[string]string{}, + Sample: &monitoring.Point{1, 2}, + }, + }, + }, + }, + scalingMap: nil, + expected: monitoring.MetricData{ + MetricType: monitoring.MetricTypeVector, + MetricValues: []monitoring.MetricValue{ + { + Metadata: map[string]string{}, + Sample: &monitoring.Point{1, 2}, + MinValue: "2.0000000000", + MaxValue: "2.0000000000", + AvgValue: "2.0000000000", + SumValue: "2.0000000000", + CurrencyUnit: "CNY", + }, + }, + }, + }, + } + + for _, test := range tests { + got := updateMetricStatData(test.metric, test.scalingMap) + if diff := cmp.Diff(got, test.expected); diff != "" { + t.Errorf("%T differ (-got, +want): %s", test.expected, diff) + return + } + } + +} diff --git a/pkg/models/tenant/metering_test.go b/pkg/models/tenant/metering_test.go new file mode 100644 index 000000000..434377705 --- /dev/null +++ b/pkg/models/tenant/metering_test.go @@ -0,0 +1,193 @@ +package tenant + +import ( + "fmt" + "testing" + "time" + + "github.com/google/go-cmp/cmp" + + "kubesphere.io/kubesphere/pkg/models/metering" + monitoringmodel "kubesphere.io/kubesphere/pkg/models/monitoring" + "kubesphere.io/kubesphere/pkg/simple/client/monitoring" +) + +func TestIsRangeQuery(t *testing.T) { + tests := []struct { + options QueryOptions + expectedValue bool + }{ + { + options: QueryOptions{}, + expectedValue: true, + }, + { + options: QueryOptions{Time: time.Now()}, + expectedValue: false, + }, + } + + for i, tt := range tests { + t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { + if tt.options.isRangeQuery() != tt.expectedValue { + t.Fatal("error isRangeQuery") + } + }) + } +} + +func TestShouldSort(t *testing.T) { + tests := []struct { + options QueryOptions + expectedValue bool + }{ + { + options: QueryOptions{ + Target: "test", + Identifier: "test", + }, + expectedValue: true, + }, + { + options: QueryOptions{}, + expectedValue: false, + }, + } + + for i, tt := range tests { + t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { + if tt.options.shouldSort() != tt.expectedValue { + t.Fatal("error shouldSort") + } + }) + } +} + +func TestGetMetricPosMap(t *testing.T) { + tests := []struct { + metrics []monitoring.Metric + expectedValue map[string]int + }{ + { + metrics: []monitoring.Metric{ + {MetricName: "one"}, + {MetricName: "two"}, + {MetricName: "three"}, + {MetricName: "four"}, + }, + expectedValue: map[string]int{ + "one": 0, + "two": 1, + "three": 2, + "four": 3, + }, + }, + } + + for i, tt := range tests { + t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { + if diff := cmp.Diff(getMetricPosMap(tt.metrics), tt.expectedValue); diff != "" { + t.Errorf("%T differ (-got, +want): %s", tt.expectedValue, diff) + } + }) + } +} + +func TestTransformMetricData(t *testing.T) { + tests := []struct { + metrics monitoringmodel.Metrics + expectedValue metering.PodsStats + }{ + { + metrics: monitoringmodel.Metrics{ + Results: []monitoring.Metric{ + { + MetricName: "meter_pod_cpu_usage", + MetricData: monitoring.MetricData{ + MetricValues: monitoring.MetricValues{ + { + Metadata: map[string]string{ + "pod": "pod1", + }, + SumValue: "10", + }, + }, + }, + }, + { + MetricName: "meter_pod_memory_usage_wo_cache", + MetricData: monitoring.MetricData{ + MetricValues: monitoring.MetricValues{ + { + Metadata: map[string]string{ + "pod": "pod1", + }, + SumValue: "200", + }, + }, + }, + }, + { + MetricName: "meter_pod_net_bytes_transmitted", + MetricData: monitoring.MetricData{ + MetricValues: monitoring.MetricValues{ + { + Metadata: map[string]string{ + "pod": "pod1", + }, + SumValue: "300", + }, + }, + }, + }, + { + MetricName: "meter_pod_net_bytes_received", + MetricData: monitoring.MetricData{ + MetricValues: monitoring.MetricValues{ + { + Metadata: map[string]string{ + "pod": "pod1", + }, + SumValue: "300", + }, + }, + }, + }, + { + MetricName: "meter_pod_pvc_bytes_total", + MetricData: monitoring.MetricData{ + MetricValues: monitoring.MetricValues{ + { + Metadata: map[string]string{ + "pod": "pod1", + }, + SumValue: "400", + }, + }, + }, + }, + }, + }, + expectedValue: metering.PodsStats{ + "pod1": &metering.PodStatistic{ + CPUUsage: 10, + MemoryUsageWoCache: 200, + NetBytesReceived: 300, + NetBytesTransmitted: 300, + PVCBytesTotal: 400, + }, + }, + }, + } + + var tOperator tenantOperator + + for i, tt := range tests { + t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { + if diff := cmp.Diff(tOperator.transformMetricData(tt.metrics), tt.expectedValue); diff != "" { + t.Errorf("%T differ (-got, +want): %s", tt.expectedValue, diff) + } + }) + } + +}