فهرست منبع

Merge branch 'develop' into nik/plugin_aggs_filters

Thomas Evans 2 سال پیش
والد
کامیت
57bc0b6a14
3فایلهای تغییر یافته به همراه151 افزوده شده و 0 حذف شده
  1. 3 0
      pkg/cloud/scaleway/provider.go
  2. 68 0
      pkg/customcost/repositoryquerier.go
  3. 80 0
      pkg/customcost/repositoryquerier_test.go

+ 3 - 0
pkg/cloud/scaleway/provider.go

@@ -68,7 +68,10 @@ func (c *Scaleway) DownloadPricingData() error {
 		"fr-par-3": 0.00032,
 		"nl-ams-1": 0.00008,
 		"nl-ams-2": 0.00008,
+		"nl-ams-3": 0.00008,
 		"pl-waw-1": 0.00011,
+		"pl-waw-2": 0.00011,
+		"pl-waw-3": 0.00011,
 	}
 
 	c.Pricing = make(map[string]*ScalewayPricing)

+ 68 - 0
pkg/customcost/repositoryquerier.go

@@ -8,6 +8,7 @@ import (
 
 	"github.com/opencost/opencost/core/pkg/opencost"
 	"github.com/opencost/opencost/core/pkg/util/timeutil"
+	"github.com/opencost/opencost/pkg/env"
 )
 
 type RepositoryQuerier struct {
@@ -77,8 +78,75 @@ func (rq *RepositoryQuerier) QueryTotal(ctx context.Context, request CostTotalRe
 	return NewCostResponse(ccs), nil
 }
 
+var allSteppedAccumulateOptions = []opencost.AccumulateOption{
+	opencost.AccumulateOptionHour,
+	opencost.AccumulateOptionDay,
+}
+
+func hasHourly(opts []opencost.AccumulateOption) bool {
+	for _, opt := range opts {
+		if opt == opencost.AccumulateOptionHour {
+			return true
+		}
+	}
+
+	return false
+}
+
+func hasDaily(opts []opencost.AccumulateOption) bool {
+	for _, opt := range opts {
+		if opt == opencost.AccumulateOptionDay {
+			return true
+		}
+	}
+
+	return false
+}
+
+// GetCustomCostAccumulateOption determines defaults in a way that matches options presented in the UI
+func getCustomCostAccumulateOption(window opencost.Window, from []opencost.AccumulateOption) (opencost.AccumulateOption, error) {
+	if window.IsOpen() || window.IsNegative() {
+		return opencost.AccumulateOptionNone, fmt.Errorf("invalid window '%s'", window.String())
+	}
+
+	if len(from) == 0 {
+		from = allSteppedAccumulateOptions
+	}
+
+	hourlyStoreHours := env.GetDataRetentionHourlyResolutionHours()
+	hourlySteps := time.Duration(hourlyStoreHours) * time.Hour
+	oldestHourly := time.Now().Add(-1 * hourlySteps)
+
+	// Use hourly if...
+	//  (1) hourly is an option;
+	//  (2) we have hourly store coverage; and
+	//  (3) the window duration is less than the hourly break point.
+	if hasHourly(from) && oldestHourly.Before(*window.Start()) && window.Duration() <= hourlySteps {
+		return opencost.AccumulateOptionHour, nil
+	}
+
+	dailyStoreDays := env.GetCustomCostQueryWindowDays()
+	dailySteps := time.Duration(dailyStoreDays) * timeutil.Day
+	oldestDaily := time.Now().Add(-1 * dailySteps)
+	// Use daily if...
+	//  (1) daily is an option; and
+	//  (2) we have daily store coverage
+	if hasDaily(from) && oldestDaily.Before(*window.Start()) {
+		return opencost.AccumulateOptionDay, nil
+	}
+
+	return opencost.AccumulateOptionNone, fmt.Errorf("no valid accumulate option in %v for %s", from, window)
+}
+
 func (rq *RepositoryQuerier) QueryTimeseries(ctx context.Context, request CostTimeseriesRequest) (*CostTimeseriesResponse, error) {
 	window, _ := opencost.NewClosedWindow(request.Start, request.End).GetAccumulateWindow(request.Accumulate)
+	var err error
+	if request.Accumulate == opencost.AccumulateOptionNone {
+		request.Accumulate, err = getCustomCostAccumulateOption(window, nil)
+		if err != nil {
+			return nil, fmt.Errorf("error determining accumulation option: %v", err)
+		}
+	}
 
 	windows, err := window.GetAccumulateWindows(request.Accumulate)
 	if err != nil {

+ 80 - 0
pkg/customcost/repositoryquerier_test.go

@@ -0,0 +1,80 @@
+package customcost
+
+import (
+	"testing"
+	"time"
+
+	"github.com/opencost/opencost/core/pkg/opencost"
+	"github.com/opencost/opencost/core/pkg/util/timeutil"
+)
+
+func TestGetCustomCostAccumulateOption(t *testing.T) {
+	now := time.Now().UTC()
+	nextHour := opencost.RoundForward(now, time.Hour)
+	midnight := opencost.RoundForward(now, timeutil.Day)
+
+	tests := map[string]struct {
+		window  opencost.Window
+		want    opencost.AccumulateOption
+		from    []opencost.AccumulateOption
+		wantErr bool
+	}{
+		"open window": {
+			window:  opencost.NewWindow(nil, nil),
+			from:    nil,
+			want:    opencost.AccumulateOptionNone,
+			wantErr: true,
+		},
+		"negative window": {
+			window:  opencost.NewClosedWindow(midnight, midnight.Add(-1)),
+			from:    nil,
+			want:    opencost.AccumulateOptionNone,
+			wantErr: true,
+		},
+		"hourly max": {
+			window:  opencost.NewClosedWindow(nextHour.Add(-time.Hour*49).Add(-1), nextHour),
+			from:    nil,
+			want:    opencost.AccumulateOptionDay,
+			wantErr: false,
+		},
+		"daily min": {
+			window:  opencost.NewClosedWindow(nextHour.Add(-time.Hour*49).Add(-1), nextHour),
+			from:    nil,
+			want:    opencost.AccumulateOptionDay,
+			wantErr: false,
+		},
+		"daily max": {
+			window:  opencost.NewClosedWindow(midnight.Add(-timeutil.Day*7), midnight),
+			from:    nil,
+			want:    opencost.AccumulateOptionDay,
+			wantErr: false,
+		},
+		"out of range": {
+			window:  opencost.NewClosedWindow(midnight.Add(-timeutil.Day*120), midnight.Add(-timeutil.Day*30)),
+			from:    nil,
+			want:    opencost.AccumulateOptionNone,
+			wantErr: true,
+		},
+		"daily from daily, monthly": {
+			window: opencost.NewClosedWindow(nextHour.Add(-time.Hour*24), nextHour),
+			from: []opencost.AccumulateOption{
+				opencost.AccumulateOptionDay,
+				opencost.AccumulateOptionMonth,
+			},
+			want:    opencost.AccumulateOptionDay,
+			wantErr: false,
+		},
+	}
+	for name, tt := range tests {
+		t.Run(name, func(t *testing.T) {
+			got, err := getCustomCostAccumulateOption(tt.window, tt.from)
+			if (err != nil) != tt.wantErr {
+				t.Errorf("GetAccumulateOption() error = %v, wantErr %v", err, tt.wantErr)
+				return
+			}
+			if got != tt.want {
+				t.Errorf("GetAccumulateOption() got = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}