mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Storage: limit the number of uploaded files (#50796)
* #50608: sql file upload quotas * rename `files_in_sql` to `file` * merge conflict
This commit is contained in:
@@ -5517,11 +5517,8 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "20"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "21"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "22"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "23"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "24"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "25"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "26"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "27"]
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "23"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "24"]
|
||||
],
|
||||
"public/app/features/plugins/hooks/tests/useImportAppPlugin.test.tsx:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||
|
||||
@@ -806,6 +806,9 @@ global_session = -1
|
||||
# global limit of alerts
|
||||
global_alert_rule = -1
|
||||
|
||||
# global limit of files uploaded to the SQL DB
|
||||
global_file = 1000
|
||||
|
||||
#################################### Unified Alerting ####################
|
||||
[unified_alerting]
|
||||
# Enable the Unified Alerting sub-system and interface. When enabled we'll migrate all of your alert rules and notification channels to the new system. New alert rules will be created and your notification channels will be converted into an Alertmanager configuration. Previous data is preserved to enable backwards compatibility but new data is removed when switching. When this configuration section and flag are not defined, the state is defined at runtime. See the documentation for more details.
|
||||
|
||||
@@ -190,6 +190,11 @@ func (s *Service) getQuotaScopes(target string) ([]models.QuotaScope, error) {
|
||||
models.QuotaScope{Name: "org", Target: target, DefaultLimit: s.Cfg.Quota.Org.AlertRule},
|
||||
)
|
||||
return scopes, nil
|
||||
case "file":
|
||||
scopes = append(scopes,
|
||||
models.QuotaScope{Name: "global", Target: target, DefaultLimit: s.Cfg.Quota.Global.File},
|
||||
)
|
||||
return scopes, nil
|
||||
default:
|
||||
return scopes, quota.ErrInvalidQuotaTarget
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
const (
|
||||
alertRuleTarget = "alert_rule"
|
||||
dashboardTarget = "dashboard"
|
||||
filesTarget = "file"
|
||||
)
|
||||
|
||||
type targetCount struct {
|
||||
@@ -255,7 +256,19 @@ func (ss *SQLStore) UpdateUserQuota(ctx context.Context, cmd *models.UpdateUserQ
|
||||
func (ss *SQLStore) GetGlobalQuotaByTarget(ctx context.Context, query *models.GetGlobalQuotaByTargetQuery) error {
|
||||
return ss.WithDbSession(ctx, func(sess *DBSession) error {
|
||||
var used int64
|
||||
if query.Target != alertRuleTarget || query.UnifiedAlertingEnabled {
|
||||
|
||||
if query.Target == filesTarget {
|
||||
// get quota used.
|
||||
rawSQL := fmt.Sprintf("SELECT COUNT(*) AS count FROM %s",
|
||||
dialect.Quote("file"))
|
||||
|
||||
notFolderCondition := fmt.Sprintf(" WHERE path NOT LIKE '%s'", "%/")
|
||||
resp := make([]*targetCount, 0)
|
||||
if err := sess.SQL(rawSQL + notFolderCondition).Find(&resp); err != nil {
|
||||
return err
|
||||
}
|
||||
used = resp[0].Count
|
||||
} else if query.Target != alertRuleTarget || query.UnifiedAlertingEnabled {
|
||||
// get quota used.
|
||||
rawSQL := fmt.Sprintf("SELECT COUNT(*) AS count FROM %s",
|
||||
dialect.Quote(query.Target))
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/response"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/quota"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
"github.com/grafana/grafana/pkg/web"
|
||||
)
|
||||
@@ -26,12 +27,14 @@ type HTTPStorageService interface {
|
||||
}
|
||||
|
||||
type httpStorage struct {
|
||||
store StorageService
|
||||
store StorageService
|
||||
quotaService quota.Service
|
||||
}
|
||||
|
||||
func ProvideHTTPService(store StorageService) HTTPStorageService {
|
||||
func ProvideHTTPService(store StorageService, quotaService quota.Service) HTTPStorageService {
|
||||
return &httpStorage{
|
||||
store: store,
|
||||
store: store,
|
||||
quotaService: quotaService,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,6 +61,16 @@ func UploadErrorToStatusCode(err error) int {
|
||||
}
|
||||
|
||||
func (s *httpStorage) Upload(c *models.ReqContext) response.Response {
|
||||
// assumes we are only uploading to the SQL database - TODO: refactor once we introduce object stores
|
||||
quotaReached, err := s.quotaService.CheckQuotaReached(c.Req.Context(), "file", nil)
|
||||
if err != nil {
|
||||
return response.Error(500, "Internal server error", err)
|
||||
}
|
||||
|
||||
if quotaReached {
|
||||
return response.Error(400, "File quota reached", errors.New("file quota reached"))
|
||||
}
|
||||
|
||||
type rspInfo struct {
|
||||
Message string `json:"message,omitempty"`
|
||||
Path string `json:"path,omitempty"`
|
||||
|
||||
@@ -24,6 +24,7 @@ type GlobalQuota struct {
|
||||
ApiKey int64 `target:"api_key"`
|
||||
Session int64 `target:"-"`
|
||||
AlertRule int64 `target:"alert_rule"`
|
||||
File int64 `target:"file"`
|
||||
}
|
||||
|
||||
func (q *OrgQuota) ToMap() map[string]int64 {
|
||||
@@ -94,6 +95,7 @@ func (cfg *Cfg) readQuotaSettings() {
|
||||
Dashboard: quota.Key("global_dashboard").MustInt64(-1),
|
||||
ApiKey: quota.Key("global_api_key").MustInt64(-1),
|
||||
Session: quota.Key("global_session").MustInt64(-1),
|
||||
File: quota.Key("global_file").MustInt64(-1),
|
||||
AlertRule: alertGlobalQuota,
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user