K8s: Improve k8s dashboard query (#88629)
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -84,24 +85,71 @@ const selector = `SELECT
|
||||
LEFT OUTER JOIN user AS UpdatedUSER ON dashboard.created_by = UpdatedUSER.id
|
||||
WHERE is_folder = false`
|
||||
|
||||
// GetDashboards implements DashboardAccess.
|
||||
func (a *dashboardSqlAccess) GetDashboards(ctx context.Context, query *DashboardQuery) (*dashboardsV0.DashboardList, error) {
|
||||
func (a *dashboardSqlAccess) getRows(ctx context.Context, query *DashboardQuery, onlySummary bool) (*rowsWrapper, int, error) {
|
||||
if !query.Labels.Empty() {
|
||||
return nil, 0, fmt.Errorf("label selection not yet supported")
|
||||
}
|
||||
if len(query.Requirements.SortBy) > 0 {
|
||||
return nil, 0, fmt.Errorf("sorting not yet supported")
|
||||
}
|
||||
if query.Requirements.ListHistory != "" {
|
||||
return nil, 0, fmt.Errorf("ListHistory not yet supported")
|
||||
}
|
||||
if query.Requirements.ListDeleted {
|
||||
return nil, 0, fmt.Errorf("ListDeleted not yet supported")
|
||||
}
|
||||
if len(query.Requirements.ListOriginKeys) > 0 {
|
||||
return nil, 0, fmt.Errorf("ListOriginKeys not yet supported")
|
||||
}
|
||||
|
||||
token, err := readContinueToken(query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
limit := query.Limit
|
||||
if limit < 1 {
|
||||
limit = 15 //
|
||||
}
|
||||
args := []any{query.OrgID}
|
||||
|
||||
rows, err := a.doQuery(ctx, selector+`
|
||||
AND dashboard.org_id=$1
|
||||
AND dashboard.id>=$2
|
||||
ORDER BY dashboard.id asc
|
||||
LIMIT $3
|
||||
`, query.OrgID, token.id, (limit + 2))
|
||||
sqlcmd := selector
|
||||
|
||||
// We can not do this yet because title + tags are in the body
|
||||
if onlySummary && false {
|
||||
sqlcmd = strings.Replace(sqlcmd, "dashboard.data", `"{}"`, 1)
|
||||
}
|
||||
|
||||
sqlcmd = fmt.Sprintf("%s AND dashboard.org_id=$%d", sqlcmd, len(args))
|
||||
if query.UID != "" {
|
||||
args = append(args, query.UID)
|
||||
sqlcmd = fmt.Sprintf("%s AND dashboard.uid=$%d", sqlcmd, len(args))
|
||||
} else {
|
||||
args = append(args, token.id)
|
||||
sqlcmd = fmt.Sprintf("%s AND dashboard.id>=$%d", sqlcmd, len(args))
|
||||
}
|
||||
|
||||
if query.Requirements.Folder != nil {
|
||||
args = append(args, *query.Requirements.Folder)
|
||||
sqlcmd = fmt.Sprintf("%s AND dashboard.folder_uid=$%d", sqlcmd, len(args))
|
||||
}
|
||||
|
||||
args = append(args, (limit + 2)) // add more so we can include a next token
|
||||
sqlcmd = fmt.Sprintf("%s ORDER BY dashboard.id asc LIMIT $%d", sqlcmd, len(args))
|
||||
|
||||
rows, err := a.doQuery(ctx, sqlcmd, args...)
|
||||
if err != nil {
|
||||
if rows != nil {
|
||||
_ = rows.Close()
|
||||
}
|
||||
rows = nil
|
||||
}
|
||||
return rows, limit, err
|
||||
}
|
||||
|
||||
// GetDashboards implements DashboardAccess.
|
||||
func (a *dashboardSqlAccess) GetDashboards(ctx context.Context, query *DashboardQuery) (*dashboardsV0.DashboardList, error) {
|
||||
rows, limit, err := a.getRows(ctx, query, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -109,9 +157,6 @@ func (a *dashboardSqlAccess) GetDashboards(ctx context.Context, query *Dashboard
|
||||
|
||||
totalSize := 0
|
||||
list := &dashboardsV0.DashboardList{}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for {
|
||||
row, err := rows.Next()
|
||||
if err != nil || row == nil {
|
||||
@@ -120,7 +165,9 @@ func (a *dashboardSqlAccess) GetDashboards(ctx context.Context, query *Dashboard
|
||||
|
||||
totalSize += row.Bytes
|
||||
if len(list.Items) > 0 && (totalSize > query.MaxBytes || len(list.Items) >= limit) {
|
||||
row.token.folder = query.FolderUID
|
||||
if query.Requirements.Folder != nil {
|
||||
row.token.folder = *query.Requirements.Folder
|
||||
}
|
||||
list.Continue = row.token.String() // will skip this one but start here next time
|
||||
return list, err
|
||||
}
|
||||
@@ -144,20 +191,7 @@ func (a *dashboardSqlAccess) GetDashboard(ctx context.Context, orgId int64, uid
|
||||
|
||||
// GetDashboards implements DashboardAccess.
|
||||
func (a *dashboardSqlAccess) GetDashboardSummaries(ctx context.Context, query *DashboardQuery) (*dashboardsV0.DashboardSummaryList, error) {
|
||||
token, err := readContinueToken(query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
limit := query.Limit
|
||||
if limit < 1 {
|
||||
limit = 15 //
|
||||
}
|
||||
rows, err := a.doQuery(ctx, selector+`
|
||||
AND dashboard.org_id=$1
|
||||
AND dashboard.id>=$2
|
||||
ORDER BY dashboard.id asc
|
||||
LIMIT $3
|
||||
`, query.OrgID, token.id, (limit + 2))
|
||||
rows, limit, err := a.getRows(ctx, query, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -165,9 +199,6 @@ func (a *dashboardSqlAccess) GetDashboardSummaries(ctx context.Context, query *D
|
||||
|
||||
totalSize := 0
|
||||
list := &dashboardsV0.DashboardSummaryList{}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for {
|
||||
row, err := rows.Next()
|
||||
if err != nil || row == nil {
|
||||
@@ -176,7 +207,9 @@ func (a *dashboardSqlAccess) GetDashboardSummaries(ctx context.Context, query *D
|
||||
|
||||
totalSize += row.Bytes
|
||||
if len(list.Items) > 0 && (totalSize > query.MaxBytes || len(list.Items) >= limit) {
|
||||
row.token.folder = query.FolderUID
|
||||
if query.Requirements.Folder != nil {
|
||||
row.token.folder = *query.Requirements.Folder
|
||||
}
|
||||
list.Continue = row.token.String() // will skip this one but start here next time
|
||||
return list, err
|
||||
}
|
||||
|
||||
@@ -50,7 +50,11 @@ func readContinueToken(q *DashboardQuery) (continueToken, error) {
|
||||
token.folder = sub[1]
|
||||
|
||||
// Check if the folder filter is the same from the previous query
|
||||
if token.folder != q.FolderUID {
|
||||
if q.Requirements.Folder == nil {
|
||||
if token.folder != "" {
|
||||
return token, fmt.Errorf("invalid token, the folder must match previous query")
|
||||
}
|
||||
} else if token.folder != *q.Requirements.Folder {
|
||||
return token, fmt.Errorf("invalid token, the folder must match previous query")
|
||||
}
|
||||
|
||||
|
||||
@@ -3,17 +3,24 @@ package access
|
||||
import (
|
||||
"context"
|
||||
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
|
||||
dashboardsV0 "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/storage/entity"
|
||||
)
|
||||
|
||||
// This does not check if you have permissions!
|
||||
|
||||
type DashboardQuery struct {
|
||||
OrgID int64
|
||||
UID string // to select a single dashboard
|
||||
FolderUID string
|
||||
Limit int
|
||||
MaxBytes int
|
||||
OrgID int64
|
||||
UID string // to select a single dashboard
|
||||
Limit int
|
||||
MaxBytes int
|
||||
|
||||
// FolderUID etc
|
||||
Requirements entity.Requirements
|
||||
// Post processing label filter
|
||||
Labels labels.Selector
|
||||
|
||||
// The token from previous query
|
||||
ContinueToken string
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/dashboard/access"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/storage/entity"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -136,12 +137,21 @@ func (s *dashboardStorage) List(ctx context.Context, options *internalversion.Li
|
||||
|
||||
// fmt.Printf("LIST: %s\n", options.Continue)
|
||||
|
||||
// translate grafana.app/* label selectors into field requirements
|
||||
requirements, newSelector, err := entity.ReadLabelSelectors(options.LabelSelector)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
query := &access.DashboardQuery{
|
||||
OrgID: orgId,
|
||||
Limit: int(options.Limit),
|
||||
MaxBytes: 2 * 1024 * 1024, // 2MB,
|
||||
ContinueToken: options.Continue,
|
||||
Requirements: requirements,
|
||||
Labels: newSelector,
|
||||
}
|
||||
|
||||
return s.access.GetDashboards(ctx, query)
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/dashboard/access"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/storage/entity"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -55,11 +56,19 @@ func (s *summaryStorage) List(ctx context.Context, options *internalversion.List
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// translate grafana.app/* label selectors into field requirements
|
||||
requirements, newSelector, err := entity.ReadLabelSelectors(options.LabelSelector)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
query := &access.DashboardQuery{
|
||||
OrgID: orgId,
|
||||
Limit: int(options.Limit),
|
||||
MaxBytes: 2 * 1024 * 1024, // 2MB,
|
||||
ContinueToken: options.Continue,
|
||||
Requirements: requirements,
|
||||
Labels: newSelector,
|
||||
}
|
||||
return s.access.GetDashboardSummaries(ctx, query)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user