@@ -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,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
403
pkg/models/monitoring/utils_test.go
Normal file
403
pkg/models/monitoring/utils_test.go
Normal file
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
193
pkg/models/tenant/metering_test.go
Normal file
193
pkg/models/tenant/metering_test.go
Normal file
@@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user