mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
K8s/Dashboards: Implement dashboards as StorageBackend (#90295)
This commit is contained in:
parent
9459e29775
commit
f409f8c169
@ -15,6 +15,7 @@ const (
|
||||
NamespaceAnonymous Namespace = "anonymous"
|
||||
NamespaceRenderService Namespace = "render"
|
||||
NamespaceAccessPolicy Namespace = "access-policy"
|
||||
NamespaceProvisioning Namespace = "provisioning"
|
||||
NamespaceEmpty Namespace = ""
|
||||
)
|
||||
|
||||
|
@ -1,35 +0,0 @@
|
||||
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
|
||||
Limit int
|
||||
MaxBytes int
|
||||
|
||||
// FolderUID etc
|
||||
Requirements entity.Requirements
|
||||
// Post processing label filter
|
||||
Labels labels.Selector
|
||||
|
||||
// The token from previous query
|
||||
ContinueToken string
|
||||
}
|
||||
|
||||
type DashboardAccess interface {
|
||||
GetDashboard(ctx context.Context, orgId int64, uid string) (*dashboardsV0.Dashboard, error)
|
||||
GetDashboards(ctx context.Context, query *DashboardQuery) (*dashboardsV0.DashboardList, error)
|
||||
|
||||
SaveDashboard(ctx context.Context, orgId int64, dash *dashboardsV0.Dashboard) (*dashboardsV0.Dashboard, bool, error)
|
||||
DeleteDashboard(ctx context.Context, orgId int64, uid string) (*dashboardsV0.Dashboard, bool, error)
|
||||
}
|
12
pkg/registry/apis/dashboard/legacy/README.md
Normal file
12
pkg/registry/apis/dashboard/legacy/README.md
Normal file
@ -0,0 +1,12 @@
|
||||
This implements a ResourceServer backed by the existing dashboard SQL tables.
|
||||
|
||||
There are a few oddities worth noting. This is not a totally accurate implementation,
|
||||
but it is good enough to drive the UI needs and let kubectl list work!
|
||||
|
||||
1. The resourceVersion is based on internal ID and dashboard version
|
||||
- can get version from the least significant digits
|
||||
- avoids duplicate resourceVersions... but not sequential
|
||||
- the resourceVersion is never set on the list commands
|
||||
|
||||
1. Results are always sorted by internal id ascending
|
||||
- this ensures everything is returned
|
@ -1,27 +1,28 @@
|
||||
package access
|
||||
package legacy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/utils/ptr"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
dashboardsV0 "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/infra/appcontext"
|
||||
"github.com/grafana/grafana/pkg/infra/db"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
|
||||
gapiutil "github.com/grafana/grafana/pkg/services/apiserver/utils"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/provisioning"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore/session"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -29,18 +30,15 @@ var (
|
||||
)
|
||||
|
||||
type dashboardRow struct {
|
||||
// The numeric version for this dashboard
|
||||
RV int64
|
||||
|
||||
// Dashboard resource
|
||||
Dash *dashboardsV0.Dashboard
|
||||
|
||||
// Title -- this may come from saved metadata rather than the body
|
||||
Title string
|
||||
|
||||
// The folder UID (needed for access control checks)
|
||||
FolderUID string
|
||||
|
||||
// Needed for fast summary access
|
||||
Tags []string
|
||||
|
||||
// Size (in bytes) of the dashboard payload
|
||||
Bytes int
|
||||
|
||||
@ -55,9 +53,17 @@ type dashboardSqlAccess struct {
|
||||
namespacer request.NamespaceMapper
|
||||
dashStore dashboards.Store
|
||||
provisioning provisioning.ProvisioningService
|
||||
|
||||
// Typically one... the server wrapper
|
||||
subscribers []chan *resource.WrittenEvent
|
||||
mutex sync.Mutex
|
||||
}
|
||||
|
||||
func NewDashboardAccess(sql db.DB, namespacer request.NamespaceMapper, dashStore dashboards.Store, provisioning provisioning.ProvisioningService) DashboardAccess {
|
||||
func NewDashboardAccess(sql db.DB,
|
||||
namespacer request.NamespaceMapper,
|
||||
dashStore dashboards.Store,
|
||||
provisioning provisioning.ProvisioningService,
|
||||
) DashboardAccess {
|
||||
return &dashboardSqlAccess{
|
||||
sql: sql,
|
||||
sess: sql.GetSqlxSession(),
|
||||
@ -69,72 +75,100 @@ func NewDashboardAccess(sql db.DB, namespacer request.NamespaceMapper, dashStore
|
||||
|
||||
const selector = `SELECT
|
||||
dashboard.org_id, dashboard.id,
|
||||
dashboard.uid,slug,
|
||||
dashboard.folder_uid,
|
||||
dashboard.created,dashboard.created_by,CreatedUSER.login,
|
||||
dashboard.updated,dashboard.updated_by,UpdatedUSER.login,
|
||||
plugin_id,
|
||||
dashboard.uid, dashboard.folder_uid,
|
||||
dashboard.created,CreatedUSER.uid as created_by,
|
||||
dashboard.updated,UpdatedUSER.uid as updated_by,
|
||||
dashboard.deleted, plugin_id,
|
||||
dashboard_provisioning.name as origin_name,
|
||||
dashboard_provisioning.external_id as origin_path,
|
||||
dashboard_provisioning.check_sum as origin_key,
|
||||
dashboard_provisioning.updated as origin_ts,
|
||||
dashboard.version,
|
||||
title,
|
||||
dashboard.data
|
||||
dashboard.version, '', dashboard.data
|
||||
FROM dashboard
|
||||
LEFT OUTER JOIN dashboard_provisioning ON dashboard.id = dashboard_provisioning.dashboard_id
|
||||
LEFT OUTER JOIN user AS CreatedUSER ON dashboard.created_by = CreatedUSER.id
|
||||
LEFT OUTER JOIN user AS UpdatedUSER ON dashboard.created_by = UpdatedUSER.id
|
||||
WHERE is_folder = false`
|
||||
LEFT OUTER JOIN user AS UpdatedUSER ON dashboard.updated_by = UpdatedUSER.id
|
||||
WHERE dashboard.is_folder = false`
|
||||
|
||||
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")
|
||||
const history = `SELECT
|
||||
dashboard.org_id, dashboard.id,
|
||||
dashboard.uid, dashboard.folder_uid,
|
||||
dashboard.created,CreatedUSER.uid as created_by,
|
||||
dashboard_version.created,UpdatedUSER.uid as updated_by,
|
||||
NULL, plugin_id,
|
||||
dashboard_provisioning.name as origin_name,
|
||||
dashboard_provisioning.external_id as origin_path,
|
||||
dashboard_provisioning.check_sum as origin_key,
|
||||
dashboard_provisioning.updated as origin_ts,
|
||||
dashboard_version.version, dashboard_version.message, dashboard_version.data
|
||||
FROM dashboard
|
||||
LEFT OUTER JOIN dashboard_provisioning ON dashboard.id = dashboard_provisioning.dashboard_id
|
||||
LEFT OUTER JOIN dashboard_version ON dashboard.id = dashboard_version.dashboard_id
|
||||
LEFT OUTER JOIN user AS CreatedUSER ON dashboard.created_by = CreatedUSER.id
|
||||
LEFT OUTER JOIN user AS UpdatedUSER ON dashboard_version.created_by = UpdatedUSER.id
|
||||
WHERE dashboard.is_folder = false`
|
||||
|
||||
func (a *dashboardSqlAccess) getRows(ctx context.Context, query *DashboardQuery) (*rowsWrapper, int, error) {
|
||||
if len(query.Labels) > 0 {
|
||||
return nil, 0, fmt.Errorf("labels not yet supported")
|
||||
// if query.Requirements.Folder != nil {
|
||||
// args = append(args, *query.Requirements.Folder)
|
||||
// sqlcmd = fmt.Sprintf("%s AND dashboard.folder_uid=$%d", sqlcmd, len(args))
|
||||
// }
|
||||
}
|
||||
|
||||
token, err := readContinueToken(query)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
var sqlcmd string
|
||||
args := []any{query.OrgID}
|
||||
|
||||
limit := query.Limit
|
||||
if limit < 1 {
|
||||
limit = 15 //
|
||||
}
|
||||
args := []any{query.OrgID}
|
||||
|
||||
sqlcmd := selector
|
||||
if query.GetHistory || query.Version > 0 {
|
||||
if query.GetTrash {
|
||||
return nil, 0, fmt.Errorf("trash not included in history table")
|
||||
}
|
||||
|
||||
// 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\n ", history, len(args))
|
||||
|
||||
if query.UID == "" {
|
||||
return nil, 0, fmt.Errorf("history query must have a UID")
|
||||
}
|
||||
|
||||
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))
|
||||
|
||||
if query.Version > 0 {
|
||||
args = append(args, query.Version)
|
||||
sqlcmd = fmt.Sprintf("%s AND dashboard_version.version=$%d", sqlcmd, len(args))
|
||||
} else if query.LastID > 0 {
|
||||
args = append(args, query.LastID)
|
||||
sqlcmd = fmt.Sprintf("%s AND dashboard_version.version<$%d", sqlcmd, len(args))
|
||||
}
|
||||
|
||||
args = append(args, (limit + 2)) // add more so we can include a next token
|
||||
sqlcmd = fmt.Sprintf("%s\n ORDER BY dashboard_version.version desc LIMIT $%d", sqlcmd, len(args))
|
||||
} else {
|
||||
args = append(args, token.id)
|
||||
sqlcmd = fmt.Sprintf("%s AND dashboard.id>=$%d", sqlcmd, len(args))
|
||||
}
|
||||
sqlcmd = fmt.Sprintf("%s AND dashboard.org_id=$%d\n ", selector, 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))
|
||||
}
|
||||
if query.UID != "" {
|
||||
args = append(args, query.UID)
|
||||
sqlcmd = fmt.Sprintf("%s AND dashboard.uid=$%d", sqlcmd, len(args))
|
||||
} else if query.LastID > 0 {
|
||||
args = append(args, query.LastID)
|
||||
sqlcmd = fmt.Sprintf("%s AND dashboard.id>=$%d", sqlcmd, len(args))
|
||||
}
|
||||
if query.GetTrash {
|
||||
sqlcmd = sqlcmd + " AND dashboard.deleted IS NOT NULL"
|
||||
} else {
|
||||
sqlcmd = sqlcmd + " AND dashboard.deleted IS NULL"
|
||||
}
|
||||
|
||||
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))
|
||||
args = append(args, (limit + 2)) // add more so we can include a next token
|
||||
sqlcmd = fmt.Sprintf("%s\n ORDER BY dashboard.id asc LIMIT $%d", sqlcmd, len(args))
|
||||
}
|
||||
// fmt.Printf("%s // %v\n", sqlcmd, args)
|
||||
|
||||
rows, err := a.doQuery(ctx, sqlcmd, args...)
|
||||
if err != nil {
|
||||
@ -146,51 +180,8 @@ func (a *dashboardSqlAccess) getRows(ctx context.Context, query *DashboardQuery,
|
||||
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
|
||||
}
|
||||
defer func() { _ = rows.Close() }()
|
||||
|
||||
totalSize := 0
|
||||
list := &dashboardsV0.DashboardList{}
|
||||
for {
|
||||
row, err := rows.Next()
|
||||
if err != nil || row == nil {
|
||||
return list, err
|
||||
}
|
||||
|
||||
totalSize += row.Bytes
|
||||
if len(list.Items) > 0 && (totalSize > query.MaxBytes || len(list.Items) >= limit) {
|
||||
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
|
||||
}
|
||||
list.Items = append(list.Items, *row.Dash)
|
||||
}
|
||||
}
|
||||
|
||||
func (a *dashboardSqlAccess) GetDashboard(ctx context.Context, orgId int64, uid string) (*dashboardsV0.Dashboard, error) {
|
||||
r, err := a.GetDashboards(ctx, &DashboardQuery{
|
||||
OrgID: orgId,
|
||||
UID: uid,
|
||||
Labels: labels.Everything(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(r.Items) > 0 {
|
||||
return &r.Items[0], nil
|
||||
}
|
||||
return nil, fmt.Errorf("not found")
|
||||
}
|
||||
|
||||
func (a *dashboardSqlAccess) doQuery(ctx context.Context, query string, args ...any) (*rowsWrapper, error) {
|
||||
user, err := appcontext.User(ctx)
|
||||
_, err := identity.GetRequester(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -199,7 +190,10 @@ func (a *dashboardSqlAccess) doQuery(ctx context.Context, query string, args ...
|
||||
rows: rows,
|
||||
a: a,
|
||||
// This looks up rules from the permissions on a user
|
||||
canReadDashboard: accesscontrol.Checker(user, dashboards.ActionDashboardsRead),
|
||||
canReadDashboard: func(scopes ...string) bool {
|
||||
return true // ???
|
||||
},
|
||||
// accesscontrol.Checker(user, dashboards.ActionDashboardsRead),
|
||||
}, err
|
||||
}
|
||||
|
||||
@ -230,7 +224,6 @@ func (r *rowsWrapper) Next() (*dashboardRow, error) {
|
||||
if !r.canReadDashboard(scopes...) {
|
||||
continue
|
||||
}
|
||||
d.token.size = r.total // size before next!
|
||||
r.total += int64(d.Bytes)
|
||||
}
|
||||
|
||||
@ -243,21 +236,20 @@ func (r *rowsWrapper) Next() (*dashboardRow, error) {
|
||||
func (a *dashboardSqlAccess) scanRow(rows *sql.Rows) (*dashboardRow, error) {
|
||||
dash := &dashboardsV0.Dashboard{
|
||||
TypeMeta: dashboardsV0.DashboardResourceInfo.TypeMeta(),
|
||||
ObjectMeta: v1.ObjectMeta{Annotations: make(map[string]string)},
|
||||
ObjectMeta: metav1.ObjectMeta{Annotations: make(map[string]string)},
|
||||
}
|
||||
row := &dashboardRow{Dash: dash}
|
||||
|
||||
var dashboard_id int64
|
||||
var orgId int64
|
||||
var slug string
|
||||
var folder_uid sql.NullString
|
||||
var updated time.Time
|
||||
var updatedByID int64
|
||||
var updatedByName sql.NullString
|
||||
var updatedBy sql.NullString
|
||||
var deleted sql.NullTime
|
||||
|
||||
var created time.Time
|
||||
var createdByID int64
|
||||
var createdByName sql.NullString
|
||||
var createdBy sql.NullString
|
||||
var message sql.NullString
|
||||
|
||||
var plugin_id string
|
||||
var origin_name sql.NullString
|
||||
@ -267,40 +259,42 @@ func (a *dashboardSqlAccess) scanRow(rows *sql.Rows) (*dashboardRow, error) {
|
||||
var data []byte // the dashboard JSON
|
||||
var version int64
|
||||
|
||||
err := rows.Scan(&orgId, &dashboard_id, &dash.Name,
|
||||
&slug, &folder_uid,
|
||||
&created, &createdByID, &createdByName,
|
||||
&updated, &updatedByID, &updatedByName,
|
||||
&plugin_id,
|
||||
err := rows.Scan(&orgId, &dashboard_id, &dash.Name, &folder_uid,
|
||||
&created, &createdBy,
|
||||
&updated, &updatedBy,
|
||||
&deleted, &plugin_id,
|
||||
&origin_name, &origin_path, &origin_hash, &origin_ts,
|
||||
&version,
|
||||
&row.Title, &data,
|
||||
&version, &message, &data,
|
||||
)
|
||||
|
||||
row.token = &continueToken{orgId: orgId, id: dashboard_id}
|
||||
if err == nil {
|
||||
dash.ResourceVersion = fmt.Sprintf("%d", created.UnixMilli())
|
||||
row.RV = getResourceVersion(dashboard_id, version)
|
||||
dash.ResourceVersion = fmt.Sprintf("%d", row.RV)
|
||||
dash.Namespace = a.namespacer(orgId)
|
||||
dash.UID = gapiutil.CalculateClusterWideUID(dash)
|
||||
dash.SetCreationTimestamp(v1.NewTime(created))
|
||||
dash.SetCreationTimestamp(metav1.NewTime(created))
|
||||
meta, err := utils.MetaAccessor(dash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
meta.SetUpdatedTimestamp(&updated)
|
||||
meta.SetSlug(slug)
|
||||
if createdByID > 0 {
|
||||
meta.SetCreatedBy(fmt.Sprintf("user:%d/%s", createdByID, createdByName.String))
|
||||
meta.SetCreatedBy(getUserID(createdBy))
|
||||
meta.SetUpdatedBy(getUserID(updatedBy))
|
||||
|
||||
if deleted.Valid {
|
||||
meta.SetDeletionTimestamp(ptr.To(metav1.NewTime(deleted.Time)))
|
||||
}
|
||||
if updatedByID > 0 {
|
||||
meta.SetUpdatedBy(fmt.Sprintf("user:%d/%s", updatedByID, updatedByName.String))
|
||||
|
||||
if message.String != "" {
|
||||
meta.SetMessage(message.String)
|
||||
}
|
||||
if folder_uid.Valid {
|
||||
if folder_uid.String != "" {
|
||||
meta.SetFolder(folder_uid.String)
|
||||
row.FolderUID = folder_uid.String
|
||||
}
|
||||
|
||||
if origin_name.Valid {
|
||||
if origin_name.String != "" {
|
||||
ts := time.Unix(origin_ts.Int64, 0)
|
||||
|
||||
resolvedPath := a.provisioning.GetDashboardProvisionerResolvedPath(origin_name.String)
|
||||
@ -332,16 +326,21 @@ func (a *dashboardSqlAccess) scanRow(rows *sql.Rows) (*dashboardRow, error) {
|
||||
return row, err
|
||||
}
|
||||
dash.Spec.Set("id", dashboard_id) // add it so we can get it from the body later
|
||||
row.Title = dash.Spec.GetNestedString("title")
|
||||
row.Tags = dash.Spec.GetNestedStringSlice("tags")
|
||||
}
|
||||
}
|
||||
return row, err
|
||||
}
|
||||
|
||||
func getUserID(v sql.NullString) string {
|
||||
if v.String == "" {
|
||||
return identity.NewNamespaceIDString(identity.NamespaceProvisioning, "").String()
|
||||
}
|
||||
return identity.NewNamespaceIDString(identity.NamespaceUser, v.String).String()
|
||||
}
|
||||
|
||||
// DeleteDashboard implements DashboardAccess.
|
||||
func (a *dashboardSqlAccess) DeleteDashboard(ctx context.Context, orgId int64, uid string) (*dashboardsV0.Dashboard, bool, error) {
|
||||
dash, err := a.GetDashboard(ctx, orgId, uid)
|
||||
dash, _, err := a.GetDashboard(ctx, orgId, uid, 0)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
@ -404,6 +403,6 @@ func (a *dashboardSqlAccess) SaveDashboard(ctx context.Context, orgId int64, das
|
||||
if out != nil {
|
||||
created = (out.Created.Unix() == out.Updated.Unix()) // and now?
|
||||
}
|
||||
dash, err = a.GetDashboard(ctx, orgId, out.UID)
|
||||
dash, _, err = a.GetDashboard(ctx, orgId, out.UID, 0)
|
||||
return dash, created, err
|
||||
}
|
329
pkg/registry/apis/dashboard/legacy/storage.go
Normal file
329
pkg/registry/apis/dashboard/legacy/storage.go
Normal file
@ -0,0 +1,329 @@
|
||||
package legacy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
dashboard "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
)
|
||||
|
||||
func getDashboardFromEvent(event resource.WriteEvent) (*dashboard.Dashboard, error) {
|
||||
obj, ok := event.Object.GetRuntimeObject()
|
||||
if ok && obj != nil {
|
||||
dash, ok := obj.(*dashboard.Dashboard)
|
||||
if ok {
|
||||
return dash, nil
|
||||
}
|
||||
}
|
||||
dash := &dashboard.Dashboard{}
|
||||
err := json.Unmarshal(event.Value, dash)
|
||||
return dash, err
|
||||
}
|
||||
|
||||
func isDashboardKey(key *resource.ResourceKey, requireName bool) error {
|
||||
gr := dashboard.DashboardResourceInfo.GroupResource()
|
||||
if key.Group != gr.Group {
|
||||
return fmt.Errorf("expecting dashboard group (%s != %s)", key.Group, gr.Group)
|
||||
}
|
||||
if key.Resource != gr.Resource {
|
||||
return fmt.Errorf("expecting dashboard resource (%s != %s)", key.Resource, gr.Resource)
|
||||
}
|
||||
if requireName && key.Name == "" {
|
||||
return fmt.Errorf("expecting dashboard name (uid)")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *dashboardSqlAccess) WriteEvent(ctx context.Context, event resource.WriteEvent) (rv int64, err error) {
|
||||
info, err := request.ParseNamespace(event.Key.Namespace)
|
||||
if err == nil {
|
||||
err = isDashboardKey(event.Key, true)
|
||||
}
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
switch event.Type {
|
||||
case resource.WatchEvent_DELETED:
|
||||
{
|
||||
_, _, err = a.DeleteDashboard(ctx, info.OrgID, event.Key.Name)
|
||||
//rv = ???
|
||||
}
|
||||
// The difference depends on embedded internal ID
|
||||
case resource.WatchEvent_ADDED, resource.WatchEvent_MODIFIED:
|
||||
{
|
||||
dash, err := getDashboardFromEvent(event)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
after, _, err := a.SaveDashboard(ctx, info.OrgID, dash)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if after != nil {
|
||||
meta, err := utils.MetaAccessor(after)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
rv, err = meta.GetResourceVersionInt64()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
return 0, fmt.Errorf("unsupported event type: %v", event.Type)
|
||||
}
|
||||
|
||||
// Async notify all subscribers (not HA!!!)
|
||||
if a.subscribers != nil {
|
||||
go func() {
|
||||
write := &resource.WrittenEvent{
|
||||
WriteEvent: event,
|
||||
|
||||
Timestamp: time.Now().UnixMilli(),
|
||||
ResourceVersion: rv,
|
||||
}
|
||||
for _, sub := range a.subscribers {
|
||||
sub <- write
|
||||
}
|
||||
}()
|
||||
}
|
||||
return rv, err
|
||||
}
|
||||
|
||||
// Read implements ResourceStoreServer.
|
||||
func (a *dashboardSqlAccess) GetDashboard(ctx context.Context, orgId int64, uid string, v int64) (*dashboard.Dashboard, int64, error) {
|
||||
rows, _, err := a.getRows(ctx, &DashboardQuery{
|
||||
OrgID: orgId,
|
||||
UID: uid,
|
||||
Limit: 2, // will only be one!
|
||||
Version: v,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
defer func() { _ = rows.Close() }()
|
||||
|
||||
row, err := rows.Next()
|
||||
if err != nil || row == nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
return row.Dash, row.RV, nil
|
||||
}
|
||||
|
||||
// Read implements ResourceStoreServer.
|
||||
func (a *dashboardSqlAccess) Read(ctx context.Context, req *resource.ReadRequest) (*resource.ReadResponse, error) {
|
||||
info, err := request.ParseNamespace(req.Key.Namespace)
|
||||
if err == nil {
|
||||
err = isDashboardKey(req.Key, true)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
version := int64(0)
|
||||
if req.ResourceVersion > 0 {
|
||||
version = getVersionFromRV(req.ResourceVersion)
|
||||
}
|
||||
|
||||
dash, rv, err := a.GetDashboard(ctx, info.OrgID, req.Key.Name, version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if dash == nil {
|
||||
return &resource.ReadResponse{
|
||||
Error: &resource.ErrorResult{
|
||||
Code: http.StatusNotFound,
|
||||
},
|
||||
}, err
|
||||
}
|
||||
|
||||
value, err := json.Marshal(dash)
|
||||
return &resource.ReadResponse{
|
||||
ResourceVersion: rv,
|
||||
Value: value,
|
||||
}, err
|
||||
}
|
||||
|
||||
// List implements AppendingStore.
|
||||
func (a *dashboardSqlAccess) PrepareList(ctx context.Context, req *resource.ListRequest) (*resource.ListResponse, error) {
|
||||
opts := req.Options
|
||||
info, err := request.ParseNamespace(opts.Key.Namespace)
|
||||
if err == nil {
|
||||
err = isDashboardKey(opts.Key, false)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
token, err := readContinueToken(req.NextPageToken)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if token.orgId > 0 && token.orgId != info.OrgID {
|
||||
return nil, fmt.Errorf("token and orgID mismatch")
|
||||
}
|
||||
|
||||
query := &DashboardQuery{
|
||||
OrgID: info.OrgID,
|
||||
Limit: int(req.Limit),
|
||||
MaxBytes: 2 * 1024 * 1024, // 2MB,
|
||||
LastID: token.id,
|
||||
Labels: req.Options.Labels,
|
||||
}
|
||||
|
||||
rows, limit, err := a.getRows(ctx, query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() { _ = rows.Close() }()
|
||||
|
||||
totalSize := 0
|
||||
list := &resource.ListResponse{}
|
||||
for {
|
||||
row, err := rows.Next()
|
||||
if err != nil || row == nil {
|
||||
return list, err
|
||||
}
|
||||
|
||||
totalSize += row.Bytes
|
||||
if len(list.Items) > 0 && (totalSize > query.MaxBytes || len(list.Items) >= limit) {
|
||||
// if query.Requirements.Folder != nil {
|
||||
// row.token.folder = *query.Requirements.Folder
|
||||
// }
|
||||
list.NextPageToken = row.token.String() // will skip this one but start here next time
|
||||
return list, err
|
||||
}
|
||||
// TODO -- make it smaller and stick the body as an annotation...
|
||||
val, err := json.Marshal(row.Dash)
|
||||
if err != nil {
|
||||
return list, err
|
||||
}
|
||||
list.Items = append(list.Items, &resource.ResourceWrapper{
|
||||
ResourceVersion: row.RV,
|
||||
Value: val,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Watch implements AppendingStore.
|
||||
func (a *dashboardSqlAccess) WatchWriteEvents(ctx context.Context) (<-chan *resource.WrittenEvent, error) {
|
||||
stream := make(chan *resource.WrittenEvent, 10)
|
||||
{
|
||||
a.mutex.Lock()
|
||||
defer a.mutex.Unlock()
|
||||
|
||||
// Add the event stream
|
||||
a.subscribers = append(a.subscribers, stream)
|
||||
}
|
||||
|
||||
// Wait for context done
|
||||
go func() {
|
||||
// Wait till the context is done
|
||||
<-ctx.Done()
|
||||
|
||||
// Then remove the subscription
|
||||
a.mutex.Lock()
|
||||
defer a.mutex.Unlock()
|
||||
|
||||
// Copy all streams without our listener
|
||||
subs := []chan *resource.WrittenEvent{}
|
||||
for _, sub := range a.subscribers {
|
||||
if sub != stream {
|
||||
subs = append(subs, sub)
|
||||
}
|
||||
}
|
||||
a.subscribers = subs
|
||||
}()
|
||||
return stream, nil
|
||||
}
|
||||
|
||||
func (a *dashboardSqlAccess) History(ctx context.Context, req *resource.HistoryRequest) (*resource.HistoryResponse, error) {
|
||||
info, err := request.ParseNamespace(req.Key.Namespace)
|
||||
if err == nil {
|
||||
err = isDashboardKey(req.Key, false)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
token, err := readContinueToken(req.NextPageToken)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if token.orgId > 0 && token.orgId != info.OrgID {
|
||||
return nil, fmt.Errorf("token and orgID mismatch")
|
||||
}
|
||||
|
||||
query := &DashboardQuery{
|
||||
OrgID: info.OrgID,
|
||||
Limit: int(req.Limit),
|
||||
MaxBytes: 2 * 1024 * 1024, // 2MB,
|
||||
LastID: token.id,
|
||||
UID: req.Key.Name,
|
||||
}
|
||||
if req.ShowDeleted {
|
||||
query.GetTrash = true
|
||||
} else {
|
||||
query.GetHistory = true
|
||||
}
|
||||
|
||||
rows, limit, err := a.getRows(ctx, query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() { _ = rows.Close() }()
|
||||
|
||||
totalSize := 0
|
||||
list := &resource.HistoryResponse{}
|
||||
for {
|
||||
row, err := rows.Next()
|
||||
if err != nil || row == nil {
|
||||
return list, err
|
||||
}
|
||||
|
||||
totalSize += row.Bytes
|
||||
if len(list.Items) > 0 && (totalSize > query.MaxBytes || len(list.Items) >= limit) {
|
||||
// if query.Requirements.Folder != nil {
|
||||
// row.token.folder = *query.Requirements.Folder
|
||||
// }
|
||||
row.token.id = getVersionFromRV(row.RV) // Use the version as the increment
|
||||
list.NextPageToken = row.token.String() // will skip this one but start here next time
|
||||
return list, err
|
||||
}
|
||||
|
||||
partial := &metav1.PartialObjectMetadata{
|
||||
ObjectMeta: row.Dash.ObjectMeta,
|
||||
}
|
||||
partial.UID = "" // it is not useful/helpful/accurate and just confusing now
|
||||
|
||||
val, err := json.Marshal(partial)
|
||||
if err != nil {
|
||||
return list, err
|
||||
}
|
||||
full, err := json.Marshal(row.Dash.Spec)
|
||||
if err != nil {
|
||||
return list, err
|
||||
}
|
||||
list.Items = append(list.Items, &resource.ResourceMeta{
|
||||
ResourceVersion: row.RV,
|
||||
PartialObjectMeta: val,
|
||||
Size: int32(len(full)),
|
||||
Hash: "??", // hash the full?
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Used for efficient provisioning
|
||||
func (a *dashboardSqlAccess) Origin(context.Context, *resource.OriginRequest) (*resource.OriginResponse, error) {
|
||||
return nil, fmt.Errorf("not yet (origin)")
|
||||
}
|
@ -1,27 +1,24 @@
|
||||
package access
|
||||
package legacy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
||||
type continueToken struct {
|
||||
orgId int64
|
||||
id int64 // the internal id (sort by!)
|
||||
folder string // from the query
|
||||
size int64
|
||||
}
|
||||
|
||||
func readContinueToken(q *DashboardQuery) (continueToken, error) {
|
||||
func readContinueToken(next string) (continueToken, error) {
|
||||
var err error
|
||||
token := continueToken{}
|
||||
if q.ContinueToken == "" {
|
||||
if next == "" {
|
||||
return token, nil
|
||||
}
|
||||
parts := strings.Split(q.ContinueToken, "/")
|
||||
parts := strings.Split(next, "/")
|
||||
if len(parts) < 3 {
|
||||
return token, fmt.Errorf("invalid continue token (too few parts)")
|
||||
}
|
||||
@ -49,19 +46,19 @@ func readContinueToken(q *DashboardQuery) (continueToken, error) {
|
||||
}
|
||||
token.folder = sub[1]
|
||||
|
||||
// Check if the folder filter is the same from the previous query
|
||||
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")
|
||||
}
|
||||
// // Check if the folder filter is the same from the previous query
|
||||
// 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")
|
||||
// }
|
||||
|
||||
return token, err
|
||||
}
|
||||
|
||||
func (r *continueToken) String() string {
|
||||
return fmt.Sprintf("org:%d/start:%d/folder:%s/%s",
|
||||
r.orgId, r.id, r.folder, util.ByteCountSI(r.size))
|
||||
return fmt.Sprintf("org:%d/start:%d/folder:%s",
|
||||
r.orgId, r.id, r.folder)
|
||||
}
|
40
pkg/registry/apis/dashboard/legacy/types.go
Normal file
40
pkg/registry/apis/dashboard/legacy/types.go
Normal file
@ -0,0 +1,40 @@
|
||||
package legacy
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
dashboardsV0 "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
)
|
||||
|
||||
// This does not check if you have permissions!
|
||||
|
||||
type DashboardQuery struct {
|
||||
OrgID int64
|
||||
UID string // to select a single dashboard
|
||||
Limit int
|
||||
MaxBytes int
|
||||
|
||||
// Included in the continue token
|
||||
// This is the ID from the last dashboard sent in the previous page
|
||||
LastID int64
|
||||
|
||||
// List dashboards with a deletion timestamp
|
||||
GetTrash bool
|
||||
|
||||
// Get dashboards from the history table
|
||||
GetHistory bool
|
||||
Version int64
|
||||
|
||||
// The label requirements
|
||||
Labels []*resource.Requirement
|
||||
}
|
||||
|
||||
type DashboardAccess interface {
|
||||
resource.StorageBackend
|
||||
resource.ResourceIndexServer
|
||||
|
||||
GetDashboard(ctx context.Context, orgId int64, uid string, version int64) (*dashboardsV0.Dashboard, int64, error)
|
||||
SaveDashboard(ctx context.Context, orgId int64, dash *dashboardsV0.Dashboard) (*dashboardsV0.Dashboard, bool, error)
|
||||
DeleteDashboard(ctx context.Context, orgId int64, uid string) (*dashboardsV0.Dashboard, bool, error)
|
||||
}
|
9
pkg/registry/apis/dashboard/legacy/utils.go
Normal file
9
pkg/registry/apis/dashboard/legacy/utils.go
Normal file
@ -0,0 +1,9 @@
|
||||
package legacy
|
||||
|
||||
func getResourceVersion(id int64, version int64) int64 {
|
||||
return version + (id * 10000000)
|
||||
}
|
||||
|
||||
func getVersionFromRV(rv int64) int64 {
|
||||
return rv % 10000000
|
||||
}
|
13
pkg/registry/apis/dashboard/legacy/utils_test.go
Normal file
13
pkg/registry/apis/dashboard/legacy/utils_test.go
Normal file
@ -0,0 +1,13 @@
|
||||
package legacy
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestVersionHacks(t *testing.T) {
|
||||
rv := getResourceVersion(123, 456)
|
||||
require.Equal(t, int64(1230000456), rv)
|
||||
require.Equal(t, int64(456), getVersionFromRV(rv))
|
||||
}
|
@ -1,170 +1,70 @@
|
||||
package dashboard
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"k8s.io/apimachinery/pkg/apis/meta/internalversion"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apiserver/pkg/registry/generic"
|
||||
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
|
||||
common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
|
||||
"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 (
|
||||
_ rest.Storage = (*dashboardStorage)(nil)
|
||||
_ rest.Scoper = (*dashboardStorage)(nil)
|
||||
_ rest.SingularNameProvider = (*dashboardStorage)(nil)
|
||||
_ rest.Getter = (*dashboardStorage)(nil)
|
||||
_ rest.Lister = (*dashboardStorage)(nil)
|
||||
_ rest.Creater = (*dashboardStorage)(nil)
|
||||
_ rest.Updater = (*dashboardStorage)(nil)
|
||||
_ rest.GracefulDeleter = (*dashboardStorage)(nil)
|
||||
grafanaregistry "github.com/grafana/grafana/pkg/apiserver/registry/generic"
|
||||
grafanarest "github.com/grafana/grafana/pkg/apiserver/rest"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/dashboard/legacy"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/apistore"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
)
|
||||
|
||||
type dashboardStorage struct {
|
||||
resource common.ResourceInfo
|
||||
access access.DashboardAccess
|
||||
access legacy.DashboardAccess
|
||||
tableConverter rest.TableConvertor
|
||||
|
||||
server resource.ResourceServer
|
||||
}
|
||||
|
||||
func (s *dashboardStorage) New() runtime.Object {
|
||||
return s.resource.NewFunc()
|
||||
}
|
||||
|
||||
func (s *dashboardStorage) Destroy() {}
|
||||
|
||||
func (s *dashboardStorage) NamespaceScoped() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *dashboardStorage) GetSingularName() string {
|
||||
return s.resource.GetSingularName()
|
||||
}
|
||||
|
||||
func (s *dashboardStorage) NewList() runtime.Object {
|
||||
return s.resource.NewListFunc()
|
||||
}
|
||||
|
||||
func (s *dashboardStorage) ConvertToTable(ctx context.Context, object runtime.Object, tableOptions runtime.Object) (*metav1.Table, error) {
|
||||
return s.tableConverter.ConvertToTable(ctx, object, tableOptions)
|
||||
}
|
||||
|
||||
func (s *dashboardStorage) Create(ctx context.Context,
|
||||
obj runtime.Object,
|
||||
createValidation rest.ValidateObjectFunc,
|
||||
options *metav1.CreateOptions,
|
||||
) (runtime.Object, error) {
|
||||
info, err := request.NamespaceInfoFrom(ctx, true)
|
||||
func (s *dashboardStorage) newStore(scheme *runtime.Scheme, defaultOptsGetter generic.RESTOptionsGetter) (grafanarest.LegacyStorage, error) {
|
||||
server, err := resource.NewResourceServer(resource.ResourceServerOptions{
|
||||
Backend: s.access,
|
||||
Index: s.access,
|
||||
// WriteAccess: resource.WriteAccessHooks{
|
||||
// Folder: func(ctx context.Context, user identity.Requester, uid string) bool {
|
||||
// // ???
|
||||
// },
|
||||
// },
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.server = server
|
||||
|
||||
p, ok := obj.(*v0alpha1.Dashboard)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expected dashboard?")
|
||||
}
|
||||
|
||||
// HACK to simplify unique name testing from kubectl
|
||||
t := p.Spec.GetNestedString("title")
|
||||
if strings.Contains(t, "${NOW}") {
|
||||
t = strings.ReplaceAll(t, "${NOW}", fmt.Sprintf("%d", time.Now().Unix()))
|
||||
p.Spec.Set("title", t)
|
||||
}
|
||||
|
||||
dash, _, err := s.access.SaveDashboard(ctx, info.OrgID, p)
|
||||
return dash, err
|
||||
}
|
||||
|
||||
func (s *dashboardStorage) Update(ctx context.Context,
|
||||
name string,
|
||||
objInfo rest.UpdatedObjectInfo,
|
||||
createValidation rest.ValidateObjectFunc,
|
||||
updateValidation rest.ValidateObjectUpdateFunc,
|
||||
forceAllowCreate bool,
|
||||
options *metav1.UpdateOptions,
|
||||
) (runtime.Object, bool, error) {
|
||||
info, err := request.NamespaceInfoFrom(ctx, true)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
created := false
|
||||
old, err := s.Get(ctx, name, nil)
|
||||
if err != nil {
|
||||
return old, created, err
|
||||
}
|
||||
|
||||
obj, err := objInfo.UpdatedObject(ctx, old)
|
||||
if err != nil {
|
||||
return old, created, err
|
||||
}
|
||||
p, ok := obj.(*v0alpha1.Dashboard)
|
||||
if !ok {
|
||||
return nil, created, fmt.Errorf("expected dashboard after update")
|
||||
}
|
||||
|
||||
_, created, err = s.access.SaveDashboard(ctx, info.OrgID, p)
|
||||
if err == nil {
|
||||
r, err := s.Get(ctx, name, nil)
|
||||
return r, created, err
|
||||
}
|
||||
return nil, created, err
|
||||
}
|
||||
|
||||
// GracefulDeleter
|
||||
func (s *dashboardStorage) Delete(ctx context.Context, name string, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions) (runtime.Object, bool, error) {
|
||||
info, err := request.NamespaceInfoFrom(ctx, true)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
return s.access.DeleteDashboard(ctx, info.OrgID, name)
|
||||
}
|
||||
|
||||
func (s *dashboardStorage) List(ctx context.Context, options *internalversion.ListOptions) (runtime.Object, error) {
|
||||
orgId, err := request.OrgIDForList(ctx)
|
||||
resourceInfo := s.resource
|
||||
defaultOpts, err := defaultOptsGetter.GetRESTOptions(resourceInfo.GroupResource())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client := resource.NewLocalResourceStoreClient(server)
|
||||
optsGetter := apistore.NewRESTOptionsGetter(client,
|
||||
defaultOpts.StorageConfig.Codec,
|
||||
)
|
||||
|
||||
// fmt.Printf("LIST: %s\n", options.Continue)
|
||||
strategy := grafanaregistry.NewStrategy(scheme)
|
||||
store := &genericregistry.Store{
|
||||
NewFunc: resourceInfo.NewFunc,
|
||||
NewListFunc: resourceInfo.NewListFunc,
|
||||
KeyRootFunc: grafanaregistry.KeyRootFunc(resourceInfo.GroupResource()),
|
||||
KeyFunc: grafanaregistry.NamespaceKeyFunc(resourceInfo.GroupResource()),
|
||||
PredicateFunc: grafanaregistry.Matcher,
|
||||
DefaultQualifiedResource: resourceInfo.GroupResource(),
|
||||
SingularQualifiedResource: resourceInfo.SingularGroupResource(),
|
||||
CreateStrategy: strategy,
|
||||
UpdateStrategy: strategy,
|
||||
DeleteStrategy: strategy,
|
||||
TableConvertor: s.tableConverter,
|
||||
}
|
||||
|
||||
// translate grafana.app/* label selectors into field requirements
|
||||
requirements, newSelector, err := entity.ReadLabelSelectors(options.LabelSelector)
|
||||
if err != nil {
|
||||
options := &generic.StoreOptions{RESTOptions: optsGetter}
|
||||
if err := store.CompleteWithOptions(options); 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)
|
||||
}
|
||||
|
||||
func (s *dashboardStorage) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
|
||||
info, err := request.NamespaceInfoFrom(ctx, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s.access.GetDashboard(ctx, info.OrgID, name)
|
||||
}
|
||||
|
||||
// GracefulDeleter
|
||||
func (s *dashboardStorage) DeleteCollection(ctx context.Context, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions, listOptions *internalversion.ListOptions) (runtime.Object, error) {
|
||||
return nil, fmt.Errorf("DeleteCollection for dashboards not implemented")
|
||||
return store, err
|
||||
}
|
||||
|
@ -1,6 +1,10 @@
|
||||
package dashboard
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
@ -11,22 +15,22 @@ import (
|
||||
common "k8s.io/kube-openapi/pkg/common"
|
||||
"k8s.io/kube-openapi/pkg/spec3"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1"
|
||||
dashboard "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1"
|
||||
grafanaregistry "github.com/grafana/grafana/pkg/apiserver/registry/generic"
|
||||
grafanarest "github.com/grafana/grafana/pkg/apiserver/rest"
|
||||
"github.com/grafana/grafana/pkg/infra/db"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/dashboard/access"
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/dashboard/legacy"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/builder"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
|
||||
gapiutil "github.com/grafana/grafana/pkg/services/apiserver/utils"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
dashver "github.com/grafana/grafana/pkg/services/dashboardversion"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/provisioning"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/apistore"
|
||||
)
|
||||
|
||||
var _ builder.APIGroupBuilder = (*DashboardsAPIBuilder)(nil)
|
||||
@ -35,11 +39,8 @@ var _ builder.APIGroupBuilder = (*DashboardsAPIBuilder)(nil)
|
||||
type DashboardsAPIBuilder struct {
|
||||
dashboardService dashboards.DashboardService
|
||||
|
||||
dashboardVersionService dashver.Service
|
||||
accessControl accesscontrol.AccessControl
|
||||
namespacer request.NamespaceMapper
|
||||
access access.DashboardAccess
|
||||
dashStore dashboards.Store
|
||||
accessControl accesscontrol.AccessControl
|
||||
legacy *dashboardStorage
|
||||
|
||||
log log.Logger
|
||||
}
|
||||
@ -47,12 +48,12 @@ type DashboardsAPIBuilder struct {
|
||||
func RegisterAPIService(cfg *setting.Cfg, features featuremgmt.FeatureToggles,
|
||||
apiregistration builder.APIRegistrar,
|
||||
dashboardService dashboards.DashboardService,
|
||||
dashboardVersionService dashver.Service,
|
||||
accessControl accesscontrol.AccessControl,
|
||||
provisioning provisioning.ProvisioningService,
|
||||
dashStore dashboards.Store,
|
||||
reg prometheus.Registerer,
|
||||
sql db.DB,
|
||||
tracing *tracing.TracingService,
|
||||
) *DashboardsAPIBuilder {
|
||||
if !features.IsEnabledGlobally(featuremgmt.FlagGrafanaAPIServerWithExperimentalAPIs) {
|
||||
return nil // skip registration unless opting into experimental apis
|
||||
@ -60,34 +61,63 @@ func RegisterAPIService(cfg *setting.Cfg, features featuremgmt.FeatureToggles,
|
||||
|
||||
namespacer := request.GetNamespaceMapper(cfg)
|
||||
builder := &DashboardsAPIBuilder{
|
||||
dashboardService: dashboardService,
|
||||
dashboardVersionService: dashboardVersionService,
|
||||
dashStore: dashStore,
|
||||
accessControl: accessControl,
|
||||
namespacer: namespacer,
|
||||
access: access.NewDashboardAccess(sql, namespacer, dashStore, provisioning),
|
||||
log: log.New("grafana-apiserver.dashboards"),
|
||||
log: log.New("grafana-apiserver.dashboards"),
|
||||
|
||||
dashboardService: dashboardService,
|
||||
accessControl: accessControl,
|
||||
|
||||
legacy: &dashboardStorage{
|
||||
resource: dashboard.DashboardResourceInfo,
|
||||
access: legacy.NewDashboardAccess(sql, namespacer, dashStore, provisioning),
|
||||
tableConverter: gapiutil.NewTableConverter(
|
||||
dashboard.DashboardResourceInfo.GroupResource(),
|
||||
[]metav1.TableColumnDefinition{
|
||||
{Name: "Name", Type: "string", Format: "name"},
|
||||
{Name: "Title", Type: "string", Format: "string", Description: "The dashboard name"},
|
||||
{Name: "Created At", Type: "date"},
|
||||
},
|
||||
func(obj any) ([]interface{}, error) {
|
||||
dash, ok := obj.(*dashboard.Dashboard)
|
||||
if ok {
|
||||
if dash != nil {
|
||||
return []interface{}{
|
||||
dash.Name,
|
||||
dash.Spec.GetNestedString("title"),
|
||||
dash.CreationTimestamp.UTC().Format(time.RFC3339),
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("expected dashboard or summary")
|
||||
}),
|
||||
},
|
||||
}
|
||||
apiregistration.RegisterAPI(builder)
|
||||
return builder
|
||||
}
|
||||
|
||||
func (b *DashboardsAPIBuilder) GetGroupVersion() schema.GroupVersion {
|
||||
return v0alpha1.DashboardResourceInfo.GroupVersion()
|
||||
return dashboard.DashboardResourceInfo.GroupVersion()
|
||||
}
|
||||
|
||||
func (b *DashboardsAPIBuilder) GetDesiredDualWriterMode(dualWrite bool, modeMap map[string]grafanarest.DualWriterMode) grafanarest.DualWriterMode {
|
||||
// Add required configuration support in order to enable other modes. For an example, see pkg/registry/apis/playlist/register.go
|
||||
return grafanarest.Mode0
|
||||
}
|
||||
|
||||
func addKnownTypes(scheme *runtime.Scheme, gv schema.GroupVersion) {
|
||||
scheme.AddKnownTypes(gv,
|
||||
&v0alpha1.Dashboard{},
|
||||
&v0alpha1.DashboardList{},
|
||||
&v0alpha1.DashboardWithAccessInfo{},
|
||||
&v0alpha1.DashboardVersionList{},
|
||||
&v0alpha1.VersionsQueryOptions{},
|
||||
&dashboard.Dashboard{},
|
||||
&dashboard.DashboardList{},
|
||||
&dashboard.DashboardWithAccessInfo{},
|
||||
&dashboard.DashboardVersionList{},
|
||||
&dashboard.VersionsQueryOptions{},
|
||||
&metav1.PartialObjectMetadata{},
|
||||
&metav1.PartialObjectMetadataList{},
|
||||
)
|
||||
}
|
||||
|
||||
func (b *DashboardsAPIBuilder) InstallSchema(scheme *runtime.Scheme) error {
|
||||
resourceInfo := v0alpha1.DashboardResourceInfo
|
||||
resourceInfo := dashboard.DashboardResourceInfo
|
||||
addKnownTypes(scheme, resourceInfo.GroupVersion())
|
||||
|
||||
// Link this version to the internal representation.
|
||||
@ -112,47 +142,47 @@ func (b *DashboardsAPIBuilder) GetAPIGroupInfo(
|
||||
optsGetter generic.RESTOptionsGetter,
|
||||
dualWriteBuilder grafanarest.DualWriteBuilder,
|
||||
) (*genericapiserver.APIGroupInfo, error) {
|
||||
apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(v0alpha1.GROUP, scheme, metav1.ParameterCodec, codecs)
|
||||
apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(dashboard.GROUP, scheme, metav1.ParameterCodec, codecs)
|
||||
|
||||
resourceInfo := v0alpha1.DashboardResourceInfo
|
||||
store, err := newStorage(scheme)
|
||||
dash := b.legacy.resource
|
||||
legacyStore, err := b.legacy.newStore(scheme, optsGetter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
legacyStore := &dashboardStorage{
|
||||
resource: resourceInfo,
|
||||
access: b.access,
|
||||
tableConverter: store.TableConvertor,
|
||||
}
|
||||
|
||||
storage := map[string]rest.Storage{}
|
||||
storage[resourceInfo.StoragePath()] = legacyStore
|
||||
storage[resourceInfo.StoragePath("dto")] = &DTOConnector{
|
||||
builder: b,
|
||||
}
|
||||
storage[resourceInfo.StoragePath("versions")] = &VersionsREST{
|
||||
storage[dash.StoragePath()] = legacyStore
|
||||
storage[dash.StoragePath("dto")] = &DTOConnector{
|
||||
builder: b,
|
||||
}
|
||||
storage[dash.StoragePath("history")] = apistore.NewHistoryConnector(
|
||||
b.legacy.server, // as client???
|
||||
dashboard.DashboardResourceInfo.GroupResource(),
|
||||
)
|
||||
|
||||
// Dual writes if a RESTOptionsGetter is provided
|
||||
if optsGetter != nil && dualWriteBuilder != nil {
|
||||
store, err := newStorage(scheme)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: grafanaregistry.GetAttrs}
|
||||
if err := store.CompleteWithOptions(options); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
storage[resourceInfo.StoragePath()], err = dualWriteBuilder(resourceInfo.GroupResource(), legacyStore, store)
|
||||
storage[dash.StoragePath()], err = dualWriteBuilder(dash.GroupResource(), legacyStore, store)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
apiGroupInfo.VersionedResourcesStorageMap[v0alpha1.VERSION] = storage
|
||||
apiGroupInfo.VersionedResourcesStorageMap[dashboard.VERSION] = storage
|
||||
return &apiGroupInfo, nil
|
||||
}
|
||||
|
||||
func (b *DashboardsAPIBuilder) GetOpenAPIDefinitions() common.GetOpenAPIDefinitions {
|
||||
return v0alpha1.GetOpenAPIDefinitions
|
||||
return dashboard.GetOpenAPIDefinitions
|
||||
}
|
||||
|
||||
func (b *DashboardsAPIBuilder) PostProcessOpenAPI(oas *spec3.OpenAPI) (*spec3.OpenAPI, error) {
|
||||
@ -163,8 +193,8 @@ func (b *DashboardsAPIBuilder) PostProcessOpenAPI(oas *spec3.OpenAPI) (*spec3.Op
|
||||
root := "/apis/" + b.GetGroupVersion().String() + "/"
|
||||
|
||||
// Hide the ability to list or watch across all tenants
|
||||
delete(oas.Paths.Paths, root+v0alpha1.DashboardResourceInfo.GroupResource().Resource)
|
||||
delete(oas.Paths.Paths, root+"watch/"+v0alpha1.DashboardResourceInfo.GroupResource().Resource)
|
||||
delete(oas.Paths.Paths, root+dashboard.DashboardResourceInfo.GroupResource().Resource)
|
||||
delete(oas.Paths.Paths, root+"watch/"+dashboard.DashboardResourceInfo.GroupResource().Resource)
|
||||
|
||||
// The root API discovery list
|
||||
sub := oas.Paths.Paths[root]
|
||||
|
@ -2,6 +2,7 @@ package dashboard
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
@ -9,6 +10,7 @@ import (
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
dashboard "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/infra/appcontext"
|
||||
"github.com/grafana/grafana/pkg/infra/slugify"
|
||||
@ -16,6 +18,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/guardian"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
)
|
||||
|
||||
// The DTO returns everything the UI needs in a single request
|
||||
@ -23,8 +26,10 @@ type DTOConnector struct {
|
||||
builder *DashboardsAPIBuilder
|
||||
}
|
||||
|
||||
var _ = rest.Connecter(&DTOConnector{})
|
||||
var _ = rest.StorageMetadata(&DTOConnector{})
|
||||
var (
|
||||
_ rest.Connecter = (*DTOConnector)(nil)
|
||||
_ rest.StorageMetadata = (*DTOConnector)(nil)
|
||||
)
|
||||
|
||||
func (r *DTOConnector) New() runtime.Object {
|
||||
return &dashboard.DashboardWithAccessInfo{}
|
||||
@ -88,10 +93,32 @@ func (r *DTOConnector) Connect(ctx context.Context, name string, opts runtime.Ob
|
||||
r.getAnnotationPermissionsByScope(ctx, user, &access.AnnotationsPermissions.Dashboard, accesscontrol.ScopeAnnotationsTypeDashboard)
|
||||
r.getAnnotationPermissionsByScope(ctx, user, &access.AnnotationsPermissions.Organization, accesscontrol.ScopeAnnotationsTypeOrganization)
|
||||
|
||||
dash, err := r.builder.access.GetDashboard(ctx, info.OrgID, name)
|
||||
key := &resource.ResourceKey{
|
||||
Namespace: info.Value,
|
||||
Group: dashboard.GROUP,
|
||||
Resource: dashboard.DashboardResourceInfo.GroupResource().Resource,
|
||||
Name: name,
|
||||
}
|
||||
store := r.builder.legacy.access
|
||||
rsp, err := store.Read(ctx, &resource.ReadRequest{Key: key})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dash := &dashboard.Dashboard{}
|
||||
err = json.Unmarshal(rsp.Value, dash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
obj, err := utils.MetaAccessor(dash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
blobInfo := obj.GetBlob()
|
||||
if blobInfo != nil {
|
||||
fmt.Printf("TODO, load full blob from storage %+v\n", blobInfo)
|
||||
}
|
||||
|
||||
access.Slug = slugify.Slugify(dash.Spec.GetNestedString("title"))
|
||||
access.Url = dashboards.GetDashboardFolderURL(false, name, access.Slug)
|
||||
|
||||
|
@ -1,117 +0,0 @@
|
||||
package dashboard
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
|
||||
common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
|
||||
dashboard "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
|
||||
dashver "github.com/grafana/grafana/pkg/services/dashboardversion"
|
||||
)
|
||||
|
||||
type VersionsREST struct {
|
||||
builder *DashboardsAPIBuilder
|
||||
}
|
||||
|
||||
var _ = rest.Connecter(&VersionsREST{})
|
||||
var _ = rest.StorageMetadata(&VersionsREST{})
|
||||
|
||||
func (r *VersionsREST) New() runtime.Object {
|
||||
return &dashboard.DashboardVersionList{}
|
||||
}
|
||||
|
||||
func (r *VersionsREST) Destroy() {
|
||||
}
|
||||
|
||||
func (r *VersionsREST) ConnectMethods() []string {
|
||||
return []string{"GET"}
|
||||
}
|
||||
|
||||
func (r *VersionsREST) ProducesMIMETypes(verb string) []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *VersionsREST) ProducesObject(verb string) interface{} {
|
||||
return &dashboard.DashboardVersionList{}
|
||||
}
|
||||
|
||||
func (r *VersionsREST) NewConnectOptions() (runtime.Object, bool, string) {
|
||||
return nil, true, ""
|
||||
}
|
||||
|
||||
func (r *VersionsREST) Connect(ctx context.Context, uid string, opts runtime.Object, responder rest.Responder) (http.Handler, error) {
|
||||
info, err := request.NamespaceInfoFrom(ctx, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
path := req.URL.Path
|
||||
idx := strings.LastIndex(path, "/versions/")
|
||||
if idx > 0 {
|
||||
key := path[strings.LastIndex(path, "/")+1:]
|
||||
version, err := strconv.Atoi(key)
|
||||
if err != nil {
|
||||
responder.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
dto, err := r.builder.dashboardVersionService.Get(ctx, &dashver.GetDashboardVersionQuery{
|
||||
DashboardUID: uid,
|
||||
OrgID: info.OrgID,
|
||||
Version: version,
|
||||
})
|
||||
if err != nil {
|
||||
responder.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
data, _ := dto.Data.Map()
|
||||
|
||||
// Convert the version to a regular dashboard
|
||||
dash := &dashboard.Dashboard{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: uid,
|
||||
CreationTimestamp: metav1.NewTime(dto.Created),
|
||||
},
|
||||
Spec: common.Unstructured{Object: data},
|
||||
}
|
||||
responder.Object(100, dash)
|
||||
return
|
||||
}
|
||||
|
||||
// Or list versions
|
||||
rsp, err := r.builder.dashboardVersionService.List(ctx, &dashver.ListDashboardVersionsQuery{
|
||||
DashboardUID: uid,
|
||||
OrgID: info.OrgID,
|
||||
})
|
||||
if err != nil {
|
||||
responder.Error(err)
|
||||
return
|
||||
}
|
||||
versions := &dashboard.DashboardVersionList{}
|
||||
for _, v := range rsp {
|
||||
info := dashboard.DashboardVersionInfo{
|
||||
Version: v.Version,
|
||||
Created: v.Created.UnixMilli(),
|
||||
Message: v.Message,
|
||||
}
|
||||
if v.ParentVersion != v.Version {
|
||||
info.ParentVersion = v.ParentVersion
|
||||
}
|
||||
if v.CreatedBy > 0 {
|
||||
info.CreatedBy = fmt.Sprintf("%d", v.CreatedBy)
|
||||
}
|
||||
versions.Items = append(versions.Items, info)
|
||||
}
|
||||
responder.Object(http.StatusOK, versions)
|
||||
}), nil
|
||||
}
|
103
pkg/storage/unified/apistore/history.go
Normal file
103
pkg/storage/unified/apistore/history.go
Normal file
@ -0,0 +1,103 @@
|
||||
package apistore
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
)
|
||||
|
||||
type HistoryConnector interface {
|
||||
rest.Storage
|
||||
rest.Connecter
|
||||
rest.StorageMetadata
|
||||
}
|
||||
|
||||
func NewHistoryConnector(index resource.ResourceIndexServer, gr schema.GroupResource) HistoryConnector {
|
||||
return &historyREST{
|
||||
index: index,
|
||||
gr: gr,
|
||||
}
|
||||
}
|
||||
|
||||
type historyREST struct {
|
||||
index resource.ResourceIndexServer // should be a client!
|
||||
gr schema.GroupResource
|
||||
}
|
||||
|
||||
func (r *historyREST) New() runtime.Object {
|
||||
return &metav1.PartialObjectMetadataList{}
|
||||
}
|
||||
|
||||
func (r *historyREST) Destroy() {
|
||||
}
|
||||
|
||||
func (r *historyREST) ConnectMethods() []string {
|
||||
return []string{"GET"}
|
||||
}
|
||||
|
||||
func (r *historyREST) ProducesMIMETypes(verb string) []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *historyREST) ProducesObject(verb string) interface{} {
|
||||
return &metav1.PartialObjectMetadataList{}
|
||||
}
|
||||
|
||||
func (r *historyREST) NewConnectOptions() (runtime.Object, bool, string) {
|
||||
return nil, false, ""
|
||||
}
|
||||
|
||||
func (r *historyREST) Connect(ctx context.Context, uid string, opts runtime.Object, responder rest.Responder) (http.Handler, error) {
|
||||
info, err := request.NamespaceInfoFrom(ctx, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
key := &resource.ResourceKey{
|
||||
Namespace: info.Value,
|
||||
Group: r.gr.Group,
|
||||
Resource: r.gr.Resource,
|
||||
Name: uid,
|
||||
}
|
||||
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
query := req.URL.Query()
|
||||
rsp, err := r.index.History(ctx, &resource.HistoryRequest{
|
||||
NextPageToken: query.Get("token"),
|
||||
Limit: 100, // TODO, from query
|
||||
Key: key,
|
||||
})
|
||||
if err != nil {
|
||||
responder.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
list := &metav1.PartialObjectMetadataList{
|
||||
ListMeta: metav1.ListMeta{
|
||||
Continue: rsp.NextPageToken,
|
||||
},
|
||||
}
|
||||
if rsp.ResourceVersion > 0 {
|
||||
list.ResourceVersion = strconv.FormatInt(rsp.ResourceVersion, 10)
|
||||
}
|
||||
for _, v := range rsp.Items {
|
||||
partial := metav1.PartialObjectMetadata{}
|
||||
err = json.Unmarshal(v.PartialObjectMeta, &partial)
|
||||
if err != nil {
|
||||
responder.Error(err)
|
||||
return
|
||||
}
|
||||
list.Items = append(list.Items, partial)
|
||||
}
|
||||
responder.Object(http.StatusOK, list)
|
||||
}), nil
|
||||
}
|
@ -5,8 +5,9 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
_ DiagnosticsServer = &noopService{}
|
||||
_ LifecycleHooks = &noopService{}
|
||||
_ DiagnosticsServer = (*noopService)(nil)
|
||||
_ ResourceIndexServer = (*noopService)(nil)
|
||||
_ LifecycleHooks = (*noopService)(nil)
|
||||
)
|
||||
|
||||
// noopService is a helper implementation to simplify tests
|
||||
@ -39,3 +40,13 @@ func (n *noopService) Read(context.Context, *ReadRequest) (*ReadResponse, error)
|
||||
func (n *noopService) List(context.Context, *ListRequest) (*ListResponse, error) {
|
||||
return nil, ErrNotImplementedYet
|
||||
}
|
||||
|
||||
// History implements ResourceServer.
|
||||
func (n *noopService) History(context.Context, *HistoryRequest) (*HistoryResponse, error) {
|
||||
return nil, ErrNotImplementedYet
|
||||
}
|
||||
|
||||
// Origin implements ResourceServer.
|
||||
func (n *noopService) Origin(context.Context, *OriginRequest) (*OriginResponse, error) {
|
||||
return nil, ErrNotImplementedYet
|
||||
}
|
||||
|
@ -173,7 +173,7 @@ func (x HealthCheckResponse_ServingStatus) Number() protoreflect.EnumNumber {
|
||||
|
||||
// Deprecated: Use HealthCheckResponse_ServingStatus.Descriptor instead.
|
||||
func (HealthCheckResponse_ServingStatus) EnumDescriptor() ([]byte, []int) {
|
||||
return file_resource_proto_rawDescGZIP(), []int{21, 0}
|
||||
return file_resource_proto_rawDescGZIP(), []int{26, 0}
|
||||
}
|
||||
|
||||
type ResourceKey struct {
|
||||
@ -1605,6 +1605,388 @@ func (x *WatchEvent) GetPrevious() *WatchEvent_Resource {
|
||||
return nil
|
||||
}
|
||||
|
||||
type HistoryRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// Starting from the requested page (other query parameters must match!)
|
||||
NextPageToken string `protobuf:"bytes,1,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"`
|
||||
// Maximum number of items to return
|
||||
Limit int64 `protobuf:"varint,2,opt,name=limit,proto3" json:"limit,omitempty"`
|
||||
// Resource identifier
|
||||
Key *ResourceKey `protobuf:"bytes,3,opt,name=key,proto3" json:"key,omitempty"`
|
||||
// List the deleted values (eg, show trash)
|
||||
ShowDeleted bool `protobuf:"varint,4,opt,name=show_deleted,json=showDeleted,proto3" json:"show_deleted,omitempty"`
|
||||
}
|
||||
|
||||
func (x *HistoryRequest) Reset() {
|
||||
*x = HistoryRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_resource_proto_msgTypes[20]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *HistoryRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*HistoryRequest) ProtoMessage() {}
|
||||
|
||||
func (x *HistoryRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_resource_proto_msgTypes[20]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use HistoryRequest.ProtoReflect.Descriptor instead.
|
||||
func (*HistoryRequest) Descriptor() ([]byte, []int) {
|
||||
return file_resource_proto_rawDescGZIP(), []int{20}
|
||||
}
|
||||
|
||||
func (x *HistoryRequest) GetNextPageToken() string {
|
||||
if x != nil {
|
||||
return x.NextPageToken
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *HistoryRequest) GetLimit() int64 {
|
||||
if x != nil {
|
||||
return x.Limit
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *HistoryRequest) GetKey() *ResourceKey {
|
||||
if x != nil {
|
||||
return x.Key
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *HistoryRequest) GetShowDeleted() bool {
|
||||
if x != nil {
|
||||
return x.ShowDeleted
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type HistoryResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Items []*ResourceMeta `protobuf:"bytes,1,rep,name=items,proto3" json:"items,omitempty"`
|
||||
// More results exist... pass this in the next request
|
||||
NextPageToken string `protobuf:"bytes,2,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"`
|
||||
// ResourceVersion of the list response
|
||||
ResourceVersion int64 `protobuf:"varint,3,opt,name=resource_version,json=resourceVersion,proto3" json:"resource_version,omitempty"`
|
||||
}
|
||||
|
||||
func (x *HistoryResponse) Reset() {
|
||||
*x = HistoryResponse{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_resource_proto_msgTypes[21]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *HistoryResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*HistoryResponse) ProtoMessage() {}
|
||||
|
||||
func (x *HistoryResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_resource_proto_msgTypes[21]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use HistoryResponse.ProtoReflect.Descriptor instead.
|
||||
func (*HistoryResponse) Descriptor() ([]byte, []int) {
|
||||
return file_resource_proto_rawDescGZIP(), []int{21}
|
||||
}
|
||||
|
||||
func (x *HistoryResponse) GetItems() []*ResourceMeta {
|
||||
if x != nil {
|
||||
return x.Items
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *HistoryResponse) GetNextPageToken() string {
|
||||
if x != nil {
|
||||
return x.NextPageToken
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *HistoryResponse) GetResourceVersion() int64 {
|
||||
if x != nil {
|
||||
return x.ResourceVersion
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type OriginRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// Starting from the requested page (other query parameters must match!)
|
||||
NextPageToken string `protobuf:"bytes,1,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"`
|
||||
// Maximum number of items to return
|
||||
Limit int64 `protobuf:"varint,2,opt,name=limit,proto3" json:"limit,omitempty"`
|
||||
// Resource identifier
|
||||
Key *ResourceKey `protobuf:"bytes,3,opt,name=key,proto3" json:"key,omitempty"`
|
||||
// List the deleted values (eg, show trash)
|
||||
Origin string `protobuf:"bytes,4,opt,name=origin,proto3" json:"origin,omitempty"`
|
||||
}
|
||||
|
||||
func (x *OriginRequest) Reset() {
|
||||
*x = OriginRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_resource_proto_msgTypes[22]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *OriginRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*OriginRequest) ProtoMessage() {}
|
||||
|
||||
func (x *OriginRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_resource_proto_msgTypes[22]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use OriginRequest.ProtoReflect.Descriptor instead.
|
||||
func (*OriginRequest) Descriptor() ([]byte, []int) {
|
||||
return file_resource_proto_rawDescGZIP(), []int{22}
|
||||
}
|
||||
|
||||
func (x *OriginRequest) GetNextPageToken() string {
|
||||
if x != nil {
|
||||
return x.NextPageToken
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *OriginRequest) GetLimit() int64 {
|
||||
if x != nil {
|
||||
return x.Limit
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *OriginRequest) GetKey() *ResourceKey {
|
||||
if x != nil {
|
||||
return x.Key
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *OriginRequest) GetOrigin() string {
|
||||
if x != nil {
|
||||
return x.Origin
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type ResourceOriginInfo struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// The resource
|
||||
Key *ResourceKey `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
|
||||
// Size of the full resource body
|
||||
ResourceSize int32 `protobuf:"varint,2,opt,name=resource_size,json=resourceSize,proto3" json:"resource_size,omitempty"`
|
||||
// Hash for the resource
|
||||
ResourceHash string `protobuf:"bytes,3,opt,name=resource_hash,json=resourceHash,proto3" json:"resource_hash,omitempty"`
|
||||
// The origin name
|
||||
Origin string `protobuf:"bytes,4,opt,name=origin,proto3" json:"origin,omitempty"`
|
||||
// Path on the origin
|
||||
Path string `protobuf:"bytes,5,opt,name=path,proto3" json:"path,omitempty"`
|
||||
// Verification hash from the origin
|
||||
Hash string `protobuf:"bytes,6,opt,name=hash,proto3" json:"hash,omitempty"`
|
||||
// Change time from the origin
|
||||
Timestamp int64 `protobuf:"varint,7,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
|
||||
}
|
||||
|
||||
func (x *ResourceOriginInfo) Reset() {
|
||||
*x = ResourceOriginInfo{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_resource_proto_msgTypes[23]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *ResourceOriginInfo) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ResourceOriginInfo) ProtoMessage() {}
|
||||
|
||||
func (x *ResourceOriginInfo) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_resource_proto_msgTypes[23]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ResourceOriginInfo.ProtoReflect.Descriptor instead.
|
||||
func (*ResourceOriginInfo) Descriptor() ([]byte, []int) {
|
||||
return file_resource_proto_rawDescGZIP(), []int{23}
|
||||
}
|
||||
|
||||
func (x *ResourceOriginInfo) GetKey() *ResourceKey {
|
||||
if x != nil {
|
||||
return x.Key
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ResourceOriginInfo) GetResourceSize() int32 {
|
||||
if x != nil {
|
||||
return x.ResourceSize
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *ResourceOriginInfo) GetResourceHash() string {
|
||||
if x != nil {
|
||||
return x.ResourceHash
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ResourceOriginInfo) GetOrigin() string {
|
||||
if x != nil {
|
||||
return x.Origin
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ResourceOriginInfo) GetPath() string {
|
||||
if x != nil {
|
||||
return x.Path
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ResourceOriginInfo) GetHash() string {
|
||||
if x != nil {
|
||||
return x.Hash
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ResourceOriginInfo) GetTimestamp() int64 {
|
||||
if x != nil {
|
||||
return x.Timestamp
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type OriginResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Items []*ResourceOriginInfo `protobuf:"bytes,1,rep,name=items,proto3" json:"items,omitempty"`
|
||||
// More results exist... pass this in the next request
|
||||
NextPageToken string `protobuf:"bytes,2,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"`
|
||||
// ResourceVersion of the list response
|
||||
ResourceVersion int64 `protobuf:"varint,3,opt,name=resource_version,json=resourceVersion,proto3" json:"resource_version,omitempty"`
|
||||
}
|
||||
|
||||
func (x *OriginResponse) Reset() {
|
||||
*x = OriginResponse{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_resource_proto_msgTypes[24]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *OriginResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*OriginResponse) ProtoMessage() {}
|
||||
|
||||
func (x *OriginResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_resource_proto_msgTypes[24]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use OriginResponse.ProtoReflect.Descriptor instead.
|
||||
func (*OriginResponse) Descriptor() ([]byte, []int) {
|
||||
return file_resource_proto_rawDescGZIP(), []int{24}
|
||||
}
|
||||
|
||||
func (x *OriginResponse) GetItems() []*ResourceOriginInfo {
|
||||
if x != nil {
|
||||
return x.Items
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *OriginResponse) GetNextPageToken() string {
|
||||
if x != nil {
|
||||
return x.NextPageToken
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *OriginResponse) GetResourceVersion() int64 {
|
||||
if x != nil {
|
||||
return x.ResourceVersion
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type HealthCheckRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
@ -1616,7 +1998,7 @@ type HealthCheckRequest struct {
|
||||
func (x *HealthCheckRequest) Reset() {
|
||||
*x = HealthCheckRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_resource_proto_msgTypes[20]
|
||||
mi := &file_resource_proto_msgTypes[25]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@ -1629,7 +2011,7 @@ func (x *HealthCheckRequest) String() string {
|
||||
func (*HealthCheckRequest) ProtoMessage() {}
|
||||
|
||||
func (x *HealthCheckRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_resource_proto_msgTypes[20]
|
||||
mi := &file_resource_proto_msgTypes[25]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@ -1642,7 +2024,7 @@ func (x *HealthCheckRequest) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use HealthCheckRequest.ProtoReflect.Descriptor instead.
|
||||
func (*HealthCheckRequest) Descriptor() ([]byte, []int) {
|
||||
return file_resource_proto_rawDescGZIP(), []int{20}
|
||||
return file_resource_proto_rawDescGZIP(), []int{25}
|
||||
}
|
||||
|
||||
func (x *HealthCheckRequest) GetService() string {
|
||||
@ -1663,7 +2045,7 @@ type HealthCheckResponse struct {
|
||||
func (x *HealthCheckResponse) Reset() {
|
||||
*x = HealthCheckResponse{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_resource_proto_msgTypes[21]
|
||||
mi := &file_resource_proto_msgTypes[26]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@ -1676,7 +2058,7 @@ func (x *HealthCheckResponse) String() string {
|
||||
func (*HealthCheckResponse) ProtoMessage() {}
|
||||
|
||||
func (x *HealthCheckResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_resource_proto_msgTypes[21]
|
||||
mi := &file_resource_proto_msgTypes[26]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@ -1689,7 +2071,7 @@ func (x *HealthCheckResponse) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use HealthCheckResponse.ProtoReflect.Descriptor instead.
|
||||
func (*HealthCheckResponse) Descriptor() ([]byte, []int) {
|
||||
return file_resource_proto_rawDescGZIP(), []int{21}
|
||||
return file_resource_proto_rawDescGZIP(), []int{26}
|
||||
}
|
||||
|
||||
func (x *HealthCheckResponse) GetStatus() HealthCheckResponse_ServingStatus {
|
||||
@ -1711,7 +2093,7 @@ type WatchEvent_Resource struct {
|
||||
func (x *WatchEvent_Resource) Reset() {
|
||||
*x = WatchEvent_Resource{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_resource_proto_msgTypes[22]
|
||||
mi := &file_resource_proto_msgTypes[27]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@ -1724,7 +2106,7 @@ func (x *WatchEvent_Resource) String() string {
|
||||
func (*WatchEvent_Resource) ProtoMessage() {}
|
||||
|
||||
func (x *WatchEvent_Resource) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_resource_proto_msgTypes[22]
|
||||
mi := &file_resource_proto_msgTypes[27]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@ -1936,56 +2318,121 @@ var file_resource_proto_rawDesc = []byte{
|
||||
0x08, 0x4d, 0x4f, 0x44, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x44,
|
||||
0x45, 0x4c, 0x45, 0x54, 0x45, 0x44, 0x10, 0x03, 0x12, 0x0c, 0x0a, 0x08, 0x42, 0x4f, 0x4f, 0x4b,
|
||||
0x4d, 0x41, 0x52, 0x4b, 0x10, 0x04, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10,
|
||||
0x05, 0x22, 0x2e, 0x0a, 0x12, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b,
|
||||
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69,
|
||||
0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63,
|
||||
0x65, 0x22, 0xab, 0x01, 0x0a, 0x13, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63,
|
||||
0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x43, 0x0a, 0x06, 0x73, 0x74, 0x61,
|
||||
0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2b, 0x2e, 0x72, 0x65, 0x73, 0x6f,
|
||||
0x75, 0x72, 0x63, 0x65, 0x2e, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b,
|
||||
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67,
|
||||
0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x4f,
|
||||
0x0a, 0x0d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12,
|
||||
0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07,
|
||||
0x53, 0x45, 0x52, 0x56, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x0f, 0x0a, 0x0b, 0x4e, 0x4f, 0x54,
|
||||
0x5f, 0x53, 0x45, 0x52, 0x56, 0x49, 0x4e, 0x47, 0x10, 0x02, 0x12, 0x13, 0x0a, 0x0f, 0x53, 0x45,
|
||||
0x52, 0x56, 0x49, 0x43, 0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x03, 0x2a,
|
||||
0x33, 0x0a, 0x14, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69,
|
||||
0x6f, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x12, 0x10, 0x0a, 0x0c, 0x4e, 0x6f, 0x74, 0x4f, 0x6c,
|
||||
0x64, 0x65, 0x72, 0x54, 0x68, 0x61, 0x6e, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x78, 0x61,
|
||||
0x63, 0x74, 0x10, 0x01, 0x32, 0xed, 0x02, 0x0a, 0x0d, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63,
|
||||
0x65, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x12, 0x35, 0x0a, 0x04, 0x52, 0x65, 0x61, 0x64, 0x12, 0x15,
|
||||
0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65,
|
||||
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
|
||||
0x2e, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a,
|
||||
0x06, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x12, 0x17, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72,
|
||||
0x63, 0x65, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
||||
0x1a, 0x18, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x43, 0x72, 0x65, 0x61,
|
||||
0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x06, 0x55, 0x70,
|
||||
0x64, 0x61, 0x74, 0x65, 0x12, 0x17, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e,
|
||||
0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e,
|
||||
0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52,
|
||||
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x06, 0x44, 0x65, 0x6c, 0x65, 0x74,
|
||||
0x65, 0x12, 0x17, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x44, 0x65, 0x6c,
|
||||
0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x72, 0x65, 0x73,
|
||||
0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70,
|
||||
0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x15, 0x2e, 0x72,
|
||||
0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75,
|
||||
0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x4c,
|
||||
0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x05, 0x57,
|
||||
0x61, 0x74, 0x63, 0x68, 0x12, 0x16, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e,
|
||||
0x57, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x72,
|
||||
0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x57, 0x61, 0x74, 0x63, 0x68, 0x45, 0x76, 0x65,
|
||||
0x6e, 0x74, 0x30, 0x01, 0x32, 0x57, 0x0a, 0x0b, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74,
|
||||
0x69, 0x63, 0x73, 0x12, 0x48, 0x0a, 0x09, 0x49, 0x73, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x79,
|
||||
0x12, 0x1c, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x48, 0x65, 0x61, 0x6c,
|
||||
0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d,
|
||||
0x05, 0x22, 0x9a, 0x01, 0x0a, 0x0e, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71,
|
||||
0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x70, 0x61, 0x67,
|
||||
0x65, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e,
|
||||
0x65, 0x78, 0x74, 0x50, 0x61, 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x14, 0x0a, 0x05,
|
||||
0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x6c, 0x69, 0x6d,
|
||||
0x69, 0x74, 0x12, 0x27, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32,
|
||||
0x15, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75,
|
||||
0x72, 0x63, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x21, 0x0a, 0x0c, 0x73,
|
||||
0x68, 0x6f, 0x77, 0x5f, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28,
|
||||
0x08, 0x52, 0x0b, 0x73, 0x68, 0x6f, 0x77, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x22, 0x92,
|
||||
0x01, 0x0a, 0x0f, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
|
||||
0x73, 0x65, 0x12, 0x2c, 0x0a, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28,
|
||||
0x0b, 0x32, 0x16, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73,
|
||||
0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73,
|
||||
0x12, 0x26, 0x0a, 0x0f, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x6f,
|
||||
0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x65, 0x78, 0x74, 0x50,
|
||||
0x61, 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f,
|
||||
0x75, 0x72, 0x63, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01,
|
||||
0x28, 0x03, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x65, 0x72, 0x73,
|
||||
0x69, 0x6f, 0x6e, 0x22, 0x8e, 0x01, 0x0a, 0x0d, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x52, 0x65,
|
||||
0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x70, 0x61,
|
||||
0x67, 0x65, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d,
|
||||
0x6e, 0x65, 0x78, 0x74, 0x50, 0x61, 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x14, 0x0a,
|
||||
0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x6c, 0x69,
|
||||
0x6d, 0x69, 0x74, 0x12, 0x27, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b,
|
||||
0x32, 0x15, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f,
|
||||
0x75, 0x72, 0x63, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x16, 0x0a, 0x06,
|
||||
0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x72,
|
||||
0x69, 0x67, 0x69, 0x6e, 0x22, 0xe5, 0x01, 0x0a, 0x12, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63,
|
||||
0x65, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x27, 0x0a, 0x03, 0x6b,
|
||||
0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75,
|
||||
0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4b, 0x65, 0x79, 0x52,
|
||||
0x03, 0x6b, 0x65, 0x79, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
|
||||
0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x72, 0x65, 0x73,
|
||||
0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x73,
|
||||
0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09,
|
||||
0x52, 0x0c, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x48, 0x61, 0x73, 0x68, 0x12, 0x16,
|
||||
0x0a, 0x06, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06,
|
||||
0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x05,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61,
|
||||
0x73, 0x68, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x1c,
|
||||
0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x07, 0x20, 0x01, 0x28,
|
||||
0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x97, 0x01, 0x0a,
|
||||
0x0e, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
|
||||
0x32, 0x0a, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c,
|
||||
0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72,
|
||||
0x63, 0x65, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x69, 0x74,
|
||||
0x65, 0x6d, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x70, 0x61, 0x67, 0x65,
|
||||
0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x65,
|
||||
0x78, 0x74, 0x50, 0x61, 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x29, 0x0a, 0x10, 0x72,
|
||||
0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18,
|
||||
0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56,
|
||||
0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x2e, 0x0a, 0x12, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68,
|
||||
0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07,
|
||||
0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73,
|
||||
0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x22, 0xab, 0x01, 0x0a, 0x13, 0x48, 0x65, 0x61, 0x6c, 0x74,
|
||||
0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x43,
|
||||
0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2b,
|
||||
0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68,
|
||||
0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x39, 0x5a,
|
||||
0x37, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x72, 0x61, 0x66,
|
||||
0x61, 0x6e, 0x61, 0x2f, 0x67, 0x72, 0x61, 0x66, 0x61, 0x6e, 0x61, 0x2f, 0x70, 0x6b, 0x67, 0x2f,
|
||||
0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2f, 0x75, 0x6e, 0x69, 0x66, 0x69, 0x65, 0x64, 0x2f,
|
||||
0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x65,
|
||||
0x72, 0x76, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61,
|
||||
0x74, 0x75, 0x73, 0x22, 0x4f, 0x0a, 0x0d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x53, 0x74,
|
||||
0x61, 0x74, 0x75, 0x73, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10,
|
||||
0x00, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x45, 0x52, 0x56, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x0f,
|
||||
0x0a, 0x0b, 0x4e, 0x4f, 0x54, 0x5f, 0x53, 0x45, 0x52, 0x56, 0x49, 0x4e, 0x47, 0x10, 0x02, 0x12,
|
||||
0x13, 0x0a, 0x0f, 0x53, 0x45, 0x52, 0x56, 0x49, 0x43, 0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f,
|
||||
0x57, 0x4e, 0x10, 0x03, 0x2a, 0x33, 0x0a, 0x14, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
|
||||
0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x12, 0x10, 0x0a, 0x0c,
|
||||
0x4e, 0x6f, 0x74, 0x4f, 0x6c, 0x64, 0x65, 0x72, 0x54, 0x68, 0x61, 0x6e, 0x10, 0x00, 0x12, 0x09,
|
||||
0x0a, 0x05, 0x45, 0x78, 0x61, 0x63, 0x74, 0x10, 0x01, 0x32, 0xed, 0x02, 0x0a, 0x0d, 0x52, 0x65,
|
||||
0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x12, 0x35, 0x0a, 0x04, 0x52,
|
||||
0x65, 0x61, 0x64, 0x12, 0x15, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52,
|
||||
0x65, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x72, 0x65, 0x73,
|
||||
0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
|
||||
0x73, 0x65, 0x12, 0x3b, 0x0a, 0x06, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x12, 0x17, 0x2e, 0x72,
|
||||
0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65,
|
||||
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
|
||||
0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
|
||||
0x3b, 0x0a, 0x06, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x17, 0x2e, 0x72, 0x65, 0x73, 0x6f,
|
||||
0x75, 0x72, 0x63, 0x65, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65,
|
||||
0x73, 0x74, 0x1a, 0x18, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x55, 0x70,
|
||||
0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x06,
|
||||
0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x17, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63,
|
||||
0x65, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
|
||||
0x18, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74,
|
||||
0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x04, 0x4c, 0x69, 0x73,
|
||||
0x74, 0x12, 0x15, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x4c, 0x69, 0x73,
|
||||
0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75,
|
||||
0x72, 0x63, 0x65, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
||||
0x12, 0x37, 0x0a, 0x05, 0x57, 0x61, 0x74, 0x63, 0x68, 0x12, 0x16, 0x2e, 0x72, 0x65, 0x73, 0x6f,
|
||||
0x75, 0x72, 0x63, 0x65, 0x2e, 0x57, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
||||
0x74, 0x1a, 0x14, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x57, 0x61, 0x74,
|
||||
0x63, 0x68, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x32, 0xc3, 0x01, 0x0a, 0x0d, 0x52, 0x65,
|
||||
0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x35, 0x0a, 0x04, 0x52,
|
||||
0x65, 0x61, 0x64, 0x12, 0x15, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52,
|
||||
0x65, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x72, 0x65, 0x73,
|
||||
0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
|
||||
0x73, 0x65, 0x12, 0x3e, 0x0a, 0x07, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x18, 0x2e,
|
||||
0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79,
|
||||
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72,
|
||||
0x63, 0x65, 0x2e, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
|
||||
0x73, 0x65, 0x12, 0x3b, 0x0a, 0x06, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x12, 0x17, 0x2e, 0x72,
|
||||
0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x52, 0x65,
|
||||
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
|
||||
0x2e, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32,
|
||||
0x57, 0x0a, 0x0b, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x12, 0x48,
|
||||
0x0a, 0x09, 0x49, 0x73, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x79, 0x12, 0x1c, 0x2e, 0x72, 0x65,
|
||||
0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65,
|
||||
0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x72, 0x65, 0x73, 0x6f,
|
||||
0x75, 0x72, 0x63, 0x65, 0x2e, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b,
|
||||
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x39, 0x5a, 0x37, 0x67, 0x69, 0x74, 0x68,
|
||||
0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x72, 0x61, 0x66, 0x61, 0x6e, 0x61, 0x2f, 0x67,
|
||||
0x72, 0x61, 0x66, 0x61, 0x6e, 0x61, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x61,
|
||||
0x67, 0x65, 0x2f, 0x75, 0x6e, 0x69, 0x66, 0x69, 0x65, 0x64, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75,
|
||||
0x72, 0x63, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
@ -2001,7 +2448,7 @@ func file_resource_proto_rawDescGZIP() []byte {
|
||||
}
|
||||
|
||||
var file_resource_proto_enumTypes = make([]protoimpl.EnumInfo, 3)
|
||||
var file_resource_proto_msgTypes = make([]protoimpl.MessageInfo, 23)
|
||||
var file_resource_proto_msgTypes = make([]protoimpl.MessageInfo, 28)
|
||||
var file_resource_proto_goTypes = []any{
|
||||
(ResourceVersionMatch)(0), // 0: resource.ResourceVersionMatch
|
||||
(WatchEvent_Type)(0), // 1: resource.WatchEvent.Type
|
||||
@ -2026,9 +2473,14 @@ var file_resource_proto_goTypes = []any{
|
||||
(*ListResponse)(nil), // 20: resource.ListResponse
|
||||
(*WatchRequest)(nil), // 21: resource.WatchRequest
|
||||
(*WatchEvent)(nil), // 22: resource.WatchEvent
|
||||
(*HealthCheckRequest)(nil), // 23: resource.HealthCheckRequest
|
||||
(*HealthCheckResponse)(nil), // 24: resource.HealthCheckResponse
|
||||
(*WatchEvent_Resource)(nil), // 25: resource.WatchEvent.Resource
|
||||
(*HistoryRequest)(nil), // 23: resource.HistoryRequest
|
||||
(*HistoryResponse)(nil), // 24: resource.HistoryResponse
|
||||
(*OriginRequest)(nil), // 25: resource.OriginRequest
|
||||
(*ResourceOriginInfo)(nil), // 26: resource.ResourceOriginInfo
|
||||
(*OriginResponse)(nil), // 27: resource.OriginResponse
|
||||
(*HealthCheckRequest)(nil), // 28: resource.HealthCheckRequest
|
||||
(*HealthCheckResponse)(nil), // 29: resource.HealthCheckResponse
|
||||
(*WatchEvent_Resource)(nil), // 30: resource.WatchEvent.Resource
|
||||
}
|
||||
var file_resource_proto_depIdxs = []int32{
|
||||
7, // 0: resource.ErrorResult.details:type_name -> resource.ErrorDetails
|
||||
@ -2049,28 +2501,39 @@ var file_resource_proto_depIdxs = []int32{
|
||||
4, // 15: resource.ListResponse.items:type_name -> resource.ResourceWrapper
|
||||
18, // 16: resource.WatchRequest.options:type_name -> resource.ListOptions
|
||||
1, // 17: resource.WatchEvent.type:type_name -> resource.WatchEvent.Type
|
||||
25, // 18: resource.WatchEvent.resource:type_name -> resource.WatchEvent.Resource
|
||||
25, // 19: resource.WatchEvent.previous:type_name -> resource.WatchEvent.Resource
|
||||
2, // 20: resource.HealthCheckResponse.status:type_name -> resource.HealthCheckResponse.ServingStatus
|
||||
15, // 21: resource.ResourceStore.Read:input_type -> resource.ReadRequest
|
||||
9, // 22: resource.ResourceStore.Create:input_type -> resource.CreateRequest
|
||||
11, // 23: resource.ResourceStore.Update:input_type -> resource.UpdateRequest
|
||||
13, // 24: resource.ResourceStore.Delete:input_type -> resource.DeleteRequest
|
||||
19, // 25: resource.ResourceStore.List:input_type -> resource.ListRequest
|
||||
21, // 26: resource.ResourceStore.Watch:input_type -> resource.WatchRequest
|
||||
23, // 27: resource.Diagnostics.IsHealthy:input_type -> resource.HealthCheckRequest
|
||||
16, // 28: resource.ResourceStore.Read:output_type -> resource.ReadResponse
|
||||
10, // 29: resource.ResourceStore.Create:output_type -> resource.CreateResponse
|
||||
12, // 30: resource.ResourceStore.Update:output_type -> resource.UpdateResponse
|
||||
14, // 31: resource.ResourceStore.Delete:output_type -> resource.DeleteResponse
|
||||
20, // 32: resource.ResourceStore.List:output_type -> resource.ListResponse
|
||||
22, // 33: resource.ResourceStore.Watch:output_type -> resource.WatchEvent
|
||||
24, // 34: resource.Diagnostics.IsHealthy:output_type -> resource.HealthCheckResponse
|
||||
28, // [28:35] is the sub-list for method output_type
|
||||
21, // [21:28] is the sub-list for method input_type
|
||||
21, // [21:21] is the sub-list for extension type_name
|
||||
21, // [21:21] is the sub-list for extension extendee
|
||||
0, // [0:21] is the sub-list for field type_name
|
||||
30, // 18: resource.WatchEvent.resource:type_name -> resource.WatchEvent.Resource
|
||||
30, // 19: resource.WatchEvent.previous:type_name -> resource.WatchEvent.Resource
|
||||
3, // 20: resource.HistoryRequest.key:type_name -> resource.ResourceKey
|
||||
5, // 21: resource.HistoryResponse.items:type_name -> resource.ResourceMeta
|
||||
3, // 22: resource.OriginRequest.key:type_name -> resource.ResourceKey
|
||||
3, // 23: resource.ResourceOriginInfo.key:type_name -> resource.ResourceKey
|
||||
26, // 24: resource.OriginResponse.items:type_name -> resource.ResourceOriginInfo
|
||||
2, // 25: resource.HealthCheckResponse.status:type_name -> resource.HealthCheckResponse.ServingStatus
|
||||
15, // 26: resource.ResourceStore.Read:input_type -> resource.ReadRequest
|
||||
9, // 27: resource.ResourceStore.Create:input_type -> resource.CreateRequest
|
||||
11, // 28: resource.ResourceStore.Update:input_type -> resource.UpdateRequest
|
||||
13, // 29: resource.ResourceStore.Delete:input_type -> resource.DeleteRequest
|
||||
19, // 30: resource.ResourceStore.List:input_type -> resource.ListRequest
|
||||
21, // 31: resource.ResourceStore.Watch:input_type -> resource.WatchRequest
|
||||
15, // 32: resource.ResourceIndex.Read:input_type -> resource.ReadRequest
|
||||
23, // 33: resource.ResourceIndex.History:input_type -> resource.HistoryRequest
|
||||
25, // 34: resource.ResourceIndex.Origin:input_type -> resource.OriginRequest
|
||||
28, // 35: resource.Diagnostics.IsHealthy:input_type -> resource.HealthCheckRequest
|
||||
16, // 36: resource.ResourceStore.Read:output_type -> resource.ReadResponse
|
||||
10, // 37: resource.ResourceStore.Create:output_type -> resource.CreateResponse
|
||||
12, // 38: resource.ResourceStore.Update:output_type -> resource.UpdateResponse
|
||||
14, // 39: resource.ResourceStore.Delete:output_type -> resource.DeleteResponse
|
||||
20, // 40: resource.ResourceStore.List:output_type -> resource.ListResponse
|
||||
22, // 41: resource.ResourceStore.Watch:output_type -> resource.WatchEvent
|
||||
16, // 42: resource.ResourceIndex.Read:output_type -> resource.ReadResponse
|
||||
24, // 43: resource.ResourceIndex.History:output_type -> resource.HistoryResponse
|
||||
27, // 44: resource.ResourceIndex.Origin:output_type -> resource.OriginResponse
|
||||
29, // 45: resource.Diagnostics.IsHealthy:output_type -> resource.HealthCheckResponse
|
||||
36, // [36:46] is the sub-list for method output_type
|
||||
26, // [26:36] is the sub-list for method input_type
|
||||
26, // [26:26] is the sub-list for extension type_name
|
||||
26, // [26:26] is the sub-list for extension extendee
|
||||
0, // [0:26] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_resource_proto_init() }
|
||||
@ -2320,7 +2783,7 @@ func file_resource_proto_init() {
|
||||
}
|
||||
}
|
||||
file_resource_proto_msgTypes[20].Exporter = func(v any, i int) any {
|
||||
switch v := v.(*HealthCheckRequest); i {
|
||||
switch v := v.(*HistoryRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
@ -2332,7 +2795,7 @@ func file_resource_proto_init() {
|
||||
}
|
||||
}
|
||||
file_resource_proto_msgTypes[21].Exporter = func(v any, i int) any {
|
||||
switch v := v.(*HealthCheckResponse); i {
|
||||
switch v := v.(*HistoryResponse); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
@ -2344,6 +2807,66 @@ func file_resource_proto_init() {
|
||||
}
|
||||
}
|
||||
file_resource_proto_msgTypes[22].Exporter = func(v any, i int) any {
|
||||
switch v := v.(*OriginRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_resource_proto_msgTypes[23].Exporter = func(v any, i int) any {
|
||||
switch v := v.(*ResourceOriginInfo); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_resource_proto_msgTypes[24].Exporter = func(v any, i int) any {
|
||||
switch v := v.(*OriginResponse); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_resource_proto_msgTypes[25].Exporter = func(v any, i int) any {
|
||||
switch v := v.(*HealthCheckRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_resource_proto_msgTypes[26].Exporter = func(v any, i int) any {
|
||||
switch v := v.(*HealthCheckResponse); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_resource_proto_msgTypes[27].Exporter = func(v any, i int) any {
|
||||
switch v := v.(*WatchEvent_Resource); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
@ -2362,9 +2885,9 @@ func file_resource_proto_init() {
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_resource_proto_rawDesc,
|
||||
NumEnums: 3,
|
||||
NumMessages: 23,
|
||||
NumMessages: 28,
|
||||
NumExtensions: 0,
|
||||
NumServices: 2,
|
||||
NumServices: 3,
|
||||
},
|
||||
GoTypes: file_resource_proto_goTypes,
|
||||
DependencyIndexes: file_resource_proto_depIdxs,
|
||||
|
@ -321,6 +321,77 @@ message WatchEvent {
|
||||
Resource previous = 4;
|
||||
}
|
||||
|
||||
message HistoryRequest {
|
||||
// Starting from the requested page (other query parameters must match!)
|
||||
string next_page_token = 1;
|
||||
|
||||
// Maximum number of items to return
|
||||
int64 limit = 2;
|
||||
|
||||
// Resource identifier
|
||||
ResourceKey key = 3;
|
||||
|
||||
// List the deleted values (eg, show trash)
|
||||
bool show_deleted = 4;
|
||||
}
|
||||
|
||||
message HistoryResponse {
|
||||
repeated ResourceMeta items = 1;
|
||||
|
||||
// More results exist... pass this in the next request
|
||||
string next_page_token = 2;
|
||||
|
||||
// ResourceVersion of the list response
|
||||
int64 resource_version = 3;
|
||||
}
|
||||
|
||||
message OriginRequest {
|
||||
// Starting from the requested page (other query parameters must match!)
|
||||
string next_page_token = 1;
|
||||
|
||||
// Maximum number of items to return
|
||||
int64 limit = 2;
|
||||
|
||||
// Resource identifier
|
||||
ResourceKey key = 3;
|
||||
|
||||
// List the deleted values (eg, show trash)
|
||||
string origin = 4;
|
||||
}
|
||||
|
||||
message ResourceOriginInfo {
|
||||
// The resource
|
||||
ResourceKey key = 1;
|
||||
|
||||
// Size of the full resource body
|
||||
int32 resource_size = 2;
|
||||
|
||||
// Hash for the resource
|
||||
string resource_hash = 3;
|
||||
|
||||
// The origin name
|
||||
string origin = 4;
|
||||
|
||||
// Path on the origin
|
||||
string path = 5;
|
||||
|
||||
// Verification hash from the origin
|
||||
string hash = 6;
|
||||
|
||||
// Change time from the origin
|
||||
int64 timestamp = 7;
|
||||
}
|
||||
|
||||
message OriginResponse {
|
||||
repeated ResourceOriginInfo items = 1;
|
||||
|
||||
// More results exist... pass this in the next request
|
||||
string next_page_token = 2;
|
||||
|
||||
// ResourceVersion of the list response
|
||||
int64 resource_version = 3;
|
||||
}
|
||||
|
||||
message HealthCheckRequest {
|
||||
string service = 1;
|
||||
}
|
||||
@ -357,6 +428,20 @@ service ResourceStore {
|
||||
rpc Watch(WatchRequest) returns (stream WatchEvent);
|
||||
}
|
||||
|
||||
// Unlike the ResourceStore, this service can be exposed to clients directly
|
||||
// It should be implemented with efficient indexes and does not need read-after-write semantics
|
||||
service ResourceIndex {
|
||||
// TODO: rpc Search(...) ... eventually a typed response
|
||||
|
||||
rpc Read(ReadRequest) returns (ReadResponse); // Duplicated -- for client read only usage
|
||||
|
||||
// Show resource history (and trash)
|
||||
rpc History(HistoryRequest) returns (HistoryResponse);
|
||||
|
||||
// Used for efficient provisioning
|
||||
rpc Origin(OriginRequest) returns (OriginResponse);
|
||||
}
|
||||
|
||||
// Clients can use this service directly
|
||||
// NOTE: This is read only, and no read afer write guarantees
|
||||
service Diagnostics {
|
||||
|
@ -347,6 +347,181 @@ var ResourceStore_ServiceDesc = grpc.ServiceDesc{
|
||||
Metadata: "resource.proto",
|
||||
}
|
||||
|
||||
const (
|
||||
ResourceIndex_Read_FullMethodName = "/resource.ResourceIndex/Read"
|
||||
ResourceIndex_History_FullMethodName = "/resource.ResourceIndex/History"
|
||||
ResourceIndex_Origin_FullMethodName = "/resource.ResourceIndex/Origin"
|
||||
)
|
||||
|
||||
// ResourceIndexClient is the client API for ResourceIndex service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
//
|
||||
// Unlike the ResourceStore, this service can be exposed to clients directly
|
||||
// It should be implemented with efficient indexes and does not need read-after-write semantics
|
||||
type ResourceIndexClient interface {
|
||||
Read(ctx context.Context, in *ReadRequest, opts ...grpc.CallOption) (*ReadResponse, error)
|
||||
// Show resource history (and trash)
|
||||
History(ctx context.Context, in *HistoryRequest, opts ...grpc.CallOption) (*HistoryResponse, error)
|
||||
// Used for efficient provisioning
|
||||
Origin(ctx context.Context, in *OriginRequest, opts ...grpc.CallOption) (*OriginResponse, error)
|
||||
}
|
||||
|
||||
type resourceIndexClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewResourceIndexClient(cc grpc.ClientConnInterface) ResourceIndexClient {
|
||||
return &resourceIndexClient{cc}
|
||||
}
|
||||
|
||||
func (c *resourceIndexClient) Read(ctx context.Context, in *ReadRequest, opts ...grpc.CallOption) (*ReadResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(ReadResponse)
|
||||
err := c.cc.Invoke(ctx, ResourceIndex_Read_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *resourceIndexClient) History(ctx context.Context, in *HistoryRequest, opts ...grpc.CallOption) (*HistoryResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(HistoryResponse)
|
||||
err := c.cc.Invoke(ctx, ResourceIndex_History_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *resourceIndexClient) Origin(ctx context.Context, in *OriginRequest, opts ...grpc.CallOption) (*OriginResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(OriginResponse)
|
||||
err := c.cc.Invoke(ctx, ResourceIndex_Origin_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// ResourceIndexServer is the server API for ResourceIndex service.
|
||||
// All implementations should embed UnimplementedResourceIndexServer
|
||||
// for forward compatibility
|
||||
//
|
||||
// Unlike the ResourceStore, this service can be exposed to clients directly
|
||||
// It should be implemented with efficient indexes and does not need read-after-write semantics
|
||||
type ResourceIndexServer interface {
|
||||
Read(context.Context, *ReadRequest) (*ReadResponse, error)
|
||||
// Show resource history (and trash)
|
||||
History(context.Context, *HistoryRequest) (*HistoryResponse, error)
|
||||
// Used for efficient provisioning
|
||||
Origin(context.Context, *OriginRequest) (*OriginResponse, error)
|
||||
}
|
||||
|
||||
// UnimplementedResourceIndexServer should be embedded to have forward compatible implementations.
|
||||
type UnimplementedResourceIndexServer struct {
|
||||
}
|
||||
|
||||
func (UnimplementedResourceIndexServer) Read(context.Context, *ReadRequest) (*ReadResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Read not implemented")
|
||||
}
|
||||
func (UnimplementedResourceIndexServer) History(context.Context, *HistoryRequest) (*HistoryResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method History not implemented")
|
||||
}
|
||||
func (UnimplementedResourceIndexServer) Origin(context.Context, *OriginRequest) (*OriginResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Origin not implemented")
|
||||
}
|
||||
|
||||
// UnsafeResourceIndexServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to ResourceIndexServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeResourceIndexServer interface {
|
||||
mustEmbedUnimplementedResourceIndexServer()
|
||||
}
|
||||
|
||||
func RegisterResourceIndexServer(s grpc.ServiceRegistrar, srv ResourceIndexServer) {
|
||||
s.RegisterService(&ResourceIndex_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _ResourceIndex_Read_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ReadRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ResourceIndexServer).Read(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: ResourceIndex_Read_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ResourceIndexServer).Read(ctx, req.(*ReadRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _ResourceIndex_History_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(HistoryRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ResourceIndexServer).History(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: ResourceIndex_History_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ResourceIndexServer).History(ctx, req.(*HistoryRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _ResourceIndex_Origin_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(OriginRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ResourceIndexServer).Origin(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: ResourceIndex_Origin_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ResourceIndexServer).Origin(ctx, req.(*OriginRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// ResourceIndex_ServiceDesc is the grpc.ServiceDesc for ResourceIndex service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var ResourceIndex_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "resource.ResourceIndex",
|
||||
HandlerType: (*ResourceIndexServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "Read",
|
||||
Handler: _ResourceIndex_Read_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "History",
|
||||
Handler: _ResourceIndex_History_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Origin",
|
||||
Handler: _ResourceIndex_Origin_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "resource.proto",
|
||||
}
|
||||
|
||||
const (
|
||||
Diagnostics_IsHealthy_FullMethodName = "/resource.Diagnostics/IsHealthy"
|
||||
)
|
||||
|
@ -22,7 +22,7 @@ import (
|
||||
|
||||
// Package-level errors.
|
||||
var (
|
||||
ErrNotFound = errors.New("entity not found")
|
||||
ErrNotFound = errors.New("resource not found")
|
||||
ErrOptimisticLockingFailed = errors.New("optimistic locking failed")
|
||||
ErrUserNotFoundInContext = errors.New("user not found in context")
|
||||
ErrUnableToReadResourceJSON = errors.New("unable to read resource json")
|
||||
@ -32,6 +32,7 @@ var (
|
||||
// ResourceServer implements all services
|
||||
type ResourceServer interface {
|
||||
ResourceStoreServer
|
||||
ResourceIndexServer
|
||||
DiagnosticsServer
|
||||
LifecycleHooks
|
||||
}
|
||||
@ -67,6 +68,9 @@ type ResourceServerOptions struct {
|
||||
// Real storage backend
|
||||
Backend StorageBackend
|
||||
|
||||
// Requests based on a search index
|
||||
Index ResourceIndexServer
|
||||
|
||||
// Diagnostics
|
||||
Diagnostics DiagnosticsServer
|
||||
|
||||
@ -89,6 +93,9 @@ func NewResourceServer(opts ResourceServerOptions) (ResourceServer, error) {
|
||||
if opts.Backend == nil {
|
||||
return nil, fmt.Errorf("missing Backend implementation")
|
||||
}
|
||||
if opts.Index == nil {
|
||||
opts.Index = &noopService{}
|
||||
}
|
||||
if opts.Diagnostics == nil {
|
||||
opts.Diagnostics = &noopService{}
|
||||
}
|
||||
@ -110,6 +117,7 @@ func NewResourceServer(opts ResourceServerOptions) (ResourceServer, error) {
|
||||
tracer: opts.Tracer,
|
||||
log: slog.Default().With("logger", "resource-server"),
|
||||
backend: opts.Backend,
|
||||
index: opts.Index,
|
||||
diagnostics: opts.Diagnostics,
|
||||
access: opts.WriteAccess,
|
||||
lifecycle: opts.Lifecycle,
|
||||
@ -125,6 +133,7 @@ type server struct {
|
||||
tracer trace.Tracer
|
||||
log *slog.Logger
|
||||
backend StorageBackend
|
||||
index ResourceIndexServer
|
||||
diagnostics DiagnosticsServer
|
||||
access WriteAccessHooks
|
||||
lifecycle LifecycleHooks
|
||||
@ -554,6 +563,22 @@ func (s *server) Watch(req *WatchRequest, srv ResourceStore_WatchServer) error {
|
||||
}
|
||||
}
|
||||
|
||||
// History implements ResourceServer.
|
||||
func (s *server) History(ctx context.Context, req *HistoryRequest) (*HistoryResponse, error) {
|
||||
if err := s.Init(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.index.History(ctx, req)
|
||||
}
|
||||
|
||||
// Origin implements ResourceServer.
|
||||
func (s *server) Origin(ctx context.Context, req *OriginRequest) (*OriginResponse, error) {
|
||||
if err := s.Init(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.index.Origin(ctx, req)
|
||||
}
|
||||
|
||||
// IsHealthy implements ResourceServer.
|
||||
func (s *server) IsHealthy(ctx context.Context, req *HealthCheckRequest) (*HealthCheckResponse, error) {
|
||||
if err := s.Init(); err != nil {
|
||||
|
@ -47,58 +47,59 @@ func TestIntegrationDashboardsApp(t *testing.T) {
|
||||
|
||||
t.Run("Check discovery client", func(t *testing.T) {
|
||||
disco := helper.GetGroupVersionInfoJSON("dashboard.grafana.app")
|
||||
//fmt.Printf("%s", string(disco))
|
||||
// fmt.Printf("%s", string(disco))
|
||||
|
||||
require.JSONEq(t, `[
|
||||
{
|
||||
"freshness": "Current",
|
||||
"resources": [
|
||||
{
|
||||
"resource": "dashboards",
|
||||
"responseKind": {
|
||||
"group": "",
|
||||
"kind": "Dashboard",
|
||||
"version": ""
|
||||
},
|
||||
"scope": "Namespaced",
|
||||
"singularResource": "dashboard",
|
||||
"subresources": [
|
||||
{
|
||||
"responseKind": {
|
||||
"group": "",
|
||||
"kind": "DashboardWithAccessInfo",
|
||||
"version": ""
|
||||
},
|
||||
"subresource": "dto",
|
||||
"verbs": [
|
||||
"get"
|
||||
]
|
||||
},
|
||||
{
|
||||
"responseKind": {
|
||||
"group": "",
|
||||
"kind": "DashboardVersionList",
|
||||
"version": ""
|
||||
},
|
||||
"subresource": "versions",
|
||||
"verbs": [
|
||||
"get"
|
||||
]
|
||||
}
|
||||
],
|
||||
"verbs": [
|
||||
"create",
|
||||
"delete",
|
||||
"deletecollection",
|
||||
"get",
|
||||
"list",
|
||||
"patch",
|
||||
"update"
|
||||
]
|
||||
}
|
||||
],
|
||||
"version": "v0alpha1"
|
||||
}
|
||||
]`, disco)
|
||||
{
|
||||
"freshness": "Current",
|
||||
"resources": [
|
||||
{
|
||||
"resource": "dashboards",
|
||||
"responseKind": {
|
||||
"group": "",
|
||||
"kind": "Dashboard",
|
||||
"version": ""
|
||||
},
|
||||
"scope": "Namespaced",
|
||||
"singularResource": "dashboard",
|
||||
"subresources": [
|
||||
{
|
||||
"responseKind": {
|
||||
"group": "",
|
||||
"kind": "DashboardWithAccessInfo",
|
||||
"version": ""
|
||||
},
|
||||
"subresource": "dto",
|
||||
"verbs": [
|
||||
"get"
|
||||
]
|
||||
},
|
||||
{
|
||||
"responseKind": {
|
||||
"group": "",
|
||||
"kind": "PartialObjectMetadataList",
|
||||
"version": ""
|
||||
},
|
||||
"subresource": "history",
|
||||
"verbs": [
|
||||
"get"
|
||||
]
|
||||
}
|
||||
],
|
||||
"verbs": [
|
||||
"create",
|
||||
"delete",
|
||||
"deletecollection",
|
||||
"get",
|
||||
"list",
|
||||
"patch",
|
||||
"update",
|
||||
"watch"
|
||||
]
|
||||
}
|
||||
],
|
||||
"version": "v0alpha1"
|
||||
}
|
||||
]`, disco)
|
||||
})
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user