Browse Source

inital backend suport for quotas. issue #321

Conflicts:
	conf/defaults.ini
	main.go
	pkg/services/sqlstore/migrations/migrations.go
woodsaj 10 years ago
parent
commit
9023171940

+ 6 - 1
conf/defaults.ini

@@ -253,4 +253,9 @@ exchange = grafana_events
 enabled = false
 path = /var/lib/grafana/dashboards
 
-
+[quota]
+user = 10
+dashboard = 100
+data_source = 10
+endpoint  = 10
+collector = 10

+ 3 - 0
main.go

@@ -15,6 +15,7 @@ import (
 	"github.com/grafana/grafana/pkg/log"
 	"github.com/grafana/grafana/pkg/login"
 	"github.com/grafana/grafana/pkg/metrics"
+	"github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/plugins"
 	"github.com/grafana/grafana/pkg/services/eventpublisher"
 	"github.com/grafana/grafana/pkg/services/notifications"
@@ -56,6 +57,8 @@ func main() {
 	eventpublisher.Init()
 	plugins.Init()
 
+	models.InitQuotaDefaults()
+
 	if err := notifications.Init(); err != nil {
 		log.Fatal(3, "Notification service failed to initialize", err)
 	}

+ 2 - 0
pkg/api/api.go

@@ -122,6 +122,8 @@ func Register(r *macaron.Macaron) {
 			r.Post("/users", bind(m.AddOrgUserCommand{}), wrap(AddOrgUser))
 			r.Patch("/users/:userId", bind(m.UpdateOrgUserCommand{}), wrap(UpdateOrgUser))
 			r.Delete("/users/:userId", wrap(RemoveOrgUser))
+			r.Get("/quotas", wrap(GetOrgQuotas))
+			r.Put("/quotas/:target", bind(m.UpdateQuotaCmd{}), wrap(UpdateOrgQuota))
 		}, reqGrafanaAdmin)
 
 		// auth api keys

+ 26 - 0
pkg/api/quota.go

@@ -0,0 +1,26 @@
+package api
+
+import (
+	"github.com/grafana/grafana/pkg/bus"
+	"github.com/grafana/grafana/pkg/middleware"
+	m "github.com/grafana/grafana/pkg/models"
+)
+
+func GetOrgQuotas(c *middleware.Context) Response {
+	query := m.GetQuotasQuery{OrgId: c.ParamsInt64(":orgId")}
+
+	if err := bus.Dispatch(&query); err != nil {
+		return ApiError(500, "Failed to get org quotas", err)
+	}
+
+	return Json(200, query.Result)
+}
+
+func UpdateOrgQuota(c *middleware.Context, cmd m.UpdateQuotaCmd) Response {
+	cmd.OrgId = c.ParamsInt64(":orgId")
+	cmd.Target = m.QuotaTarget(c.Params(":target"))
+	if err := bus.Dispatch(&cmd); err != nil {
+		return ApiError(500, "Failed to update org quotas", err)
+	}
+	return ApiSuccess("Organization quota updated")
+}

+ 63 - 0
pkg/models/quotas.go

@@ -0,0 +1,63 @@
+package models
+
+import (
+	"github.com/grafana/grafana/pkg/setting"
+	"time"
+)
+
+type QuotaTarget string
+
+const (
+	QUOTA_USER       QuotaTarget = "user" //SQL table to query. ie. "select count(*) from user where org_id=?"
+	QUOTA_DATASOURCE QuotaTarget = "data_source"
+	QUOTA_DASHBOARD  QuotaTarget = "dashboard"
+	QUOTA_ENDPOINT   QuotaTarget = "endpoint"
+	QUOTA_COLLECTOR  QuotaTarget = "collector"
+)
+
+// defaults are set from settings package.
+var DefaultQuotas map[QuotaTarget]int64
+
+func InitQuotaDefaults() {
+	// set global defaults.
+	DefaultQuotas = make(map[QuotaTarget]int64)
+	quota := setting.Cfg.Section("quota")
+	DefaultQuotas[QUOTA_USER] = quota.Key("user").MustInt64(10)
+	DefaultQuotas[QUOTA_DATASOURCE] = quota.Key("data_source").MustInt64(10)
+	DefaultQuotas[QUOTA_DASHBOARD] = quota.Key("dashboard").MustInt64(10)
+	DefaultQuotas[QUOTA_ENDPOINT] = quota.Key("endpoint").MustInt64(10)
+	DefaultQuotas[QUOTA_COLLECTOR] = quota.Key("collector").MustInt64(10)
+}
+
+type Quota struct {
+	Id      int64
+	OrgId   int64
+	Target  QuotaTarget
+	Limit   int64
+	Created time.Time
+	Updated time.Time
+}
+
+type QuotaDTO struct {
+	OrgId  int64       `json:"org_id"`
+	Target QuotaTarget `json:"target"`
+	Limit  int64       `json:"limit"`
+	Used   int64       `json:"used"`
+}
+
+type GetQuotaByTargetQuery struct {
+	Target QuotaTarget
+	OrgId  int64
+	Result *QuotaDTO
+}
+
+type GetQuotasQuery struct {
+	OrgId  int64
+	Result []*QuotaDTO
+}
+
+type UpdateQuotaCmd struct {
+	Target QuotaTarget `json:"target"`
+	Limit  int64       `json:"limit"`
+	OrgId  int64       `json:"-"`
+}

+ 1 - 0
pkg/services/sqlstore/migrations/migrations.go

@@ -17,6 +17,7 @@ func AddMigrations(mg *Migrator) {
 	addDataSourceMigration(mg)
 	addApiKeyMigrations(mg)
 	addDashboardSnapshotMigrations(mg)
+	addQuotaMigration(mg)
 }
 
 func addMigrationLogMigrations(mg *Migrator) {

+ 27 - 0
pkg/services/sqlstore/migrations/quota_mig.go

@@ -0,0 +1,27 @@
+package migrations
+
+import (
+	. "github.com/grafana/grafana/pkg/services/sqlstore/migrator"
+)
+
+func addQuotaMigration(mg *Migrator) {
+
+	var quotaV1 = Table{
+		Name: "quota",
+		Columns: []*Column{
+			{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
+			{Name: "org_id", Type: DB_BigInt, Nullable: false},
+			{Name: "target", Type: DB_NVarchar, Length: 255, Nullable: false},
+			{Name: "limit", Type: DB_BigInt, Nullable: false},
+			{Name: "created", Type: DB_DateTime, Nullable: false},
+			{Name: "updated", Type: DB_DateTime, Nullable: false},
+		},
+		Indices: []*Index{
+			{Cols: []string{"org_id", "target"}, Type: UniqueIndex},
+		},
+	}
+	mg.AddMigration("create quota table v1", NewAddTableMigration(quotaV1))
+
+	//-------  indexes ------------------
+	addTableIndicesMigrations(mg, "v1", quotaV1)
+}

+ 90 - 0
pkg/services/sqlstore/quota.go

@@ -0,0 +1,90 @@
+package sqlstore
+
+import (
+	"fmt"
+	"github.com/grafana/grafana/pkg/bus"
+	m "github.com/grafana/grafana/pkg/models"
+)
+
+func init() {
+	bus.AddHandler("sql", GetQuotaByTarget)
+	bus.AddHandler("sql", GetQuotas)
+	bus.AddHandler("sql", UpdateQuota)
+}
+
+type targetCount struct {
+	Count int64
+}
+
+func GetQuotaByTarget(query *m.GetQuotaByTargetQuery) error {
+	quota := m.Quota{
+		Target: query.Target,
+		OrgId:  query.OrgId,
+	}
+	has, err := x.Get(quota)
+	if err != nil {
+		return err
+	} else if has == false {
+		quota.Limit = m.DefaultQuotas[query.Target]
+	}
+
+	//get quota used.
+	rawSql := fmt.Sprintf("SELECT COUNT(*) as count from %s where org_id=?", string(query.Target))
+	resp := make([]*targetCount, 0)
+	if err := x.Sql(rawSql, query.OrgId).Find(&resp); err != nil {
+		return err
+	}
+
+	query.Result = &m.QuotaDTO{
+		Target: query.Target,
+		Limit:  quota.Limit,
+		OrgId:  query.OrgId,
+		Used:   resp[0].Count,
+	}
+
+	return nil
+}
+
+func GetQuotas(query *m.GetQuotasQuery) error {
+	quotas := make([]*m.Quota, 0)
+	sess := x.Table("quota")
+	if err := sess.Where("org_id=?", query.OrgId).Find(&quotas); err != nil {
+		return err
+	}
+
+	seenTargets := make(map[m.QuotaTarget]bool)
+	for _, q := range quotas {
+		seenTargets[q.Target] = true
+	}
+
+	for t, v := range m.DefaultQuotas {
+		if _, ok := seenTargets[t]; !ok {
+			quotas = append(quotas, &m.Quota{
+				OrgId:  query.OrgId,
+				Target: t,
+				Limit:  v,
+			})
+		}
+	}
+	result := make([]*m.QuotaDTO, len(quotas))
+	for i, q := range quotas {
+		//get quota used.
+		rawSql := fmt.Sprintf("SELECT COUNT(*) as count from %s where org_id=?", string(q.Target))
+		resp := make([]*targetCount, 0)
+		if err := x.Sql(rawSql, q.OrgId).Find(&resp); err != nil {
+			return err
+		}
+		result[i] = &m.QuotaDTO{
+			Target: q.Target,
+			Limit:  q.Limit,
+			OrgId:  q.OrgId,
+			Used:   resp[0].Count,
+		}
+	}
+	query.Result = result
+	return nil
+}
+
+func UpdateQuota(cmd *m.UpdateQuotaCmd) error {
+	return nil
+}