|
|
@@ -14,7 +14,8 @@ const Ki = 1024
|
|
|
const Mi = Ki * 1024
|
|
|
const Gi = Mi * 1024
|
|
|
|
|
|
-const minute = 60.0
|
|
|
+const second = 1.0
|
|
|
+const minute = second * 60.0
|
|
|
const hour = minute * 60.0
|
|
|
|
|
|
var windowStart = time.Date(2020, 6, 16, 0, 0, 0, 0, time.UTC)
|
|
|
@@ -201,7 +202,6 @@ var pvMap1 = map[pvKey]*pv{
|
|
|
},
|
|
|
}
|
|
|
|
|
|
-/* pv/pvc Helpers */
|
|
|
func TestBuildPVMap(t *testing.T) {
|
|
|
pvMap1NoBytes := make(map[pvKey]*pv, len(pvMap1))
|
|
|
for thisPVKey, thisPV := range pvMap1 {
|
|
|
@@ -211,6 +211,9 @@ func TestBuildPVMap(t *testing.T) {
|
|
|
pvMap1NoBytes[thisPVKey] = clonePV
|
|
|
}
|
|
|
|
|
|
+ // These test cases are mocking behavior from Prometheus v3+
|
|
|
+ prometheusVersion = "3.0.0"
|
|
|
+
|
|
|
testCases := map[string]struct {
|
|
|
resolution time.Duration
|
|
|
resultsPVCostPerGiBHour []*prom.QueryResult
|
|
|
@@ -272,9 +275,6 @@ func TestBuildPVMap(t *testing.T) {
|
|
|
"persistentvolume": "pv1",
|
|
|
},
|
|
|
Values: []*util.Vector{
|
|
|
- {
|
|
|
- Timestamp: startFloat,
|
|
|
- },
|
|
|
{
|
|
|
Timestamp: startFloat + (hour * 6),
|
|
|
},
|
|
|
@@ -292,9 +292,6 @@ func TestBuildPVMap(t *testing.T) {
|
|
|
"persistentvolume": "pv2",
|
|
|
},
|
|
|
Values: []*util.Vector{
|
|
|
- {
|
|
|
- Timestamp: startFloat,
|
|
|
- },
|
|
|
{
|
|
|
Timestamp: startFloat + (hour * 6),
|
|
|
},
|
|
|
@@ -315,9 +312,6 @@ func TestBuildPVMap(t *testing.T) {
|
|
|
"persistentvolume": "pv3",
|
|
|
},
|
|
|
Values: []*util.Vector{
|
|
|
- {
|
|
|
- Timestamp: startFloat + (hour * 6),
|
|
|
- },
|
|
|
{
|
|
|
Timestamp: startFloat + (hour * 12),
|
|
|
},
|
|
|
@@ -332,9 +326,6 @@ func TestBuildPVMap(t *testing.T) {
|
|
|
"persistentvolume": "pv4",
|
|
|
},
|
|
|
Values: []*util.Vector{
|
|
|
- {
|
|
|
- Timestamp: startFloat,
|
|
|
- },
|
|
|
{
|
|
|
Timestamp: startFloat + (hour * 6),
|
|
|
},
|
|
|
@@ -372,8 +363,6 @@ func TestBuildPVMap(t *testing.T) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-/* Helper Helpers */
|
|
|
-
|
|
|
func TestGetUnmountedPodForCluster(t *testing.T) {
|
|
|
testCases := map[string]struct {
|
|
|
window opencost.Window
|
|
|
@@ -455,37 +444,38 @@ func TestGetUnmountedPodForCluster(t *testing.T) {
|
|
|
}
|
|
|
|
|
|
func TestCalculateStartAndEnd(t *testing.T) {
|
|
|
+ // These test cases are mocking behavior from Prometheus v3+
|
|
|
+ prometheusVersion = "3.0.0"
|
|
|
|
|
|
testCases := map[string]struct {
|
|
|
- resolution time.Duration
|
|
|
+ resolution time.Duration // User defined config when querying Prometheus
|
|
|
+ window opencost.Window // User defined config when querying Allocations/Assets
|
|
|
expectedStart time.Time
|
|
|
expectedEnd time.Time
|
|
|
result *prom.QueryResult
|
|
|
}{
|
|
|
+ // Example: avg(node_total_hourly_cost{}) by (node, provider_id)[1h:1h]
|
|
|
"1 hour resolution, 1 hour window": {
|
|
|
resolution: time.Hour,
|
|
|
+ window: opencost.NewClosedWindow(windowStart, windowStart.Add(time.Hour)),
|
|
|
expectedStart: windowStart,
|
|
|
expectedEnd: windowStart.Add(time.Hour),
|
|
|
result: &prom.QueryResult{
|
|
|
Values: []*util.Vector{
|
|
|
- {
|
|
|
- Timestamp: startFloat,
|
|
|
- },
|
|
|
{
|
|
|
Timestamp: startFloat + (minute * 60),
|
|
|
},
|
|
|
},
|
|
|
},
|
|
|
},
|
|
|
+ // Example: avg(node_total_hourly_cost{}) by (node, provider_id)[1h:30m]
|
|
|
"30 minute resolution, 1 hour window": {
|
|
|
resolution: time.Minute * 30,
|
|
|
+ window: opencost.NewClosedWindow(windowStart, windowStart.Add(time.Hour)),
|
|
|
expectedStart: windowStart,
|
|
|
expectedEnd: windowStart.Add(time.Hour),
|
|
|
result: &prom.QueryResult{
|
|
|
Values: []*util.Vector{
|
|
|
- {
|
|
|
- Timestamp: startFloat,
|
|
|
- },
|
|
|
{
|
|
|
Timestamp: startFloat + (minute * 30),
|
|
|
},
|
|
|
@@ -495,36 +485,83 @@ func TestCalculateStartAndEnd(t *testing.T) {
|
|
|
},
|
|
|
},
|
|
|
},
|
|
|
+ // Example: avg(node_total_hourly_cost{}) by (node, provider_id)[45m:15m]
|
|
|
"15 minute resolution, 45 minute window": {
|
|
|
resolution: time.Minute * 15,
|
|
|
+ window: opencost.NewClosedWindow(windowStart, windowStart.Add(time.Minute*45)),
|
|
|
expectedStart: windowStart,
|
|
|
expectedEnd: windowStart.Add(time.Minute * 45),
|
|
|
result: &prom.QueryResult{
|
|
|
Values: []*util.Vector{
|
|
|
{
|
|
|
- Timestamp: startFloat + (minute * 0),
|
|
|
+ Timestamp: startFloat + (minute * 15),
|
|
|
+ },
|
|
|
+ {
|
|
|
+ Timestamp: startFloat + (minute * 30),
|
|
|
+ },
|
|
|
+ {
|
|
|
+ Timestamp: startFloat + (minute * 45),
|
|
|
+ },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ // Example: avg(node_total_hourly_cost{}) by (node, provider_id)[30m:5m]
|
|
|
+ "5 minute resolution, 30 minute window": {
|
|
|
+ resolution: time.Minute * 5,
|
|
|
+ window: opencost.NewClosedWindow(windowStart, windowStart.Add(time.Minute*30)),
|
|
|
+ expectedStart: windowStart,
|
|
|
+ expectedEnd: windowStart.Add(time.Minute * 30),
|
|
|
+ result: &prom.QueryResult{
|
|
|
+ Values: []*util.Vector{
|
|
|
+ {
|
|
|
+ Timestamp: startFloat + (minute * 5),
|
|
|
+ },
|
|
|
+ {
|
|
|
+ Timestamp: startFloat + (minute * 10),
|
|
|
},
|
|
|
{
|
|
|
Timestamp: startFloat + (minute * 15),
|
|
|
},
|
|
|
+ {
|
|
|
+ Timestamp: startFloat + (minute * 20),
|
|
|
+ },
|
|
|
+ {
|
|
|
+ Timestamp: startFloat + (minute * 25),
|
|
|
+ },
|
|
|
{
|
|
|
Timestamp: startFloat + (minute * 30),
|
|
|
},
|
|
|
+ },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ // Example: avg(node_total_hourly_cost{}) by (node, provider_id)[30m:5m]
|
|
|
+ "5 minute resolution, 30 minute window, partial data": {
|
|
|
+ resolution: time.Minute * 5,
|
|
|
+ window: opencost.NewClosedWindow(windowStart, windowStart.Add(time.Minute*30)),
|
|
|
+ expectedStart: windowStart.Add(time.Minute * 5),
|
|
|
+ expectedEnd: windowStart.Add(time.Minute * 20),
|
|
|
+ result: &prom.QueryResult{
|
|
|
+ Values: []*util.Vector{
|
|
|
{
|
|
|
- Timestamp: startFloat + (minute * 45),
|
|
|
+ Timestamp: startFloat + (minute * 10),
|
|
|
+ },
|
|
|
+ {
|
|
|
+ Timestamp: startFloat + (minute * 15),
|
|
|
+ },
|
|
|
+ {
|
|
|
+ Timestamp: startFloat + (minute * 20),
|
|
|
},
|
|
|
},
|
|
|
},
|
|
|
},
|
|
|
+ // Example: avg(node_total_hourly_cost{}) by (node, provider_id)[5m:1m]
|
|
|
"1 minute resolution, 5 minute window": {
|
|
|
resolution: time.Minute,
|
|
|
+ window: opencost.NewClosedWindow(windowStart.Add(time.Minute*15), windowStart.Add(time.Minute*20)),
|
|
|
expectedStart: windowStart.Add(time.Minute * 15),
|
|
|
expectedEnd: windowStart.Add(time.Minute * 20),
|
|
|
result: &prom.QueryResult{
|
|
|
Values: []*util.Vector{
|
|
|
- {
|
|
|
- Timestamp: startFloat + (minute * 15),
|
|
|
- },
|
|
|
{
|
|
|
Timestamp: startFloat + (minute * 16),
|
|
|
},
|
|
|
@@ -543,26 +580,47 @@ func TestCalculateStartAndEnd(t *testing.T) {
|
|
|
},
|
|
|
},
|
|
|
},
|
|
|
+ // Example: avg(node_total_hourly_cost{}) by (node, provider_id)[5m:1m]
|
|
|
+ "1 minute resolution, 5 minute window, partial data": {
|
|
|
+ resolution: time.Minute,
|
|
|
+ window: opencost.NewClosedWindow(windowStart.Add(time.Minute*15), windowStart.Add(time.Minute*20)),
|
|
|
+ expectedStart: windowStart.Add(time.Minute * 18),
|
|
|
+ expectedEnd: windowStart.Add(time.Minute * 20),
|
|
|
+ result: &prom.QueryResult{
|
|
|
+ Values: []*util.Vector{
|
|
|
+ {
|
|
|
+ Timestamp: startFloat + (minute * 19),
|
|
|
+ },
|
|
|
+ {
|
|
|
+ Timestamp: startFloat + (minute * 20),
|
|
|
+ },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ // Example: avg(node_total_hourly_cost{}) by (node, provider_id)[1m:1m]
|
|
|
"1 minute resolution, 1 minute window": {
|
|
|
resolution: time.Minute,
|
|
|
+ window: opencost.NewClosedWindow(windowStart.Add(time.Minute*14).Add(time.Second*30), windowStart.Add(time.Minute*15).Add(time.Second*30)),
|
|
|
expectedStart: windowStart.Add(time.Minute * 14).Add(time.Second * 30),
|
|
|
expectedEnd: windowStart.Add(time.Minute * 15).Add(time.Second * 30),
|
|
|
result: &prom.QueryResult{
|
|
|
Values: []*util.Vector{
|
|
|
{
|
|
|
- Timestamp: startFloat + (minute * 15),
|
|
|
+ Timestamp: startFloat + (minute * 15) + (second * 30),
|
|
|
},
|
|
|
},
|
|
|
},
|
|
|
},
|
|
|
+ // Example: avg(node_total_hourly_cost{}) by (node, provider_id)[1m:1m]
|
|
|
"1 minute resolution, 1 minute window, at window start": {
|
|
|
resolution: time.Minute,
|
|
|
+ window: opencost.NewClosedWindow(windowStart, windowStart.Add(time.Second*30)),
|
|
|
expectedStart: windowStart,
|
|
|
expectedEnd: windowStart.Add(time.Second * 30),
|
|
|
result: &prom.QueryResult{
|
|
|
Values: []*util.Vector{
|
|
|
{
|
|
|
- Timestamp: startFloat,
|
|
|
+ Timestamp: startFloat + (second * 30),
|
|
|
},
|
|
|
},
|
|
|
},
|
|
|
@@ -571,7 +629,7 @@ func TestCalculateStartAndEnd(t *testing.T) {
|
|
|
|
|
|
for name, testCase := range testCases {
|
|
|
t.Run(name, func(t *testing.T) {
|
|
|
- start, end := calculateStartAndEnd(testCase.result, testCase.resolution, window)
|
|
|
+ start, end := calculateStartAndEnd(testCase.result, testCase.resolution, testCase.window)
|
|
|
if !start.Equal(testCase.expectedStart) {
|
|
|
t.Errorf("start does not match: expected %v; got %v", testCase.expectedStart, start)
|
|
|
}
|