K8s: Improve k8s dashboard query (#88629)

This commit is contained in:
Ryan McKinley
2024-06-05 11:21:51 +03:00
committed by GitHub
parent 7bc7ae2e8c
commit b640b6e8d1
5 changed files with 100 additions and 37 deletions
@@ -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
}
+5 -1
View File
@@ -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")
}
+12 -5
View File
@@ -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)
}