Snapshots: Migrate API as dashboards k8s subresource (#113552)
This commit is contained in:
@@ -7,10 +7,10 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
snapshot "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/api/dtos"
|
||||
"github.com/grafana/grafana/pkg/api/response"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
dashboardsnapshot "github.com/grafana/grafana/pkg/apis/dashboardsnapshot/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/infra/metrics"
|
||||
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
|
||||
@@ -34,8 +34,8 @@ func (hs *HTTPServer) getCreatedSnapshotHandler() web.Handler {
|
||||
errhttp.Write(r.Context(), fmt.Errorf("no user"), w)
|
||||
return
|
||||
}
|
||||
r.URL.Path = "/apis/dashboardsnapshot.grafana.app/v0alpha1/namespaces/" +
|
||||
namespaceMapper(user.GetOrgID()) + "/dashboardsnapshots/create"
|
||||
r.URL.Path = "/apis/dashboard.grafana.app/v0alpha1/namespaces/" +
|
||||
namespaceMapper(user.GetOrgID()) + "/snapshots/create"
|
||||
hs.clientConfigProvider.DirectlyServeHTTP(w, r)
|
||||
}
|
||||
}
|
||||
@@ -85,7 +85,7 @@ func (hs *HTTPServer) CreateDashboardSnapshot(c *contextmodel.ReqContext) {
|
||||
}
|
||||
}
|
||||
|
||||
dashboardsnapshots.CreateDashboardSnapshot(c, dashboardsnapshot.SnapshotSharingOptions{
|
||||
dashboardsnapshots.CreateDashboardSnapshot(c, snapshot.SnapshotSharingOptions{
|
||||
SnapshotsEnabled: hs.Cfg.SnapshotEnabled,
|
||||
ExternalEnabled: hs.Cfg.ExternalEnabled,
|
||||
ExternalSnapshotName: hs.Cfg.ExternalSnapshotName,
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
// +k8s:deepcopy-gen=package
|
||||
// +k8s:openapi-gen=true
|
||||
// +k8s:defaulter-gen=TypeMeta
|
||||
// +groupName=dashboardsnapshot.grafana.app
|
||||
|
||||
package v0alpha1
|
||||
@@ -1,46 +0,0 @@
|
||||
package v0alpha1
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
const (
|
||||
GROUP = "dashboardsnapshot.grafana.app"
|
||||
VERSION = "v0alpha1"
|
||||
APIVERSION = GROUP + "/" + VERSION
|
||||
)
|
||||
|
||||
var DashboardSnapshotResourceInfo = utils.NewResourceInfo(GROUP, VERSION,
|
||||
"dashboardsnapshots", "dashboardsnapshot", "DashboardSnapshot",
|
||||
func() runtime.Object { return &DashboardSnapshot{} },
|
||||
func() runtime.Object { return &DashboardSnapshotList{} },
|
||||
utils.TableColumns{
|
||||
Definition: []metav1.TableColumnDefinition{
|
||||
{Name: "Name", Type: "string", Format: "name"},
|
||||
{Name: "Title", Type: "string", Format: "string", Description: "The snapshot name"},
|
||||
{Name: "Created At", Type: "date"},
|
||||
},
|
||||
Reader: func(obj any) ([]interface{}, error) {
|
||||
m, ok := obj.(*DashboardSnapshot)
|
||||
if ok {
|
||||
return []interface{}{
|
||||
m.Name,
|
||||
m.Spec.Title,
|
||||
m.CreationTimestamp.UTC().Format(time.RFC3339),
|
||||
}, nil
|
||||
}
|
||||
return nil, fmt.Errorf("expected snapshot")
|
||||
},
|
||||
}, // default table converter
|
||||
)
|
||||
|
||||
var (
|
||||
// SchemeGroupVersion is group version used to register these objects
|
||||
SchemeGroupVersion = schema.GroupVersion{Group: GROUP, Version: VERSION}
|
||||
)
|
||||
@@ -1,134 +0,0 @@
|
||||
package v0alpha1
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
|
||||
)
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
type DashboardSnapshot struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
// +optional
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
// Snapshot summary info
|
||||
Spec SnapshotInfo `json:"spec"`
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
type DashboardSnapshotList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
|
||||
Items []DashboardSnapshot `json:"items"`
|
||||
}
|
||||
|
||||
type SnapshotInfo struct {
|
||||
Title string `json:"title,omitempty"`
|
||||
// Optionally auto-remove the snapshot at a future date
|
||||
Expires int64 `json:"expires,omitempty"`
|
||||
// When set to true, the snapshot exists in a remote server
|
||||
External bool `json:"external,omitempty"`
|
||||
// The external URL where the snapshot can be seen
|
||||
ExternalURL string `json:"externalUrl,omitempty"`
|
||||
// The URL that created the dashboard originally
|
||||
OriginalUrl string `json:"originalUrl,omitempty"`
|
||||
// Snapshot creation timestamp
|
||||
Timestamp string `json:"timestamp,omitempty"`
|
||||
}
|
||||
|
||||
// This is returned from the POST command
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
type DashboardSnapshotWithDeleteKey struct {
|
||||
DashboardSnapshot `json:",inline"`
|
||||
|
||||
// The delete key is only returned when the item is created. It is not returned from a get request
|
||||
DeleteKey string `json:"deleteKey,omitempty"`
|
||||
}
|
||||
|
||||
// This is the snapshot returned from the subresource
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
type FullDashboardSnapshot struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
// +optional
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
// Snapshot summary info
|
||||
Info SnapshotInfo `json:"info"`
|
||||
|
||||
// The raw dashboard (unstructured for now)
|
||||
Dashboard common.Unstructured `json:"dashboard"`
|
||||
}
|
||||
|
||||
// Each tenant, may have different sharing options
|
||||
// This is currently set using custom.ini, but multi-tenant support will need
|
||||
// to be managed differently
|
||||
type SnapshotSharingOptions struct {
|
||||
SnapshotsEnabled bool `json:"snapshotEnabled"`
|
||||
ExternalSnapshotURL string `json:"externalSnapshotURL,omitempty"`
|
||||
ExternalSnapshotName string `json:"externalSnapshotName,omitempty"`
|
||||
ExternalEnabled bool `json:"externalEnabled,omitempty"`
|
||||
}
|
||||
|
||||
// These are the values expected to be sent from an end user
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
type DashboardCreateCommand struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
|
||||
// Snapshot name
|
||||
// required:false
|
||||
Name string `json:"name"`
|
||||
|
||||
// The complete dashboard model.
|
||||
// required:true
|
||||
Dashboard *common.Unstructured `json:"dashboard" binding:"Required"`
|
||||
|
||||
// When the snapshot should expire in seconds in seconds. Default is never to expire.
|
||||
// required:false
|
||||
// default:0
|
||||
Expires int64 `json:"expires"`
|
||||
|
||||
// these are passed when storing an external snapshot ref
|
||||
// Save the snapshot on an external server rather than locally.
|
||||
// required:false
|
||||
// default: false
|
||||
External bool `json:"external"`
|
||||
}
|
||||
|
||||
// The create response
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
type DashboardCreateResponse struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
|
||||
// The unique key
|
||||
Key string `json:"key"`
|
||||
|
||||
// A unique key that will allow delete
|
||||
DeleteKey string `json:"deleteKey"`
|
||||
|
||||
// Absolute URL to show the dashboard
|
||||
URL string `json:"url"`
|
||||
|
||||
// URL that will delete the response
|
||||
DeleteURL string `json:"deleteUrl"`
|
||||
}
|
||||
|
||||
// Represents an options object that must be named for each namespace/team/user
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
type SharingOptions struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
// Show the options inline
|
||||
Spec SnapshotSharingOptions `json:"spec"`
|
||||
}
|
||||
|
||||
// Represents a list of namespaced options
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
type SharingOptionsList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
|
||||
Items []SharingOptions `json:"items"`
|
||||
}
|
||||
@@ -1,271 +0,0 @@
|
||||
//go:build !ignore_autogenerated
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
// Code generated by deepcopy-gen. DO NOT EDIT.
|
||||
|
||||
package v0alpha1
|
||||
|
||||
import (
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *DashboardCreateCommand) DeepCopyInto(out *DashboardCreateCommand) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
if in.Dashboard != nil {
|
||||
in, out := &in.Dashboard, &out.Dashboard
|
||||
*out = (*in).DeepCopy()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DashboardCreateCommand.
|
||||
func (in *DashboardCreateCommand) DeepCopy() *DashboardCreateCommand {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(DashboardCreateCommand)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *DashboardCreateCommand) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *DashboardCreateResponse) DeepCopyInto(out *DashboardCreateResponse) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DashboardCreateResponse.
|
||||
func (in *DashboardCreateResponse) DeepCopy() *DashboardCreateResponse {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(DashboardCreateResponse)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *DashboardCreateResponse) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *DashboardSnapshot) DeepCopyInto(out *DashboardSnapshot) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
out.Spec = in.Spec
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DashboardSnapshot.
|
||||
func (in *DashboardSnapshot) DeepCopy() *DashboardSnapshot {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(DashboardSnapshot)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *DashboardSnapshot) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *DashboardSnapshotList) DeepCopyInto(out *DashboardSnapshotList) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = make([]DashboardSnapshot, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DashboardSnapshotList.
|
||||
func (in *DashboardSnapshotList) DeepCopy() *DashboardSnapshotList {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(DashboardSnapshotList)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *DashboardSnapshotList) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *DashboardSnapshotWithDeleteKey) DeepCopyInto(out *DashboardSnapshotWithDeleteKey) {
|
||||
*out = *in
|
||||
in.DashboardSnapshot.DeepCopyInto(&out.DashboardSnapshot)
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DashboardSnapshotWithDeleteKey.
|
||||
func (in *DashboardSnapshotWithDeleteKey) DeepCopy() *DashboardSnapshotWithDeleteKey {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(DashboardSnapshotWithDeleteKey)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *DashboardSnapshotWithDeleteKey) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *FullDashboardSnapshot) DeepCopyInto(out *FullDashboardSnapshot) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
out.Info = in.Info
|
||||
in.Dashboard.DeepCopyInto(&out.Dashboard)
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FullDashboardSnapshot.
|
||||
func (in *FullDashboardSnapshot) DeepCopy() *FullDashboardSnapshot {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(FullDashboardSnapshot)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *FullDashboardSnapshot) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *SharingOptions) DeepCopyInto(out *SharingOptions) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
out.Spec = in.Spec
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SharingOptions.
|
||||
func (in *SharingOptions) DeepCopy() *SharingOptions {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(SharingOptions)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *SharingOptions) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *SharingOptionsList) DeepCopyInto(out *SharingOptionsList) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = make([]SharingOptions, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SharingOptionsList.
|
||||
func (in *SharingOptionsList) DeepCopy() *SharingOptionsList {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(SharingOptionsList)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *SharingOptionsList) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *SnapshotInfo) DeepCopyInto(out *SnapshotInfo) {
|
||||
*out = *in
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SnapshotInfo.
|
||||
func (in *SnapshotInfo) DeepCopy() *SnapshotInfo {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(SnapshotInfo)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *SnapshotSharingOptions) DeepCopyInto(out *SnapshotSharingOptions) {
|
||||
*out = *in
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SnapshotSharingOptions.
|
||||
func (in *SnapshotSharingOptions) DeepCopy() *SnapshotSharingOptions {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(SnapshotSharingOptions)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
//go:build !ignore_autogenerated
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
// Code generated by defaulter-gen. DO NOT EDIT.
|
||||
|
||||
package v0alpha1
|
||||
|
||||
import (
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// RegisterDefaults adds defaulters functions to the given scheme.
|
||||
// Public to allow building arbitrary schemes.
|
||||
// All generated defaulters are covering - they call all nested defaulters.
|
||||
func RegisterDefaults(scheme *runtime.Scheme) error {
|
||||
return nil
|
||||
}
|
||||
@@ -1,521 +0,0 @@
|
||||
//go:build !ignore_autogenerated
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
// Code generated by openapi-gen. DO NOT EDIT.
|
||||
|
||||
package v0alpha1
|
||||
|
||||
import (
|
||||
common "k8s.io/kube-openapi/pkg/common"
|
||||
spec "k8s.io/kube-openapi/pkg/validation/spec"
|
||||
)
|
||||
|
||||
func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition {
|
||||
return map[string]common.OpenAPIDefinition{
|
||||
"github.com/grafana/grafana/pkg/apis/dashboardsnapshot/v0alpha1.DashboardCreateCommand": schema_pkg_apis_dashboardsnapshot_v0alpha1_DashboardCreateCommand(ref),
|
||||
"github.com/grafana/grafana/pkg/apis/dashboardsnapshot/v0alpha1.DashboardCreateResponse": schema_pkg_apis_dashboardsnapshot_v0alpha1_DashboardCreateResponse(ref),
|
||||
"github.com/grafana/grafana/pkg/apis/dashboardsnapshot/v0alpha1.DashboardSnapshot": schema_pkg_apis_dashboardsnapshot_v0alpha1_DashboardSnapshot(ref),
|
||||
"github.com/grafana/grafana/pkg/apis/dashboardsnapshot/v0alpha1.DashboardSnapshotList": schema_pkg_apis_dashboardsnapshot_v0alpha1_DashboardSnapshotList(ref),
|
||||
"github.com/grafana/grafana/pkg/apis/dashboardsnapshot/v0alpha1.DashboardSnapshotWithDeleteKey": schema_pkg_apis_dashboardsnapshot_v0alpha1_DashboardSnapshotWithDeleteKey(ref),
|
||||
"github.com/grafana/grafana/pkg/apis/dashboardsnapshot/v0alpha1.FullDashboardSnapshot": schema_pkg_apis_dashboardsnapshot_v0alpha1_FullDashboardSnapshot(ref),
|
||||
"github.com/grafana/grafana/pkg/apis/dashboardsnapshot/v0alpha1.SharingOptions": schema_pkg_apis_dashboardsnapshot_v0alpha1_SharingOptions(ref),
|
||||
"github.com/grafana/grafana/pkg/apis/dashboardsnapshot/v0alpha1.SharingOptionsList": schema_pkg_apis_dashboardsnapshot_v0alpha1_SharingOptionsList(ref),
|
||||
"github.com/grafana/grafana/pkg/apis/dashboardsnapshot/v0alpha1.SnapshotInfo": schema_pkg_apis_dashboardsnapshot_v0alpha1_SnapshotInfo(ref),
|
||||
"github.com/grafana/grafana/pkg/apis/dashboardsnapshot/v0alpha1.SnapshotSharingOptions": schema_pkg_apis_dashboardsnapshot_v0alpha1_SnapshotSharingOptions(ref),
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_dashboardsnapshot_v0alpha1_DashboardCreateCommand(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "These are the values expected to be sent from an end user",
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"kind": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"apiVersion": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"name": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Snapshot name required:false",
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"dashboard": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "The complete dashboard model. required:true",
|
||||
Ref: ref("github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.Unstructured"),
|
||||
},
|
||||
},
|
||||
"expires": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "When the snapshot should expire in seconds in seconds. Default is never to expire. required:false default:0",
|
||||
Default: 0,
|
||||
Type: []string{"integer"},
|
||||
Format: "int64",
|
||||
},
|
||||
},
|
||||
"external": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "these are passed when storing an external snapshot ref Save the snapshot on an external server rather than locally. required:false default: false",
|
||||
Default: false,
|
||||
Type: []string{"boolean"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"name", "dashboard", "expires", "external"},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
"github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.Unstructured"},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_dashboardsnapshot_v0alpha1_DashboardCreateResponse(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "The create response",
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"kind": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"apiVersion": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"key": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "The unique key",
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"deleteKey": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "A unique key that will allow delete",
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"url": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Absolute URL to show the dashboard",
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"deleteUrl": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "URL that will delete the response",
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"key", "deleteKey", "url", "deleteUrl"},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_dashboardsnapshot_v0alpha1_DashboardSnapshot(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"kind": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"apiVersion": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"metadata": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"),
|
||||
},
|
||||
},
|
||||
"spec": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Snapshot summary info",
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("github.com/grafana/grafana/pkg/apis/dashboardsnapshot/v0alpha1.SnapshotInfo"),
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"spec"},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
"github.com/grafana/grafana/pkg/apis/dashboardsnapshot/v0alpha1.SnapshotInfo", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_dashboardsnapshot_v0alpha1_DashboardSnapshotList(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"kind": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"apiVersion": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"metadata": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"),
|
||||
},
|
||||
},
|
||||
"items": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"array"},
|
||||
Items: &spec.SchemaOrArray{
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("github.com/grafana/grafana/pkg/apis/dashboardsnapshot/v0alpha1.DashboardSnapshot"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"items"},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
"github.com/grafana/grafana/pkg/apis/dashboardsnapshot/v0alpha1.DashboardSnapshot", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_dashboardsnapshot_v0alpha1_DashboardSnapshotWithDeleteKey(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "This is returned from the POST command",
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"kind": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"apiVersion": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"metadata": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"),
|
||||
},
|
||||
},
|
||||
"spec": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Snapshot summary info",
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("github.com/grafana/grafana/pkg/apis/dashboardsnapshot/v0alpha1.SnapshotInfo"),
|
||||
},
|
||||
},
|
||||
"deleteKey": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "The delete key is only returned when the item is created. It is not returned from a get request",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"spec"},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
"github.com/grafana/grafana/pkg/apis/dashboardsnapshot/v0alpha1.SnapshotInfo", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_dashboardsnapshot_v0alpha1_FullDashboardSnapshot(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "This is the snapshot returned from the subresource",
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"kind": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"apiVersion": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"metadata": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"),
|
||||
},
|
||||
},
|
||||
"info": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Snapshot summary info",
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("github.com/grafana/grafana/pkg/apis/dashboardsnapshot/v0alpha1.SnapshotInfo"),
|
||||
},
|
||||
},
|
||||
"dashboard": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "The raw dashboard (unstructured for now)",
|
||||
Ref: ref("github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.Unstructured"),
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"info", "dashboard"},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
"github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.Unstructured", "github.com/grafana/grafana/pkg/apis/dashboardsnapshot/v0alpha1.SnapshotInfo", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_dashboardsnapshot_v0alpha1_SharingOptions(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Represents an options object that must be named for each namespace/team/user",
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"kind": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"apiVersion": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"metadata": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"),
|
||||
},
|
||||
},
|
||||
"spec": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Show the options inline",
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("github.com/grafana/grafana/pkg/apis/dashboardsnapshot/v0alpha1.SnapshotSharingOptions"),
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"spec"},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
"github.com/grafana/grafana/pkg/apis/dashboardsnapshot/v0alpha1.SnapshotSharingOptions", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_dashboardsnapshot_v0alpha1_SharingOptionsList(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Represents a list of namespaced options",
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"kind": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"apiVersion": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"metadata": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"),
|
||||
},
|
||||
},
|
||||
"items": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"array"},
|
||||
Items: &spec.SchemaOrArray{
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("github.com/grafana/grafana/pkg/apis/dashboardsnapshot/v0alpha1.SharingOptions"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"items"},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
"github.com/grafana/grafana/pkg/apis/dashboardsnapshot/v0alpha1.SharingOptions", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_dashboardsnapshot_v0alpha1_SnapshotInfo(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"title": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"expires": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Optionally auto-remove the snapshot at a future date",
|
||||
Type: []string{"integer"},
|
||||
Format: "int64",
|
||||
},
|
||||
},
|
||||
"external": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "When set to true, the snapshot exists in a remote server",
|
||||
Type: []string{"boolean"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"externalUrl": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "The external URL where the snapshot can be seen",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"originalUrl": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "The URL that created the dashboard originally",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"timestamp": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Snapshot creation timestamp",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_dashboardsnapshot_v0alpha1_SnapshotSharingOptions(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Each tenant, may have different sharing options This is currently set using custom.ini, but multi-tenant support will need to be managed differently",
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"snapshotEnabled": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: false,
|
||||
Type: []string{"boolean"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"externalSnapshotURL": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"externalSnapshotName": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"externalEnabled": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"boolean"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"snapshotEnabled"},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
API rule violation: names_match,github.com/grafana/grafana/pkg/apis/dashboardsnapshot/v0alpha1,DashboardCreateResponse,DeleteURL
|
||||
API rule violation: names_match,github.com/grafana/grafana/pkg/apis/dashboardsnapshot/v0alpha1,SnapshotInfo,ExternalURL
|
||||
API rule violation: names_match,github.com/grafana/grafana/pkg/apis/dashboardsnapshot/v0alpha1,SnapshotSharingOptions,SnapshotsEnabled
|
||||
@@ -3,7 +3,6 @@ package apiregistry
|
||||
import (
|
||||
"github.com/grafana/grafana/pkg/registry/apis/collections"
|
||||
dashboardinternal "github.com/grafana/grafana/pkg/registry/apis/dashboard"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/dashboardsnapshot"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/datasource"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/folders"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/iam"
|
||||
@@ -21,7 +20,6 @@ type Service struct{}
|
||||
// and give each builder the chance to register itself with the main server
|
||||
func ProvideRegistryServiceSink(
|
||||
_ *dashboardinternal.DashboardsAPIBuilder,
|
||||
_ *dashboardsnapshot.SnapshotsAPIBuilder,
|
||||
_ *datasource.DataSourceAPIBuilder,
|
||||
_ *folders.FolderAPIBuilder,
|
||||
_ *iam.IdentityAccessManagementAPIBuilder,
|
||||
|
||||
@@ -33,6 +33,8 @@ func (b *DashboardsAPIBuilder) Mutate(ctx context.Context, a admission.Attribute
|
||||
|
||||
case dashboardV0.LIBRARY_PANEL_RESOURCE:
|
||||
return nil // nothing needed
|
||||
case dashboardV0.SNAPSHOT_RESOURCE:
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("unexpected resource: %+v", a.GetResource())
|
||||
|
||||
@@ -40,6 +40,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/dashboard/legacy"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/dashboard/legacysearcher"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/dashboard/snapshot"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver"
|
||||
grafanaauthorizer "github.com/grafana/grafana/pkg/services/apiserver/auth/authorizer"
|
||||
@@ -48,6 +49,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
dashsvc "github.com/grafana/grafana/pkg/services/dashboards/service"
|
||||
"github.com/grafana/grafana/pkg/services/dashboardsnapshots"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/libraryelements"
|
||||
@@ -114,8 +116,10 @@ type DashboardsAPIBuilder struct {
|
||||
folderClientProvider client.K8sHandlerProvider
|
||||
libraryPanels libraryelements.Service // for legacy library panels
|
||||
publicDashboardService publicdashboards.Service
|
||||
|
||||
isStandalone bool // skips any handling including anything to do with legacy storage
|
||||
snapshotService dashboardsnapshots.Service
|
||||
snapshotOptions dashv0.SnapshotSharingOptions
|
||||
namespacer request.NamespaceMapper
|
||||
isStandalone bool // skips any handling including anything to do with legacy storage
|
||||
}
|
||||
|
||||
func RegisterAPIService(
|
||||
@@ -143,12 +147,20 @@ func RegisterAPIService(
|
||||
userService user.Service,
|
||||
libraryPanels libraryelements.Service,
|
||||
publicDashboardService publicdashboards.Service,
|
||||
snapshotService dashboardsnapshots.Service,
|
||||
) *DashboardsAPIBuilder {
|
||||
dbp := legacysql.NewDatabaseProvider(sql)
|
||||
namespacer := request.GetNamespaceMapper(cfg)
|
||||
legacyDashboardSearcher := legacysearcher.NewDashboardSearchClient(dashStore, sorter)
|
||||
folderClient := client.NewK8sHandler(dual, request.GetNamespaceMapper(cfg), folders.FolderResourceInfo.GroupVersionResource(), restConfigProvider.GetRestConfig, dashStore, userService, unified, sorter, features)
|
||||
|
||||
snapshotOptions := dashv0.SnapshotSharingOptions{
|
||||
SnapshotsEnabled: cfg.SnapshotEnabled,
|
||||
ExternalSnapshotURL: cfg.ExternalSnapshotUrl,
|
||||
ExternalSnapshotName: cfg.ExternalSnapshotName,
|
||||
ExternalEnabled: cfg.ExternalEnabled,
|
||||
}
|
||||
|
||||
builder := &DashboardsAPIBuilder{
|
||||
dashboardService: dashboardService,
|
||||
dashboardPermissions: dashboardPermissions,
|
||||
@@ -167,7 +179,9 @@ func RegisterAPIService(
|
||||
folderClientProvider: newSimpleFolderClientProvider(folderClient),
|
||||
libraryPanels: libraryPanels,
|
||||
publicDashboardService: publicDashboardService,
|
||||
|
||||
snapshotService: snapshotService,
|
||||
snapshotOptions: snapshotOptions,
|
||||
namespacer: namespacer,
|
||||
legacy: &DashboardStorage{
|
||||
Access: legacy.NewDashboardSQLAccess(dbp, namespacer, dashStore, provisioning, libraryPanelSvc, sorter, dashboardPermissionsSvc, accessControl, features),
|
||||
DashboardService: dashboardService,
|
||||
@@ -244,6 +258,7 @@ func (b *DashboardsAPIBuilder) AllowedV0Alpha1Resources() []string {
|
||||
return []string{
|
||||
dashv0.DashboardKind().Plural(),
|
||||
dashv0.LIBRARY_PANEL_RESOURCE,
|
||||
dashv0.SNAPSHOT_RESOURCE,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -267,6 +282,8 @@ func (b *DashboardsAPIBuilder) Validate(ctx context.Context, a admission.Attribu
|
||||
|
||||
case dashv0.LIBRARY_PANEL_RESOURCE:
|
||||
return nil // OK for now
|
||||
case dashv0.SNAPSHOT_RESOURCE:
|
||||
return nil // OK for now
|
||||
}
|
||||
|
||||
return fmt.Errorf("unsupported validation: %+v", a.GetResource())
|
||||
@@ -535,6 +552,7 @@ func (b *DashboardsAPIBuilder) UpdateAPIGroupInfo(apiGroupInfo *genericapiserver
|
||||
if err := b.storageForVersion(apiGroupInfo, opts, largeObjects,
|
||||
dashv0.DashboardResourceInfo,
|
||||
&dashv0.LibraryPanelResourceInfo,
|
||||
&dashv0.SnapshotResourceInfo,
|
||||
func(obj runtime.Object, access *internal.DashboardAccess) (v runtime.Object, err error) {
|
||||
dto := &dashv0.DashboardWithAccessInfo{}
|
||||
dash, ok := obj.(*dashv0.Dashboard)
|
||||
@@ -553,6 +571,7 @@ func (b *DashboardsAPIBuilder) UpdateAPIGroupInfo(apiGroupInfo *genericapiserver
|
||||
if err := b.storageForVersion(apiGroupInfo, opts, largeObjects,
|
||||
dashv1.DashboardResourceInfo,
|
||||
nil, // do not register library panel
|
||||
nil,
|
||||
func(obj runtime.Object, access *internal.DashboardAccess) (v runtime.Object, err error) {
|
||||
dto := &dashv1.DashboardWithAccessInfo{}
|
||||
dash, ok := obj.(*dashv1.Dashboard)
|
||||
@@ -571,6 +590,7 @@ func (b *DashboardsAPIBuilder) UpdateAPIGroupInfo(apiGroupInfo *genericapiserver
|
||||
if err := b.storageForVersion(apiGroupInfo, opts, largeObjects,
|
||||
dashv2alpha1.DashboardResourceInfo,
|
||||
nil, // do not register library panel
|
||||
nil,
|
||||
func(obj runtime.Object, access *internal.DashboardAccess) (v runtime.Object, err error) {
|
||||
dto := &dashv2alpha1.DashboardWithAccessInfo{}
|
||||
dash, ok := obj.(*dashv2alpha1.Dashboard)
|
||||
@@ -588,6 +608,7 @@ func (b *DashboardsAPIBuilder) UpdateAPIGroupInfo(apiGroupInfo *genericapiserver
|
||||
if err := b.storageForVersion(apiGroupInfo, opts, largeObjects,
|
||||
dashv2beta1.DashboardResourceInfo,
|
||||
nil, // do not register library panel
|
||||
nil,
|
||||
func(obj runtime.Object, access *internal.DashboardAccess) (v runtime.Object, err error) {
|
||||
dto := &dashv2beta1.DashboardWithAccessInfo{}
|
||||
dash, ok := obj.(*dashv2beta1.Dashboard)
|
||||
@@ -611,6 +632,7 @@ func (b *DashboardsAPIBuilder) storageForVersion(
|
||||
largeObjects apistore.LargeObjectSupport,
|
||||
dashboards utils.ResourceInfo,
|
||||
libraryPanels *utils.ResourceInfo,
|
||||
snapshots *utils.ResourceInfo,
|
||||
newDTOFunc dtoBuilder,
|
||||
) error {
|
||||
// Register the versioned storage
|
||||
@@ -685,6 +707,20 @@ func (b *DashboardsAPIBuilder) storageForVersion(
|
||||
}
|
||||
}
|
||||
|
||||
// Legacy only (for now) and only v0alpha1
|
||||
if snapshots != nil && dashboards.GroupVersion().Version == "v0alpha1" {
|
||||
snapshotLegacyStore := &snapshot.SnapshotLegacyStore{
|
||||
ResourceInfo: *snapshots,
|
||||
Service: b.snapshotService,
|
||||
Namespacer: b.namespacer,
|
||||
Options: b.snapshotOptions,
|
||||
}
|
||||
storage[snapshots.StoragePath()] = snapshotLegacyStore
|
||||
storage[snapshots.StoragePath("dashboard")], err = snapshot.NewDashboardREST(dashboards, b.snapshotService)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -782,7 +818,12 @@ func (b *DashboardsAPIBuilder) GetAPIRoutes(gv schema.GroupVersion) *builder.API
|
||||
}
|
||||
|
||||
defs := b.GetOpenAPIDefinitions()(func(path string) spec.Ref { return spec.Ref{} })
|
||||
return b.search.GetAPIRoutes(defs)
|
||||
searchAPIRoutes := b.search.GetAPIRoutes(defs)
|
||||
snapshotAPIRoutes := snapshot.GetRoutes(b.snapshotService, b.snapshotOptions, defs)
|
||||
|
||||
return &builder.APIRoutes{
|
||||
Namespace: append(searchAPIRoutes.Namespace, snapshotAPIRoutes.Namespace...),
|
||||
}
|
||||
}
|
||||
|
||||
// The default authorizer is fine because authorization happens in storage where we know the parent folder
|
||||
|
||||
+31
-22
@@ -1,4 +1,4 @@
|
||||
package dashboardsnapshot
|
||||
package snapshot
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -6,31 +6,38 @@ import (
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
dashV0 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
dashboardsnapshot "github.com/grafana/grafana/pkg/apis/dashboardsnapshot/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
|
||||
"github.com/grafana/grafana/pkg/services/dashboardsnapshots"
|
||||
)
|
||||
|
||||
func convertDTOToSnapshot(v *dashboardsnapshots.DashboardSnapshotDTO, namespacer request.NamespaceMapper) *dashboardsnapshot.DashboardSnapshot {
|
||||
func convertSnapshotDTOToK8sResource(v *dashboardsnapshots.DashboardSnapshotDTO, namespacer request.NamespaceMapper) *dashV0.Snapshot {
|
||||
expires := v.Expires.UnixMilli()
|
||||
if v.Expires.After(time.Date(2070, time.January, 0, 0, 0, 0, 0, time.UTC)) {
|
||||
expires = 0 // ignore things expiring long into the future
|
||||
}
|
||||
snap := &dashboardsnapshot.DashboardSnapshot{
|
||||
TypeMeta: resourceInfo.TypeMeta(),
|
||||
snap := &dashV0.Snapshot{
|
||||
TypeMeta: dashV0.SnapshotResourceInfo.TypeMeta(),
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: v.Key,
|
||||
ResourceVersion: fmt.Sprintf("%d", v.Updated.UnixMilli()),
|
||||
CreationTimestamp: metav1.NewTime(v.Created),
|
||||
Namespace: namespacer(v.OrgID),
|
||||
},
|
||||
Spec: dashboardsnapshot.SnapshotInfo{
|
||||
Title: v.Name,
|
||||
ExternalURL: v.ExternalURL,
|
||||
Expires: expires,
|
||||
Spec: dashV0.SnapshotSpec{
|
||||
Title: &v.Name,
|
||||
},
|
||||
}
|
||||
|
||||
// Only show external settings when it is external
|
||||
if v.External {
|
||||
snap.Spec.External = &v.External
|
||||
snap.Spec.ExternalUrl = &v.ExternalURL
|
||||
}
|
||||
if expires > 0 {
|
||||
snap.Spec.Expires = &expires
|
||||
}
|
||||
if v.Updated != v.Created {
|
||||
meta, _ := utils.MetaAccessor(snap)
|
||||
meta.SetUpdatedTimestamp(&v.Updated)
|
||||
@@ -38,31 +45,33 @@ func convertDTOToSnapshot(v *dashboardsnapshots.DashboardSnapshotDTO, namespacer
|
||||
return snap
|
||||
}
|
||||
|
||||
func convertSnapshotToK8sResource(v *dashboardsnapshots.DashboardSnapshot, namespacer request.NamespaceMapper) *dashboardsnapshot.DashboardSnapshot {
|
||||
func convertSnapshotToK8sResource(v *dashboardsnapshots.DashboardSnapshot, namespacer request.NamespaceMapper) *dashV0.Snapshot {
|
||||
expires := v.Expires.UnixMilli()
|
||||
if v.Expires.After(time.Date(2070, time.January, 0, 0, 0, 0, 0, time.UTC)) {
|
||||
expires = 0 // ignore things expiring long into the future
|
||||
}
|
||||
|
||||
info := dashboardsnapshot.SnapshotInfo{
|
||||
Title: v.Name,
|
||||
ExternalURL: v.ExternalURL,
|
||||
Expires: expires,
|
||||
}
|
||||
s := v.Dashboard.Get("snapshot")
|
||||
if s != nil {
|
||||
info.OriginalUrl, _ = s.Get("originalUrl").String()
|
||||
info.Timestamp, _ = s.Get("timestamp").String()
|
||||
}
|
||||
snap := &dashboardsnapshot.DashboardSnapshot{
|
||||
snap := &dashV0.Snapshot{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: v.Key,
|
||||
ResourceVersion: fmt.Sprintf("%d", v.Updated.UnixMilli()),
|
||||
CreationTimestamp: metav1.NewTime(v.Created),
|
||||
Namespace: namespacer(v.OrgID),
|
||||
},
|
||||
Spec: info,
|
||||
Spec: dashV0.SnapshotSpec{
|
||||
Title: &v.Name,
|
||||
},
|
||||
}
|
||||
|
||||
// Only show external settings when it is external
|
||||
if v.External {
|
||||
snap.Spec.External = &v.External
|
||||
snap.Spec.ExternalUrl = &v.ExternalURL
|
||||
}
|
||||
if expires > 0 {
|
||||
snap.Spec.Expires = &expires
|
||||
}
|
||||
|
||||
if v.Updated != v.Created {
|
||||
meta, _ := utils.MetaAccessor(snap)
|
||||
meta.SetUpdatedTimestamp(&v.Updated)
|
||||
+19
-207
@@ -1,168 +1,36 @@
|
||||
package dashboardsnapshot
|
||||
package snapshot
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"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"
|
||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
genericapiserver "k8s.io/apiserver/pkg/server"
|
||||
common "k8s.io/kube-openapi/pkg/common"
|
||||
"k8s.io/kube-openapi/pkg/common"
|
||||
"k8s.io/kube-openapi/pkg/spec3"
|
||||
"k8s.io/kube-openapi/pkg/validation/spec"
|
||||
|
||||
claims "github.com/grafana/authlib/types"
|
||||
authlib "github.com/grafana/authlib/types"
|
||||
dashv0 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
dashboardsnapshot "github.com/grafana/grafana/pkg/apis/dashboardsnapshot/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/infra/db"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/builder"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
|
||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||
"github.com/grafana/grafana/pkg/services/dashboardsnapshots"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
"github.com/grafana/grafana/pkg/util/errhttp"
|
||||
"github.com/grafana/grafana/pkg/web"
|
||||
)
|
||||
|
||||
var (
|
||||
_ builder.APIGroupBuilder = (*SnapshotsAPIBuilder)(nil)
|
||||
_ builder.OpenAPIPostProcessor = (*SnapshotsAPIBuilder)(nil)
|
||||
_ builder.APIGroupRouteProvider = (*SnapshotsAPIBuilder)(nil)
|
||||
)
|
||||
func GetRoutes(service dashboardsnapshots.Service, options dashv0.SnapshotSharingOptions, defs map[string]common.OpenAPIDefinition) *builder.APIRoutes {
|
||||
prefix := dashv0.SnapshotResourceInfo.GroupResource().Resource
|
||||
tags := []string{dashv0.SnapshotResourceInfo.GroupVersionKind().Kind}
|
||||
|
||||
var resourceInfo = dashboardsnapshot.DashboardSnapshotResourceInfo
|
||||
|
||||
// This is used just so wire has something unique to return
|
||||
type SnapshotsAPIBuilder struct {
|
||||
service dashboardsnapshots.Service
|
||||
namespacer request.NamespaceMapper
|
||||
options sharingOptionsGetter
|
||||
exporter *dashExporter
|
||||
logger log.Logger
|
||||
}
|
||||
|
||||
func NewSnapshotsAPIBuilder(
|
||||
p dashboardsnapshots.Service,
|
||||
cfg *setting.Cfg,
|
||||
exporter *dashExporter,
|
||||
) *SnapshotsAPIBuilder {
|
||||
return &SnapshotsAPIBuilder{
|
||||
service: p,
|
||||
options: newSharingOptionsGetter(cfg),
|
||||
namespacer: request.GetNamespaceMapper(cfg),
|
||||
exporter: exporter,
|
||||
logger: log.New("snapshots::RawHandlers"),
|
||||
}
|
||||
}
|
||||
|
||||
func RegisterAPIService(
|
||||
service dashboardsnapshots.Service,
|
||||
apiregistration builder.APIRegistrar,
|
||||
cfg *setting.Cfg,
|
||||
features featuremgmt.FeatureToggles,
|
||||
sql db.DB,
|
||||
reg prometheus.Registerer,
|
||||
) *SnapshotsAPIBuilder {
|
||||
//nolint:staticcheck // not yet migrated to OpenFeature
|
||||
if !features.IsEnabledGlobally(featuremgmt.FlagGrafanaAPIServerWithExperimentalAPIs) {
|
||||
return nil // skip registration unless opting into experimental apis
|
||||
}
|
||||
builder := NewSnapshotsAPIBuilder(service, cfg, &dashExporter{
|
||||
service: service,
|
||||
sql: sql,
|
||||
})
|
||||
apiregistration.RegisterAPI(builder)
|
||||
return builder
|
||||
}
|
||||
|
||||
func (b *SnapshotsAPIBuilder) GetGroupVersion() schema.GroupVersion {
|
||||
return resourceInfo.GroupVersion()
|
||||
}
|
||||
|
||||
func addKnownTypes(scheme *runtime.Scheme, gv schema.GroupVersion) {
|
||||
scheme.AddKnownTypes(gv,
|
||||
&dashboardsnapshot.DashboardSnapshot{},
|
||||
&dashboardsnapshot.DashboardSnapshotList{},
|
||||
&dashboardsnapshot.SharingOptions{},
|
||||
&dashboardsnapshot.SharingOptionsList{},
|
||||
&dashboardsnapshot.FullDashboardSnapshot{},
|
||||
&dashboardsnapshot.DashboardSnapshotWithDeleteKey{},
|
||||
&metav1.Status{},
|
||||
)
|
||||
}
|
||||
|
||||
func (b *SnapshotsAPIBuilder) InstallSchema(scheme *runtime.Scheme) error {
|
||||
gv := resourceInfo.GroupVersion()
|
||||
addKnownTypes(scheme, gv)
|
||||
|
||||
// Link this version to the internal representation.
|
||||
// This is used for server-side-apply (PATCH), and avoids the error:
|
||||
// "no kind is registered for the type"
|
||||
addKnownTypes(scheme, schema.GroupVersion{
|
||||
Group: gv.Group,
|
||||
Version: runtime.APIVersionInternal,
|
||||
})
|
||||
|
||||
// If multiple versions exist, then register conversions from zz_generated.conversion.go
|
||||
// if err := playlist.RegisterConversions(scheme); err != nil {
|
||||
// return err
|
||||
// }
|
||||
metav1.AddToGroupVersion(scheme, gv)
|
||||
return scheme.SetVersionPriority(gv)
|
||||
}
|
||||
|
||||
func (b *SnapshotsAPIBuilder) AllowedV0Alpha1Resources() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *SnapshotsAPIBuilder) UpdateAPIGroupInfo(apiGroupInfo *genericapiserver.APIGroupInfo, _ builder.APIGroupOptions) error {
|
||||
storage := map[string]rest.Storage{}
|
||||
|
||||
legacyStore := &legacyStorage{
|
||||
service: b.service,
|
||||
namespacer: b.namespacer,
|
||||
options: b.options,
|
||||
}
|
||||
legacyStore.tableConverter = resourceInfo.TableConverter()
|
||||
storage[resourceInfo.StoragePath()] = legacyStore
|
||||
storage[resourceInfo.StoragePath("body")] = &subBodyREST{
|
||||
service: b.service,
|
||||
namespacer: b.namespacer,
|
||||
}
|
||||
|
||||
storage["options"] = &optionsStorage{
|
||||
getter: b.options,
|
||||
tableConverter: legacyStore.tableConverter,
|
||||
}
|
||||
|
||||
apiGroupInfo.VersionedResourcesStorageMap[dashboardsnapshot.VERSION] = storage
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *SnapshotsAPIBuilder) GetOpenAPIDefinitions() common.GetOpenAPIDefinitions {
|
||||
return dashboardsnapshot.GetOpenAPIDefinitions
|
||||
}
|
||||
|
||||
// Register additional routes with the server
|
||||
func (b *SnapshotsAPIBuilder) GetAPIRoutes(gv schema.GroupVersion) *builder.APIRoutes {
|
||||
prefix := dashboardsnapshot.DashboardSnapshotResourceInfo.GroupResource().Resource
|
||||
defs := dashboardsnapshot.GetOpenAPIDefinitions(func(path string) spec.Ref { return spec.Ref{} })
|
||||
createCmd := defs["github.com/grafana/grafana/apps/dashboard/pkg/apissnapshot/v0alpha1.DashboardCreateCommand"].Schema
|
||||
createExample := `{"dashboard":{"annotations":{"list":[{"name":"Annotations & Alerts","enable":true,"iconColor":"rgba(0, 211, 255, 1)","snapshotData":[],"type":"dashboard","builtIn":1,"hide":true}]},"editable":true,"fiscalYearStartMonth":0,"graphTooltip":0,"id":203,"links":[],"liveNow":false,"panels":[{"datasource":null,"fieldConfig":{"defaults":{"color":{"mode":"palette-classic"},"custom":{"axisBorderShow":false,"axisCenteredZero":false,"axisColorMode":"text","axisLabel":"","axisPlacement":"auto","barAlignment":0,"drawStyle":"line","fillOpacity":43,"gradientMode":"opacity","hideFrom":{"legend":false,"tooltip":false,"viz":false},"insertNulls":false,"lineInterpolation":"smooth","lineWidth":1,"pointSize":5,"scaleDistribution":{"type":"linear"},"showPoints":"auto","spanNulls":false,"stacking":{"group":"A","mode":"none"},"thresholdsStyle":{"mode":"off"}},"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null},{"color":"red","value":80}]},"unitScale":true},"overrides":[]},"gridPos":{"h":8,"w":12,"x":0,"y":0},"id":1,"options":{"legend":{"calcs":[],"displayMode":"list","placement":"bottom","showLegend":true},"tooltip":{"mode":"single","sort":"none"}},"pluginVersion":"10.4.0-pre","snapshotData":[{"fields":[{"config":{"color":{"mode":"palette-classic"},"custom":{"axisBorderShow":false,"axisCenteredZero":false,"axisColorMode":"text","axisPlacement":"auto","barAlignment":0,"drawStyle":"line","fillOpacity":43,"gradientMode":"opacity","hideFrom":{"legend":false,"tooltip":false,"viz":false},"lineInterpolation":"smooth","lineWidth":1,"pointSize":5,"showPoints":"auto","thresholdsStyle":{"mode":"off"}},"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null},{"color":"red","value":80}]},"unitScale":true},"name":"time","type":"time","values":[1706030536378,1706034856378,1706039176378,1706043496378,1706047816378,1706052136378]},{"config":{"color":{"mode":"palette-classic"},"custom":{"axisBorderShow":false,"axisCenteredZero":false,"axisColorMode":"text","axisLabel":"","axisPlacement":"auto","barAlignment":0,"drawStyle":"line","fillOpacity":43,"gradientMode":"opacity","hideFrom":{"legend":false,"tooltip":false,"viz":false},"insertNulls":false,"lineInterpolation":"smooth","lineWidth":1,"pointSize":5,"scaleDistribution":{"type":"linear"},"showPoints":"auto","spanNulls":false,"stacking":{"group":"A","mode":"none"},"thresholdsStyle":{"mode":"off"}},"mappings":[],"thresholds":{"mode":"absolute","steps":[{"color":"green","value":null},{"color":"red","value":80}]},"unitScale":true},"name":"A-series","type":"number","values":[1,20,90,30,50,0]}],"refId":"A"}],"targets":[],"title":"Simple example","type":"timeseries","links":[]}],"refresh":"","schemaVersion":39,"snapshot":{"timestamp":"2024-01-23T23:22:16.377Z"},"tags":[],"templating":{"list":[]},"time":{"from":"2024-01-23T17:22:20.380Z","to":"2024-01-23T23:22:20.380Z","raw":{"from":"now-6h","to":"now"}},"timepicker":{},"timezone":"","title":"simple and small","uid":"b22ec8db-399b-403b-b6c7-b0fb30ccb2a5","version":1,"weekStart":""},"name":"simple and small","expires":86400}`
|
||||
createRsp := defs["github.com/grafana/grafana/apps/dashboard/pkg/apissnapshot/v0alpha1.DashboardCreateResponse"].Schema
|
||||
|
||||
tags := []string{dashboardsnapshot.DashboardSnapshotResourceInfo.GroupVersionKind().Kind}
|
||||
routes := &builder.APIRoutes{
|
||||
return &builder.APIRoutes{
|
||||
Namespace: []builder.APIRouteHandler{
|
||||
{
|
||||
Path: prefix + "/create",
|
||||
@@ -172,16 +40,16 @@ func (b *SnapshotsAPIBuilder) GetAPIRoutes(gv schema.GroupVersion) *builder.APIR
|
||||
Extensions: map[string]any{
|
||||
"x-grafana-action": "create",
|
||||
"x-kubernetes-group-version-kind": metav1.GroupVersionKind{
|
||||
Group: dashboardsnapshot.GROUP,
|
||||
Version: dashboardsnapshot.VERSION,
|
||||
Group: dashv0.GROUP,
|
||||
Version: dashv0.VERSION,
|
||||
Kind: "DashboardCreateResponse",
|
||||
},
|
||||
},
|
||||
},
|
||||
OperationProps: spec3.OperationProps{
|
||||
Tags: tags,
|
||||
Summary: "Full dashboard",
|
||||
Description: "longer description here?",
|
||||
OperationId: "createSnapshot",
|
||||
Description: "Creates a new Snapshot",
|
||||
Parameters: []*spec3.Parameter{
|
||||
{
|
||||
ParameterProps: spec3.ParameterProps{
|
||||
@@ -233,7 +101,6 @@ func (b *SnapshotsAPIBuilder) GetAPIRoutes(gv schema.GroupVersion) *builder.APIR
|
||||
return
|
||||
}
|
||||
wrap := &contextmodel.ReqContext{
|
||||
Logger: b.logger,
|
||||
Context: &web.Context{
|
||||
Req: r,
|
||||
Resp: web.NewResponseWriter(r.Method, w),
|
||||
@@ -242,7 +109,7 @@ func (b *SnapshotsAPIBuilder) GetAPIRoutes(gv schema.GroupVersion) *builder.APIR
|
||||
}
|
||||
|
||||
vars := mux.Vars(r)
|
||||
info, err := claims.ParseNamespace(vars["namespace"])
|
||||
info, err := authlib.ParseNamespace(vars["namespace"])
|
||||
if err != nil {
|
||||
wrap.JsonApiErr(http.StatusBadRequest, "expected namespace", nil)
|
||||
return
|
||||
@@ -259,24 +126,18 @@ func (b *SnapshotsAPIBuilder) GetAPIRoutes(gv schema.GroupVersion) *builder.APIR
|
||||
return
|
||||
}
|
||||
|
||||
opts, err := b.options(info.Value)
|
||||
if err != nil {
|
||||
wrap.JsonApiErr(http.StatusBadRequest, "error getting options", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Use the existing snapshot service
|
||||
dashboardsnapshots.CreateDashboardSnapshot(wrap, opts.Spec, cmd, b.service)
|
||||
dashboardsnapshots.CreateDashboardSnapshot(wrap, options, cmd, service)
|
||||
},
|
||||
},
|
||||
{
|
||||
Path: prefix + "/delete/{deleteKey}",
|
||||
Spec: &spec3.PathProps{
|
||||
Summary: "an example at the root level",
|
||||
Description: "longer description here?",
|
||||
Description: "Delete snapshot by delete key",
|
||||
Delete: &spec3.Operation{
|
||||
OperationProps: spec3.OperationProps{
|
||||
Tags: tags,
|
||||
Tags: tags,
|
||||
OperationId: "deleteWithKey",
|
||||
Parameters: []*spec3.Parameter{
|
||||
{
|
||||
ParameterProps: spec3.ParameterProps{
|
||||
@@ -296,7 +157,7 @@ func (b *SnapshotsAPIBuilder) GetAPIRoutes(gv schema.GroupVersion) *builder.APIR
|
||||
vars := mux.Vars(r)
|
||||
key := vars["deleteKey"]
|
||||
|
||||
err := dashboardsnapshots.DeleteWithKey(ctx, key, b.service)
|
||||
err := dashboardsnapshots.DeleteWithKey(ctx, key, service)
|
||||
if err != nil {
|
||||
errhttp.Write(ctx, fmt.Errorf("failed to delete external dashboard (%w)", err), w)
|
||||
return
|
||||
@@ -306,54 +167,5 @@ func (b *SnapshotsAPIBuilder) GetAPIRoutes(gv schema.GroupVersion) *builder.APIR
|
||||
})
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// dev environment to export all snapshots to a blob store
|
||||
if b.exporter != nil && false {
|
||||
routes.Root = append(routes.Root, b.exporter.getAPIRouteHandler())
|
||||
}
|
||||
return routes
|
||||
}
|
||||
|
||||
func (b *SnapshotsAPIBuilder) GetAuthorizer() authorizer.Authorizer {
|
||||
// TODO: this behavior must match the existing logic (it is currently more restrictive)
|
||||
//
|
||||
// https://github.com/grafana/grafana/blob/f63e43c113ac0cf8f78ed96ee2953874139bd2dc/pkg/middleware/auth.go#L203
|
||||
// func SnapshotPublicModeOrSignedIn(cfg *setting.Cfg) web.Handler {
|
||||
// return func(c *contextmodel.ReqContext) {
|
||||
// if cfg.SnapshotPublicMode {
|
||||
// return
|
||||
// }
|
||||
|
||||
// if !c.IsSignedIn {
|
||||
// notAuthorized(c)
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
return authorizer.AuthorizerFunc(
|
||||
func(ctx context.Context, attr authorizer.Attributes) (authorized authorizer.Decision, reason string, err error) {
|
||||
// Everyone can view dashsnaps
|
||||
if attr.GetVerb() == "get" && attr.GetResource() == dashboardsnapshot.DashboardSnapshotResourceInfo.GroupResource().Resource {
|
||||
return authorizer.DecisionAllow, "", err
|
||||
}
|
||||
|
||||
// Fallback to the default behaviors (namespace matches org)
|
||||
return authorizer.DecisionNoOpinion, "", err
|
||||
})
|
||||
}
|
||||
|
||||
func (b *SnapshotsAPIBuilder) PostProcessOpenAPI(oas *spec3.OpenAPI) (*spec3.OpenAPI, error) {
|
||||
oas.Info.Description = "A dashboard snapshot shares an interactive dashboard publicly."
|
||||
|
||||
// Set a description on the
|
||||
sub := oas.Paths.Paths["/apis/dashboardsnapshot.grafana.app/v0alpha1/namespaces/{namespace}/dashboardsnapshots/{name}/body"]
|
||||
if sub != nil && sub.Get != nil {
|
||||
sub.Get.Summary = "Full dashboard"
|
||||
sub.Get.Description = "Read the full dashboard body"
|
||||
}
|
||||
|
||||
return oas, nil
|
||||
}}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
package snapshot
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"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/rest"
|
||||
|
||||
dashV0 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
|
||||
"github.com/grafana/grafana/pkg/services/dashboardsnapshots"
|
||||
)
|
||||
|
||||
var (
|
||||
_ rest.Scoper = (*SnapshotLegacyStore)(nil)
|
||||
_ rest.SingularNameProvider = (*SnapshotLegacyStore)(nil)
|
||||
_ rest.Getter = (*SnapshotLegacyStore)(nil)
|
||||
_ rest.Lister = (*SnapshotLegacyStore)(nil)
|
||||
_ rest.GracefulDeleter = (*SnapshotLegacyStore)(nil)
|
||||
_ rest.Storage = (*SnapshotLegacyStore)(nil)
|
||||
)
|
||||
|
||||
type SnapshotLegacyStore struct {
|
||||
ResourceInfo utils.ResourceInfo
|
||||
Service dashboardsnapshots.Service
|
||||
Namespacer request.NamespaceMapper
|
||||
Options dashV0.SnapshotSharingOptions
|
||||
}
|
||||
|
||||
func (s *SnapshotLegacyStore) New() runtime.Object {
|
||||
return s.ResourceInfo.NewFunc()
|
||||
}
|
||||
|
||||
func (s *SnapshotLegacyStore) Destroy() {}
|
||||
|
||||
func (s *SnapshotLegacyStore) NamespaceScoped() bool {
|
||||
return true // namespace == org
|
||||
}
|
||||
|
||||
func (s *SnapshotLegacyStore) GetSingularName() string {
|
||||
return s.ResourceInfo.GetSingularName()
|
||||
}
|
||||
|
||||
func (s *SnapshotLegacyStore) NewList() runtime.Object {
|
||||
return s.ResourceInfo.NewListFunc()
|
||||
}
|
||||
|
||||
func (s *SnapshotLegacyStore) ConvertToTable(ctx context.Context, object runtime.Object, tableOptions runtime.Object) (*metav1.Table, error) {
|
||||
return s.ResourceInfo.TableConverter().ConvertToTable(ctx, object, tableOptions)
|
||||
}
|
||||
|
||||
// GracefulDeleter
|
||||
func (s *SnapshotLegacyStore) Delete(ctx context.Context, name string, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions) (runtime.Object, bool, error) {
|
||||
snap, err := s.Service.GetDashboardSnapshot(ctx, &dashboardsnapshots.GetDashboardSnapshotQuery{
|
||||
Key: name,
|
||||
})
|
||||
if err != nil || snap == nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
// Delete the external one first
|
||||
if snap.ExternalDeleteURL != "" {
|
||||
err := dashboardsnapshots.DeleteExternalDashboardSnapshot(snap.ExternalDeleteURL)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
}
|
||||
|
||||
err = s.Service.DeleteDashboardSnapshot(ctx, &dashboardsnapshots.DeleteDashboardSnapshotCommand{
|
||||
DeleteKey: snap.DeleteKey,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
return nil, true, nil
|
||||
}
|
||||
|
||||
func (s *SnapshotLegacyStore) List(ctx context.Context, options *internalversion.ListOptions) (runtime.Object, error) {
|
||||
orgId, err := request.OrgIDForList(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
requester, err := identity.GetRequester(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
limit := options.Limit
|
||||
if limit < 1 {
|
||||
limit = 1000
|
||||
}
|
||||
|
||||
searchQuery := dashboardsnapshots.GetDashboardSnapshotsQuery{
|
||||
Name: "", // TODO: Should we support searching by name? In the levacy api is a string query param called query
|
||||
Limit: int(limit),
|
||||
OrgID: orgId,
|
||||
SignedInUser: requester,
|
||||
}
|
||||
|
||||
res, err := s.Service.SearchDashboardSnapshots(ctx, &searchQuery)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
list := &dashV0.SnapshotList{}
|
||||
//convert
|
||||
for idx := range res {
|
||||
list.Items = append(list.Items, *convertSnapshotDTOToK8sResource(res[idx], s.Namespacer))
|
||||
}
|
||||
return list, nil
|
||||
}
|
||||
|
||||
func (s *SnapshotLegacyStore) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
|
||||
info, err := request.NamespaceInfoFrom(ctx, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = s.checkEnabled(info.Value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
query := dashboardsnapshots.GetDashboardSnapshotQuery{
|
||||
Key: name,
|
||||
}
|
||||
|
||||
res, err := s.Service.GetDashboardSnapshot(ctx, &query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if res != nil {
|
||||
return convertSnapshotToK8sResource(res, s.Namespacer), nil
|
||||
}
|
||||
return nil, s.ResourceInfo.NewNotFound(name)
|
||||
}
|
||||
|
||||
func (s *SnapshotLegacyStore) checkEnabled(ns string) error {
|
||||
if !s.Options.SnapshotsEnabled {
|
||||
return fmt.Errorf("snapshots not enabled")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package snapshot
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
|
||||
dashv0 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
|
||||
"github.com/grafana/grafana/pkg/services/dashboardsnapshots"
|
||||
)
|
||||
|
||||
// Currently only works with v0alpha1
|
||||
type dashboardREST struct {
|
||||
Service dashboardsnapshots.Service
|
||||
}
|
||||
|
||||
func NewDashboardREST(
|
||||
resourceInfo utils.ResourceInfo,
|
||||
service dashboardsnapshots.Service,
|
||||
) (rest.Storage, error) {
|
||||
return &dashboardREST{
|
||||
Service: service,
|
||||
}, nil
|
||||
}
|
||||
|
||||
var (
|
||||
_ rest.Connecter = (*dashboardREST)(nil)
|
||||
_ rest.StorageMetadata = (*dashboardREST)(nil)
|
||||
)
|
||||
|
||||
func (r *dashboardREST) New() runtime.Object {
|
||||
return &dashv0.Dashboard{}
|
||||
}
|
||||
|
||||
func (r *dashboardREST) Destroy() {
|
||||
}
|
||||
|
||||
func (r *dashboardREST) ConnectMethods() []string {
|
||||
return []string{"GET"}
|
||||
}
|
||||
|
||||
func (r *dashboardREST) NewConnectOptions() (runtime.Object, bool, string) {
|
||||
return nil, false, ""
|
||||
}
|
||||
|
||||
func (r *dashboardREST) ProducesMIMETypes(verb string) []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *dashboardREST) ProducesObject(verb string) interface{} {
|
||||
return r.New()
|
||||
}
|
||||
|
||||
func (r *dashboardREST) Connect(ctx context.Context, name string, opts runtime.Object, responder rest.Responder) (http.Handler, error) {
|
||||
_, err := request.NamespaceInfoFrom(ctx, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
snap, err := r.Service.GetDashboardSnapshot(ctx, &dashboardsnapshots.GetDashboardSnapshotQuery{Key: name})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
// TODO... support conversions (not required in v0)
|
||||
dash := &dashv0.Dashboard{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: name,
|
||||
},
|
||||
Spec: v0alpha1.Unstructured{
|
||||
Object: snap.Dashboard.MustMap(),
|
||||
},
|
||||
}
|
||||
responder.Object(200, dash)
|
||||
}), nil
|
||||
}
|
||||
@@ -1,130 +1,131 @@
|
||||
package dashboardsnapshot
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"gocloud.dev/blob"
|
||||
"k8s.io/kube-openapi/pkg/spec3"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/db"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/builder"
|
||||
"github.com/grafana/grafana/pkg/services/dashboardsnapshots"
|
||||
)
|
||||
|
||||
type dashExportStatus struct {
|
||||
Count int
|
||||
Index int
|
||||
Started int64
|
||||
Updated int64
|
||||
Finished int64
|
||||
Error string
|
||||
}
|
||||
|
||||
type dashExporter struct {
|
||||
status dashExportStatus
|
||||
|
||||
service dashboardsnapshots.Service
|
||||
sql db.DB
|
||||
}
|
||||
|
||||
func (d *dashExporter) getAPIRouteHandler() builder.APIRouteHandler {
|
||||
return builder.APIRouteHandler{
|
||||
Path: "admin/export",
|
||||
Spec: &spec3.PathProps{
|
||||
Summary: "an example at the root level",
|
||||
Description: "longer description here?",
|
||||
Post: &spec3.Operation{
|
||||
OperationProps: spec3.OperationProps{
|
||||
Tags: []string{"export"},
|
||||
Responses: &spec3.Responses{
|
||||
ResponsesProps: spec3.ResponsesProps{
|
||||
StatusCodeResponses: map[int]*spec3.Response{
|
||||
200: {
|
||||
ResponseProps: spec3.ResponseProps{
|
||||
Content: map[string]*spec3.MediaType{
|
||||
"application/json": {},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Handler: func(w http.ResponseWriter, r *http.Request) {
|
||||
// Only let it start once
|
||||
if d.status.Started == 0 {
|
||||
go d.doExport()
|
||||
}
|
||||
time.Sleep(time.Second)
|
||||
_ = json.NewEncoder(w).Encode(d.status)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// NO way to stop!!!!!!
|
||||
func (d *dashExporter) doExport() {
|
||||
defer func() {
|
||||
d.status.Finished = time.Now().UnixMilli()
|
||||
}()
|
||||
d.status = dashExportStatus{
|
||||
Started: time.Now().UnixMilli(),
|
||||
}
|
||||
if d.sql == nil {
|
||||
d.status.Error = "missing dependencies"
|
||||
return
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
keys := []string{}
|
||||
err := d.sql.GetSqlxSession().Select(ctx,
|
||||
&keys, "SELECT key FROM dashboard_snapshot ORDER BY id asc")
|
||||
if err != nil {
|
||||
d.status.Error = err.Error()
|
||||
return
|
||||
}
|
||||
d.status.Count = len(keys)
|
||||
|
||||
bucket, err := blob.OpenBucket(ctx, "mem://?key=foo.txt&prefix=a/subfolder/")
|
||||
if err != nil {
|
||||
d.status.Error = err.Error()
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
_ = bucket.Close()
|
||||
}()
|
||||
|
||||
for idx, key := range keys {
|
||||
d.status.Index = idx
|
||||
snap, err := d.service.GetDashboardSnapshot(ctx, &dashboardsnapshots.GetDashboardSnapshotQuery{
|
||||
Key: key,
|
||||
})
|
||||
if err != nil {
|
||||
d.status.Error = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
dash, err := snap.Dashboard.ToDB()
|
||||
if err != nil {
|
||||
d.status.Error = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("TODO, export: %s (len: %d)\n", snap.Key, len(dash))
|
||||
|
||||
// w, err := bucket.NewWriter(ctx, "foo.txt", nil)
|
||||
// if err != nil {
|
||||
// d.status.Error = err.Error()
|
||||
// return
|
||||
// }
|
||||
|
||||
time.Sleep(time.Second * 1)
|
||||
d.status.Updated = time.Now().UnixMilli()
|
||||
}
|
||||
fmt.Printf("done!\n")
|
||||
}
|
||||
//
|
||||
//import (
|
||||
// "context"
|
||||
// "encoding/json"
|
||||
// "fmt"
|
||||
// "net/http"
|
||||
// "time"
|
||||
//
|
||||
// "gocloud.dev/blob"
|
||||
// "k8s.io/kube-openapi/pkg/spec3"
|
||||
//
|
||||
// "github.com/grafana/grafana/pkg/infra/db"
|
||||
// "github.com/grafana/grafana/pkg/services/apiserver/builder"
|
||||
// "github.com/grafana/grafana/pkg/services/dashboardsnapshots"
|
||||
//)
|
||||
//
|
||||
//type dashExportStatus struct {
|
||||
// Count int
|
||||
// Index int
|
||||
// Started int64
|
||||
// Updated int64
|
||||
// Finished int64
|
||||
// Error string
|
||||
//}
|
||||
//
|
||||
//type dashExporter struct {
|
||||
// status dashExportStatus
|
||||
//
|
||||
// service dashboardsnapshots.Service
|
||||
// sql db.DB
|
||||
//}
|
||||
//
|
||||
//func (d *dashExporter) getAPIRouteHandler() builder.APIRouteHandler {
|
||||
// return builder.APIRouteHandler{
|
||||
// Path: "admin/export",
|
||||
// Spec: &spec3.PathProps{
|
||||
// Summary: "an example at the root level",
|
||||
// Description: "longer description here?",
|
||||
// Post: &spec3.Operation{
|
||||
// OperationProps: spec3.OperationProps{
|
||||
// Tags: []string{"export"},
|
||||
// Responses: &spec3.Responses{
|
||||
// ResponsesProps: spec3.ResponsesProps{
|
||||
// StatusCodeResponses: map[int]*spec3.Response{
|
||||
// 200: {
|
||||
// ResponseProps: spec3.ResponseProps{
|
||||
// Content: map[string]*spec3.MediaType{
|
||||
// "application/json": {},
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// Handler: func(w http.ResponseWriter, r *http.Request) {
|
||||
// // Only let it start once
|
||||
// if d.status.Started == 0 {
|
||||
// go d.doExport()
|
||||
// }
|
||||
// time.Sleep(time.Second)
|
||||
// _ = json.NewEncoder(w).Encode(d.status)
|
||||
// },
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//// NO way to stop!!!!!!
|
||||
//func (d *dashExporter) doExport() {
|
||||
// defer func() {
|
||||
// d.status.Finished = time.Now().UnixMilli()
|
||||
// }()
|
||||
// d.status = dashExportStatus{
|
||||
// Started: time.Now().UnixMilli(),
|
||||
// }
|
||||
// if d.sql == nil {
|
||||
// d.status.Error = "missing dependencies"
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// ctx := context.Background()
|
||||
// keys := []string{}
|
||||
// err := d.sql.GetSqlxSession().Select(ctx,
|
||||
// &keys, "SELECT key FROM dashboard_snapshot ORDER BY id asc")
|
||||
// if err != nil {
|
||||
// d.status.Error = err.Error()
|
||||
// return
|
||||
// }
|
||||
// d.status.Count = len(keys)
|
||||
//
|
||||
// bucket, err := blob.OpenBucket(ctx, "mem://?key=foo.txt&prefix=a/subfolder/")
|
||||
// if err != nil {
|
||||
// d.status.Error = err.Error()
|
||||
// return
|
||||
// }
|
||||
// defer func() {
|
||||
// _ = bucket.Close()
|
||||
// }()
|
||||
//
|
||||
// for idx, key := range keys {
|
||||
// d.status.Index = idx
|
||||
// snap, err := d.service.GetDashboardSnapshot(ctx, &dashboardsnapshots.GetDashboardSnapshotQuery{
|
||||
// Key: key,
|
||||
// })
|
||||
// if err != nil {
|
||||
// d.status.Error = err.Error()
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// dash, err := snap.Dashboard.ToDB()
|
||||
// if err != nil {
|
||||
// d.status.Error = err.Error()
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// fmt.Printf("TODO, export: %s (len: %d)\n", snap.Key, len(dash))
|
||||
//
|
||||
// // w, err := bucket.NewWriter(ctx, "foo.txt", nil)
|
||||
// // if err != nil {
|
||||
// // d.status.Error = err.Error()
|
||||
// // return
|
||||
// // }
|
||||
//
|
||||
// time.Sleep(time.Second * 1)
|
||||
// d.status.Updated = time.Now().UnixMilli()
|
||||
// }
|
||||
// fmt.Printf("done!\n")
|
||||
//}
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
package dashboardsnapshot
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"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/rest"
|
||||
|
||||
dashboardsnapshot "github.com/grafana/grafana/pkg/apis/dashboardsnapshot/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
var (
|
||||
_ rest.Scoper = (*optionsStorage)(nil)
|
||||
_ rest.SingularNameProvider = (*optionsStorage)(nil)
|
||||
_ rest.Getter = (*optionsStorage)(nil)
|
||||
_ rest.Lister = (*optionsStorage)(nil)
|
||||
_ rest.Storage = (*optionsStorage)(nil)
|
||||
)
|
||||
|
||||
type sharingOptionsGetter = func(namespace string) (*dashboardsnapshot.SharingOptions, error)
|
||||
|
||||
func newSharingOptionsGetter(cfg *setting.Cfg) sharingOptionsGetter {
|
||||
s := &dashboardsnapshot.SharingOptions{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
CreationTimestamp: metav1.Now(),
|
||||
},
|
||||
Spec: dashboardsnapshot.SnapshotSharingOptions{
|
||||
SnapshotsEnabled: cfg.SnapshotEnabled,
|
||||
ExternalSnapshotURL: cfg.ExternalSnapshotUrl,
|
||||
ExternalSnapshotName: cfg.ExternalSnapshotName,
|
||||
ExternalEnabled: cfg.ExternalEnabled,
|
||||
},
|
||||
}
|
||||
return func(namespace string) (*dashboardsnapshot.SharingOptions, error) {
|
||||
return s, nil
|
||||
}
|
||||
}
|
||||
|
||||
type optionsStorage struct {
|
||||
getter sharingOptionsGetter
|
||||
tableConverter rest.TableConvertor
|
||||
}
|
||||
|
||||
func (s *optionsStorage) New() runtime.Object {
|
||||
return &dashboardsnapshot.SharingOptions{}
|
||||
}
|
||||
|
||||
func (s *optionsStorage) Destroy() {}
|
||||
|
||||
func (s *optionsStorage) NamespaceScoped() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *optionsStorage) GetSingularName() string {
|
||||
return "options"
|
||||
}
|
||||
|
||||
func (s *optionsStorage) NewList() runtime.Object {
|
||||
return &dashboardsnapshot.SharingOptionsList{}
|
||||
}
|
||||
|
||||
func (s *optionsStorage) ConvertToTable(ctx context.Context, object runtime.Object, tableOptions runtime.Object) (*metav1.Table, error) {
|
||||
return s.tableConverter.ConvertToTable(ctx, object, tableOptions)
|
||||
}
|
||||
|
||||
func (s *optionsStorage) List(ctx context.Context, options *internalversion.ListOptions) (runtime.Object, error) {
|
||||
info, err := request.NamespaceInfoFrom(ctx, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if info.OrgID < 0 {
|
||||
return nil, fmt.Errorf("missing namespace")
|
||||
}
|
||||
v, err := s.getter(info.Value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
list := &dashboardsnapshot.SharingOptionsList{
|
||||
Items: []dashboardsnapshot.SharingOptions{*v},
|
||||
}
|
||||
return list, nil
|
||||
}
|
||||
|
||||
func (s *optionsStorage) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
|
||||
return s.getter(name)
|
||||
}
|
||||
@@ -1,147 +0,0 @@
|
||||
package dashboardsnapshot
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"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/rest"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
dashboardsnapshot "github.com/grafana/grafana/pkg/apis/dashboardsnapshot/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
|
||||
"github.com/grafana/grafana/pkg/services/dashboardsnapshots"
|
||||
)
|
||||
|
||||
var (
|
||||
_ rest.Scoper = (*legacyStorage)(nil)
|
||||
_ rest.SingularNameProvider = (*legacyStorage)(nil)
|
||||
_ rest.Getter = (*legacyStorage)(nil)
|
||||
_ rest.Lister = (*legacyStorage)(nil)
|
||||
_ rest.Storage = (*legacyStorage)(nil)
|
||||
_ rest.GracefulDeleter = (*legacyStorage)(nil)
|
||||
)
|
||||
|
||||
type legacyStorage struct {
|
||||
service dashboardsnapshots.Service
|
||||
namespacer request.NamespaceMapper
|
||||
tableConverter rest.TableConvertor
|
||||
options sharingOptionsGetter
|
||||
}
|
||||
|
||||
func (s *legacyStorage) New() runtime.Object {
|
||||
return resourceInfo.NewFunc()
|
||||
}
|
||||
|
||||
func (s *legacyStorage) Destroy() {}
|
||||
|
||||
func (s *legacyStorage) NamespaceScoped() bool {
|
||||
return true // namespace == org
|
||||
}
|
||||
|
||||
func (s *legacyStorage) GetSingularName() string {
|
||||
return resourceInfo.GetSingularName()
|
||||
}
|
||||
|
||||
func (s *legacyStorage) NewList() runtime.Object {
|
||||
return resourceInfo.NewListFunc()
|
||||
}
|
||||
|
||||
func (s *legacyStorage) checkEnabled(ns string) error {
|
||||
opts, err := s.options(ns)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !opts.Spec.SnapshotsEnabled {
|
||||
return fmt.Errorf("snapshots not enabled")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *legacyStorage) ConvertToTable(ctx context.Context, object runtime.Object, tableOptions runtime.Object) (*metav1.Table, error) {
|
||||
return s.tableConverter.ConvertToTable(ctx, object, tableOptions)
|
||||
}
|
||||
|
||||
func (s *legacyStorage) List(ctx context.Context, options *internalversion.ListOptions) (runtime.Object, error) {
|
||||
info, err := request.NamespaceInfoFrom(ctx, true)
|
||||
if err == nil {
|
||||
err = s.checkEnabled(info.Value)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
user, err := identity.GetRequester(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
limit := 5000
|
||||
if options.Limit > 0 {
|
||||
limit = int(options.Limit)
|
||||
}
|
||||
res, err := s.service.SearchDashboardSnapshots(ctx, &dashboardsnapshots.GetDashboardSnapshotsQuery{
|
||||
OrgID: info.OrgID,
|
||||
SignedInUser: user,
|
||||
Limit: limit,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
list := &dashboardsnapshot.DashboardSnapshotList{}
|
||||
for _, v := range res {
|
||||
list.Items = append(list.Items, *convertDTOToSnapshot(v, s.namespacer))
|
||||
}
|
||||
return list, nil
|
||||
}
|
||||
|
||||
func (s *legacyStorage) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
|
||||
info, err := request.NamespaceInfoFrom(ctx, true)
|
||||
if err == nil {
|
||||
err = s.checkEnabled(info.Value)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
v, err := s.service.GetDashboardSnapshot(ctx, &dashboardsnapshots.GetDashboardSnapshotQuery{
|
||||
Key: name,
|
||||
})
|
||||
if err != nil || v == nil {
|
||||
// if errors.Is(err, playlistsvc.ErrPlaylistNotFound) || err == nil {
|
||||
// err = k8serrors.NewNotFound(s.SingularQualifiedResource, name)
|
||||
// }
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return convertSnapshotToK8sResource(v, s.namespacer), nil
|
||||
}
|
||||
|
||||
// GracefulDeleter
|
||||
func (s *legacyStorage) Delete(ctx context.Context, name string, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions) (runtime.Object, bool, error) {
|
||||
snap, err := s.service.GetDashboardSnapshot(ctx, &dashboardsnapshots.GetDashboardSnapshotQuery{
|
||||
Key: name,
|
||||
})
|
||||
if err != nil || snap == nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
// Delete the external one first
|
||||
if snap.ExternalDeleteURL != "" {
|
||||
err := dashboardsnapshots.DeleteExternalDashboardSnapshot(snap.ExternalDeleteURL)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
}
|
||||
|
||||
err = s.service.DeleteDashboardSnapshot(ctx, &dashboardsnapshots.DeleteDashboardSnapshotCommand{
|
||||
DeleteKey: snap.DeleteKey,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
return nil, true, nil
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
package dashboardsnapshot
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
|
||||
common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
|
||||
dashboardsnapshot "github.com/grafana/grafana/pkg/apis/dashboardsnapshot/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
|
||||
"github.com/grafana/grafana/pkg/services/dashboardsnapshots"
|
||||
)
|
||||
|
||||
type subBodyREST struct {
|
||||
service dashboardsnapshots.Service
|
||||
namespacer request.NamespaceMapper
|
||||
}
|
||||
|
||||
var _ = rest.Connecter(&subBodyREST{})
|
||||
|
||||
func (r *subBodyREST) New() runtime.Object {
|
||||
return &dashboardsnapshot.FullDashboardSnapshot{}
|
||||
}
|
||||
|
||||
func (r *subBodyREST) Destroy() {}
|
||||
|
||||
func (r *subBodyREST) ConnectMethods() []string {
|
||||
return []string{"GET"}
|
||||
}
|
||||
|
||||
func (r *subBodyREST) NewConnectOptions() (runtime.Object, bool, string) {
|
||||
return nil, false, ""
|
||||
}
|
||||
|
||||
func (r *subBodyREST) Connect(ctx context.Context, name string, opts runtime.Object, responder rest.Responder) (http.Handler, error) {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
snap, err := r.service.GetDashboardSnapshot(ctx, &dashboardsnapshots.GetDashboardSnapshotQuery{
|
||||
Key: name,
|
||||
})
|
||||
if err != nil {
|
||||
responder.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
data, err := snap.Dashboard.Map()
|
||||
if err != nil {
|
||||
responder.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
r := convertSnapshotToK8sResource(snap, r.namespacer)
|
||||
responder.Object(200, &dashboardsnapshot.FullDashboardSnapshot{
|
||||
ObjectMeta: r.ObjectMeta,
|
||||
Info: r.Spec,
|
||||
Dashboard: common.Unstructured{Object: data},
|
||||
})
|
||||
}), nil
|
||||
}
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
|
||||
"github.com/grafana/grafana/pkg/registry/apis/collections"
|
||||
dashboardinternal "github.com/grafana/grafana/pkg/registry/apis/dashboard"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/dashboardsnapshot"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/datasource"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/folders"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/iam"
|
||||
@@ -60,7 +59,6 @@ var WireSet = wire.NewSet(
|
||||
|
||||
// Each must be added here *and* in the ServiceSink above
|
||||
dashboardinternal.RegisterAPIService,
|
||||
dashboardsnapshot.RegisterAPIService,
|
||||
datasource.RegisterAPIService,
|
||||
folders.RegisterAPIService,
|
||||
iam.RegisterAPIService,
|
||||
|
||||
Generated
+4
-7
@@ -51,7 +51,6 @@ import (
|
||||
"github.com/grafana/grafana/pkg/registry/apis/collections"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/dashboard"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/dashboard/legacy"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/dashboardsnapshot"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/datasource"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/folders"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/iam"
|
||||
@@ -866,8 +865,7 @@ func Initialize(ctx context.Context, cfg *setting.Cfg, opts Options, apiOpts api
|
||||
identitySynchronizer := authnimpl.ProvideIdentitySynchronizer(authnimplService)
|
||||
ldapImpl := service12.ProvideService(cfg, featureToggles, ssosettingsimplService)
|
||||
apiService := api4.ProvideService(cfg, routeRegisterImpl, accessControl, userService, authinfoimplService, ossGroups, identitySynchronizer, orgService, ldapImpl, userAuthTokenService, bundleregistryService)
|
||||
dashboardsAPIBuilder := dashboard.RegisterAPIService(cfg, featureToggles, apiserverService, dashboardService, dashboardProvisioningService, service15, dashboardServiceImpl, dashboardPermissionsService, accessControl, accessClient, provisioningServiceImpl, dashboardsStore, registerer, sqlStore, tracingService, resourceClient, dualwriteService, sortService, quotaService, libraryPanelService, eventualRestConfigProvider, userService, libraryElementService, publicDashboardServiceImpl)
|
||||
snapshotsAPIBuilder := dashboardsnapshot.RegisterAPIService(serviceImpl, apiserverService, cfg, featureToggles, sqlStore, registerer)
|
||||
dashboardsAPIBuilder := dashboard.RegisterAPIService(cfg, featureToggles, apiserverService, dashboardService, dashboardProvisioningService, service15, dashboardServiceImpl, dashboardPermissionsService, accessControl, accessClient, provisioningServiceImpl, dashboardsStore, registerer, sqlStore, tracingService, resourceClient, dualwriteService, sortService, quotaService, libraryPanelService, eventualRestConfigProvider, userService, libraryElementService, publicDashboardServiceImpl, serviceImpl)
|
||||
dataSourceAPIBuilder, err := datasource.RegisterAPIService(featureToggles, apiserverService, middlewareHandler, scopedPluginDatasourceProvider, plugincontextProvider, accessControl, registerer, sourcesService)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -919,7 +917,7 @@ func Initialize(ctx context.Context, cfg *setting.Cfg, opts Options, apiOpts api
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
apiregistryService := apiregistry.ProvideRegistryServiceSink(dashboardsAPIBuilder, snapshotsAPIBuilder, dataSourceAPIBuilder, folderAPIBuilder, identityAccessManagementAPIBuilder, queryAPIBuilder, userStorageAPIBuilder, apiBuilder, collectionsAPIBuilder, provisioningAPIBuilder, ofrepAPIBuilder, dependencyRegisterer, provisioningDependencyRegisterer)
|
||||
apiregistryService := apiregistry.ProvideRegistryServiceSink(dashboardsAPIBuilder, dataSourceAPIBuilder, folderAPIBuilder, identityAccessManagementAPIBuilder, queryAPIBuilder, userStorageAPIBuilder, apiBuilder, collectionsAPIBuilder, provisioningAPIBuilder, ofrepAPIBuilder, dependencyRegisterer, provisioningDependencyRegisterer)
|
||||
teamPermissionsService, err := ossaccesscontrol.ProvideTeamPermissions(cfg, featureToggles, routeRegisterImpl, sqlStore, accessControl, ossLicensingService, acimplService, teamService, userService, actionSetService)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -1520,8 +1518,7 @@ func InitializeForTest(ctx context.Context, t sqlutil.ITestDB, testingT interfac
|
||||
identitySynchronizer := authnimpl.ProvideIdentitySynchronizer(authnimplService)
|
||||
ldapImpl := service12.ProvideService(cfg, featureToggles, ssosettingsimplService)
|
||||
apiService := api4.ProvideService(cfg, routeRegisterImpl, accessControl, userService, authinfoimplService, ossGroups, identitySynchronizer, orgService, ldapImpl, userAuthTokenService, bundleregistryService)
|
||||
dashboardsAPIBuilder := dashboard.RegisterAPIService(cfg, featureToggles, apiserverService, dashboardService, dashboardProvisioningService, service15, dashboardServiceImpl, dashboardPermissionsService, accessControl, accessClient, provisioningServiceImpl, dashboardsStore, registerer, sqlStore, tracingService, resourceClient, dualwriteService, sortService, quotaService, libraryPanelService, eventualRestConfigProvider, userService, libraryElementService, publicDashboardServiceImpl)
|
||||
snapshotsAPIBuilder := dashboardsnapshot.RegisterAPIService(serviceImpl, apiserverService, cfg, featureToggles, sqlStore, registerer)
|
||||
dashboardsAPIBuilder := dashboard.RegisterAPIService(cfg, featureToggles, apiserverService, dashboardService, dashboardProvisioningService, service15, dashboardServiceImpl, dashboardPermissionsService, accessControl, accessClient, provisioningServiceImpl, dashboardsStore, registerer, sqlStore, tracingService, resourceClient, dualwriteService, sortService, quotaService, libraryPanelService, eventualRestConfigProvider, userService, libraryElementService, publicDashboardServiceImpl, serviceImpl)
|
||||
dataSourceAPIBuilder, err := datasource.RegisterAPIService(featureToggles, apiserverService, middlewareHandler, scopedPluginDatasourceProvider, plugincontextProvider, accessControl, registerer, sourcesService)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -1573,7 +1570,7 @@ func InitializeForTest(ctx context.Context, t sqlutil.ITestDB, testingT interfac
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
apiregistryService := apiregistry.ProvideRegistryServiceSink(dashboardsAPIBuilder, snapshotsAPIBuilder, dataSourceAPIBuilder, folderAPIBuilder, identityAccessManagementAPIBuilder, queryAPIBuilder, userStorageAPIBuilder, apiBuilder, collectionsAPIBuilder, provisioningAPIBuilder, ofrepAPIBuilder, dependencyRegisterer, provisioningDependencyRegisterer)
|
||||
apiregistryService := apiregistry.ProvideRegistryServiceSink(dashboardsAPIBuilder, dataSourceAPIBuilder, folderAPIBuilder, identityAccessManagementAPIBuilder, queryAPIBuilder, userStorageAPIBuilder, apiBuilder, collectionsAPIBuilder, provisioningAPIBuilder, ofrepAPIBuilder, dependencyRegisterer, provisioningDependencyRegisterer)
|
||||
teamPermissionsService, err := ossaccesscontrol.ProvideTeamPermissions(cfg, featureToggles, routeRegisterImpl, sqlStore, accessControl, ossLicensingService, acimplService, teamService, userService, actionSetService)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -8,8 +8,8 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
snapshot "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v0alpha1"
|
||||
common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
|
||||
dashboardsnapshot "github.com/grafana/grafana/pkg/apis/dashboardsnapshot/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/infra/db"
|
||||
"github.com/grafana/grafana/pkg/services/dashboardsnapshots"
|
||||
@@ -123,7 +123,7 @@ func TestIntegrationDashboardSnapshotDBAccess(t *testing.T) {
|
||||
cmd := dashboardsnapshots.CreateDashboardSnapshotCommand{
|
||||
Key: "strangesnapshotwithuserid0",
|
||||
DeleteKey: "adeletekey",
|
||||
DashboardCreateCommand: dashboardsnapshot.DashboardCreateCommand{
|
||||
DashboardCreateCommand: snapshot.DashboardCreateCommand{
|
||||
Dashboard: &common.Unstructured{Object: map[string]any{
|
||||
"hello": "mupp",
|
||||
}},
|
||||
@@ -202,7 +202,7 @@ func createTestSnapshot(t *testing.T, dashStore *DashboardSnapshotStore, key str
|
||||
cmd := dashboardsnapshots.CreateDashboardSnapshotCommand{
|
||||
Key: key,
|
||||
DeleteKey: "delete" + key,
|
||||
DashboardCreateCommand: dashboardsnapshot.DashboardCreateCommand{
|
||||
DashboardCreateCommand: snapshot.DashboardCreateCommand{
|
||||
Expires: expires,
|
||||
Dashboard: &common.Unstructured{Object: map[string]any{
|
||||
"hello": "mupp",
|
||||
|
||||
@@ -3,8 +3,8 @@ package dashboardsnapshots
|
||||
import (
|
||||
"time"
|
||||
|
||||
snapshot "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
dashboardsnapshot "github.com/grafana/grafana/pkg/apis/dashboardsnapshot/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
)
|
||||
|
||||
@@ -50,7 +50,7 @@ type DashboardSnapshotDTO struct {
|
||||
type CreateDashboardSnapshotCommand struct {
|
||||
// The "public" fields are defined in this struct while the private/SQL/response params are
|
||||
// defied in the rest of this command
|
||||
dashboardsnapshot.DashboardCreateCommand
|
||||
snapshot.DashboardCreateCommand
|
||||
|
||||
ExternalURL string `json:"-"`
|
||||
ExternalDeleteURL string `json:"-"`
|
||||
|
||||
@@ -9,9 +9,9 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
snapshot "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v0alpha1"
|
||||
common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
dashboardsnapshot "github.com/grafana/grafana/pkg/apis/dashboardsnapshot/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/infra/metrics"
|
||||
@@ -36,7 +36,7 @@ var client = &http.Client{
|
||||
Transport: &http.Transport{Proxy: http.ProxyFromEnvironment},
|
||||
}
|
||||
|
||||
func CreateDashboardSnapshot(c *contextmodel.ReqContext, cfg dashboardsnapshot.SnapshotSharingOptions, cmd CreateDashboardSnapshotCommand, svc Service) {
|
||||
func CreateDashboardSnapshot(c *contextmodel.ReqContext, cfg snapshot.SnapshotSharingOptions, cmd CreateDashboardSnapshotCommand, svc Service) {
|
||||
if !cfg.SnapshotsEnabled {
|
||||
c.JsonApiErr(http.StatusForbidden, "Dashboard Snapshots are disabled", nil)
|
||||
return
|
||||
@@ -125,7 +125,7 @@ func CreateDashboardSnapshot(c *contextmodel.ReqContext, cfg dashboardsnapshot.S
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, dashboardsnapshot.DashboardCreateResponse{
|
||||
c.JSON(http.StatusOK, snapshot.DashboardCreateResponse{
|
||||
Key: result.Key,
|
||||
DeleteKey: result.DeleteKey,
|
||||
URL: snapshotUrl,
|
||||
|
||||
@@ -7,8 +7,8 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
snapshot "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v0alpha1"
|
||||
common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
|
||||
dashboardsnapshot "github.com/grafana/grafana/pkg/apis/dashboardsnapshot/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/infra/db"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/dashboardsnapshots"
|
||||
@@ -53,7 +53,7 @@ func TestIntegrationDashboardSnapshotsService(t *testing.T) {
|
||||
cmd := dashboardsnapshots.CreateDashboardSnapshotCommand{
|
||||
Key: dashboardKey,
|
||||
DeleteKey: dashboardKey,
|
||||
DashboardCreateCommand: dashboardsnapshot.DashboardCreateCommand{
|
||||
DashboardCreateCommand: snapshot.DashboardCreateCommand{
|
||||
Dashboard: dashboard,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -10,9 +10,9 @@ import (
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
snapshot "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v0alpha1"
|
||||
common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
dashboardsnapshot "github.com/grafana/grafana/pkg/apis/dashboardsnapshot/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
@@ -22,7 +22,7 @@ import (
|
||||
|
||||
func TestCreateDashboardSnapshot_DashboardNotFound(t *testing.T) {
|
||||
mockService := &MockService{}
|
||||
cfg := dashboardsnapshot.SnapshotSharingOptions{
|
||||
cfg := snapshot.SnapshotSharingOptions{
|
||||
SnapshotsEnabled: true,
|
||||
ExternalEnabled: false,
|
||||
}
|
||||
@@ -42,7 +42,7 @@ func TestCreateDashboardSnapshot_DashboardNotFound(t *testing.T) {
|
||||
_ = json.Unmarshal(dashboardBytes, dashboard)
|
||||
|
||||
cmd := CreateDashboardSnapshotCommand{
|
||||
DashboardCreateCommand: dashboardsnapshot.DashboardCreateCommand{
|
||||
DashboardCreateCommand: snapshot.DashboardCreateCommand{
|
||||
Dashboard: dashboard,
|
||||
Name: "Test Snapshot",
|
||||
},
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
package dashboardsnapshots
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/tests/apis"
|
||||
"github.com/grafana/grafana/pkg/tests/testinfra"
|
||||
"github.com/grafana/grafana/pkg/tests/testsuite"
|
||||
"github.com/grafana/grafana/pkg/util/testutil"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
testsuite.Run(m)
|
||||
}
|
||||
|
||||
func TestIntegrationDashboardSnapshots(t *testing.T) {
|
||||
testutil.SkipIntegrationTestInShortMode(t)
|
||||
|
||||
helper := apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
||||
AppModeProduction: false, // required for experimental apis
|
||||
DisableAnonymous: true,
|
||||
EnableFeatureToggles: []string{
|
||||
featuremgmt.FlagGrafanaAPIServerWithExperimentalAPIs, // required to register dashboardsnapshot.grafana.app
|
||||
},
|
||||
})
|
||||
|
||||
t.Run("Check discovery client", func(t *testing.T) {
|
||||
disco := helper.GetGroupVersionInfoJSON("dashboardsnapshot.grafana.app")
|
||||
|
||||
// fmt.Printf("%s", disco)
|
||||
require.JSONEq(t, `[
|
||||
{
|
||||
"freshness": "Current",
|
||||
"resources": [
|
||||
{
|
||||
"resource": "dashboardsnapshots",
|
||||
"responseKind": {
|
||||
"group": "",
|
||||
"kind": "DashboardSnapshot",
|
||||
"version": ""
|
||||
},
|
||||
"scope": "Namespaced",
|
||||
"singularResource": "dashboardsnapshot",
|
||||
"subresources": [
|
||||
{
|
||||
"responseKind": {
|
||||
"group": "",
|
||||
"kind": "FullDashboardSnapshot",
|
||||
"version": ""
|
||||
},
|
||||
"subresource": "body",
|
||||
"verbs": [
|
||||
"get"
|
||||
]
|
||||
}
|
||||
],
|
||||
"verbs": [
|
||||
"delete",
|
||||
"get",
|
||||
"list"
|
||||
]
|
||||
},
|
||||
{
|
||||
"resource": "options",
|
||||
"responseKind": {
|
||||
"group": "",
|
||||
"kind": "SharingOptions",
|
||||
"version": ""
|
||||
},
|
||||
"scope": "Namespaced",
|
||||
"singularResource": "options",
|
||||
"verbs": [
|
||||
"get",
|
||||
"list"
|
||||
]
|
||||
}
|
||||
],
|
||||
"version": "v0alpha1"
|
||||
}
|
||||
]`, disco)
|
||||
})
|
||||
}
|
||||
@@ -1935,6 +1935,447 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/apis/dashboard.grafana.app/v0alpha1/namespaces/{namespace}/snapshots": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"Snapshot"
|
||||
],
|
||||
"description": "list objects of kind Snapshot",
|
||||
"operationId": "listSnapshot",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v0alpha1.SnapshotList"
|
||||
}
|
||||
},
|
||||
"application/json;stream=watch": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v0alpha1.SnapshotList"
|
||||
}
|
||||
},
|
||||
"application/vnd.kubernetes.protobuf": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v0alpha1.SnapshotList"
|
||||
}
|
||||
},
|
||||
"application/vnd.kubernetes.protobuf;stream=watch": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v0alpha1.SnapshotList"
|
||||
}
|
||||
},
|
||||
"application/yaml": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v0alpha1.SnapshotList"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"x-kubernetes-action": "list",
|
||||
"x-kubernetes-group-version-kind": {
|
||||
"group": "dashboard.grafana.app",
|
||||
"version": "v0alpha1",
|
||||
"kind": "Snapshot"
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "allowWatchBookmarks",
|
||||
"in": "query",
|
||||
"description": "allowWatchBookmarks requests watch events with type \"BOOKMARK\". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored.",
|
||||
"schema": {
|
||||
"type": "boolean",
|
||||
"uniqueItems": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "continue",
|
||||
"in": "query",
|
||||
"description": "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"uniqueItems": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "fieldSelector",
|
||||
"in": "query",
|
||||
"description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"uniqueItems": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "labelSelector",
|
||||
"in": "query",
|
||||
"description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"uniqueItems": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "limit",
|
||||
"in": "query",
|
||||
"description": "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.",
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"uniqueItems": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "namespace",
|
||||
"in": "path",
|
||||
"description": "object name and auth scope, such as for teams and projects",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"uniqueItems": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "pretty",
|
||||
"in": "query",
|
||||
"description": "If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget).",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"uniqueItems": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "resourceVersion",
|
||||
"in": "query",
|
||||
"description": "resourceVersion sets a constraint on what resource versions a request may be served from. See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"uniqueItems": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "resourceVersionMatch",
|
||||
"in": "query",
|
||||
"description": "resourceVersionMatch determines how resourceVersion is applied to list calls. It is highly recommended that resourceVersionMatch be set for list calls where resourceVersion is set See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"uniqueItems": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "sendInitialEvents",
|
||||
"in": "query",
|
||||
"description": "`sendInitialEvents=true` may be set together with `watch=true`. In that case, the watch stream will begin with synthetic events to produce the current state of objects in the collection. Once all such events have been sent, a synthetic \"Bookmark\" event will be sent. The bookmark will report the ResourceVersion (RV) corresponding to the set of objects, and be marked with `\"k8s.io/initial-events-end\": \"true\"` annotation. Afterwards, the watch stream will proceed as usual, sending watch events corresponding to changes (subsequent to the RV) to objects watched.\n\nWhen `sendInitialEvents` option is set, we require `resourceVersionMatch` option to also be set. The semantic of the watch request is as following: - `resourceVersionMatch` = NotOlderThan\n is interpreted as \"data at least as new as the provided `resourceVersion`\"\n and the bookmark event is send when the state is synced\n to a `resourceVersion` at least as fresh as the one provided by the ListOptions.\n If `resourceVersion` is unset, this is interpreted as \"consistent read\" and the\n bookmark event is send when the state is synced at least to the moment\n when request started being processed.\n- `resourceVersionMatch` set to any other value or unset\n Invalid error is returned.\n\nDefaults to true if `resourceVersion=\"\"` or `resourceVersion=\"0\"` (for backward compatibility reasons) and to false otherwise.",
|
||||
"schema": {
|
||||
"type": "boolean",
|
||||
"uniqueItems": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "timeoutSeconds",
|
||||
"in": "query",
|
||||
"description": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.",
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"uniqueItems": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "watch",
|
||||
"in": "query",
|
||||
"description": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.",
|
||||
"schema": {
|
||||
"type": "boolean",
|
||||
"uniqueItems": true
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"/apis/dashboard.grafana.app/v0alpha1/namespaces/{namespace}/snapshots/create": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"Snapshot"
|
||||
],
|
||||
"description": "Creates a new Snapshot",
|
||||
"operationId": "createSnapshot",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "namespace",
|
||||
"in": "path",
|
||||
"description": "workspace",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"example": "default"
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {},
|
||||
"example": "{\"dashboard\":{\"annotations\":{\"list\":[{\"name\":\"Annotations \u0026 Alerts\",\"enable\":true,\"iconColor\":\"rgba(0, 211, 255, 1)\",\"snapshotData\":[],\"type\":\"dashboard\",\"builtIn\":1,\"hide\":true}]},\"editable\":true,\"fiscalYearStartMonth\":0,\"graphTooltip\":0,\"id\":203,\"links\":[],\"liveNow\":false,\"panels\":[{\"datasource\":null,\"fieldConfig\":{\"defaults\":{\"color\":{\"mode\":\"palette-classic\"},\"custom\":{\"axisBorderShow\":false,\"axisCenteredZero\":false,\"axisColorMode\":\"text\",\"axisLabel\":\"\",\"axisPlacement\":\"auto\",\"barAlignment\":0,\"drawStyle\":\"line\",\"fillOpacity\":43,\"gradientMode\":\"opacity\",\"hideFrom\":{\"legend\":false,\"tooltip\":false,\"viz\":false},\"insertNulls\":false,\"lineInterpolation\":\"smooth\",\"lineWidth\":1,\"pointSize\":5,\"scaleDistribution\":{\"type\":\"linear\"},\"showPoints\":\"auto\",\"spanNulls\":false,\"stacking\":{\"group\":\"A\",\"mode\":\"none\"},\"thresholdsStyle\":{\"mode\":\"off\"}},\"mappings\":[],\"thresholds\":{\"mode\":\"absolute\",\"steps\":[{\"color\":\"green\",\"value\":null},{\"color\":\"red\",\"value\":80}]},\"unitScale\":true},\"overrides\":[]},\"gridPos\":{\"h\":8,\"w\":12,\"x\":0,\"y\":0},\"id\":1,\"options\":{\"legend\":{\"calcs\":[],\"displayMode\":\"list\",\"placement\":\"bottom\",\"showLegend\":true},\"tooltip\":{\"mode\":\"single\",\"sort\":\"none\"}},\"pluginVersion\":\"10.4.0-pre\",\"snapshotData\":[{\"fields\":[{\"config\":{\"color\":{\"mode\":\"palette-classic\"},\"custom\":{\"axisBorderShow\":false,\"axisCenteredZero\":false,\"axisColorMode\":\"text\",\"axisPlacement\":\"auto\",\"barAlignment\":0,\"drawStyle\":\"line\",\"fillOpacity\":43,\"gradientMode\":\"opacity\",\"hideFrom\":{\"legend\":false,\"tooltip\":false,\"viz\":false},\"lineInterpolation\":\"smooth\",\"lineWidth\":1,\"pointSize\":5,\"showPoints\":\"auto\",\"thresholdsStyle\":{\"mode\":\"off\"}},\"thresholds\":{\"mode\":\"absolute\",\"steps\":[{\"color\":\"green\",\"value\":null},{\"color\":\"red\",\"value\":80}]},\"unitScale\":true},\"name\":\"time\",\"type\":\"time\",\"values\":[1706030536378,1706034856378,1706039176378,1706043496378,1706047816378,1706052136378]},{\"config\":{\"color\":{\"mode\":\"palette-classic\"},\"custom\":{\"axisBorderShow\":false,\"axisCenteredZero\":false,\"axisColorMode\":\"text\",\"axisLabel\":\"\",\"axisPlacement\":\"auto\",\"barAlignment\":0,\"drawStyle\":\"line\",\"fillOpacity\":43,\"gradientMode\":\"opacity\",\"hideFrom\":{\"legend\":false,\"tooltip\":false,\"viz\":false},\"insertNulls\":false,\"lineInterpolation\":\"smooth\",\"lineWidth\":1,\"pointSize\":5,\"scaleDistribution\":{\"type\":\"linear\"},\"showPoints\":\"auto\",\"spanNulls\":false,\"stacking\":{\"group\":\"A\",\"mode\":\"none\"},\"thresholdsStyle\":{\"mode\":\"off\"}},\"mappings\":[],\"thresholds\":{\"mode\":\"absolute\",\"steps\":[{\"color\":\"green\",\"value\":null},{\"color\":\"red\",\"value\":80}]},\"unitScale\":true},\"name\":\"A-series\",\"type\":\"number\",\"values\":[1,20,90,30,50,0]}],\"refId\":\"A\"}],\"targets\":[],\"title\":\"Simple example\",\"type\":\"timeseries\",\"links\":[]}],\"refresh\":\"\",\"schemaVersion\":39,\"snapshot\":{\"timestamp\":\"2024-01-23T23:22:16.377Z\"},\"tags\":[],\"templating\":{\"list\":[]},\"time\":{\"from\":\"2024-01-23T17:22:20.380Z\",\"to\":\"2024-01-23T23:22:20.380Z\",\"raw\":{\"from\":\"now-6h\",\"to\":\"now\"}},\"timepicker\":{},\"timezone\":\"\",\"title\":\"simple and small\",\"uid\":\"b22ec8db-399b-403b-b6c7-b0fb30ccb2a5\",\"version\":1,\"weekStart\":\"\"},\"name\":\"simple and small\",\"expires\":86400}"
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"x-grafana-action": "create",
|
||||
"x-kubernetes-group-version-kind": {
|
||||
"group": "dashboard.grafana.app",
|
||||
"version": "v0alpha1",
|
||||
"kind": "DashboardCreateResponse"
|
||||
}
|
||||
}
|
||||
},
|
||||
"/apis/dashboard.grafana.app/v0alpha1/namespaces/{namespace}/snapshots/delete/{deleteKey}": {
|
||||
"description": "Delete snapshot by delete key",
|
||||
"delete": {
|
||||
"tags": [
|
||||
"Snapshot"
|
||||
],
|
||||
"operationId": "deleteWithKey",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "deleteKey",
|
||||
"in": "path",
|
||||
"description": "unique key returned in create",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/apis/dashboard.grafana.app/v0alpha1/namespaces/{namespace}/snapshots/{name}": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"Snapshot"
|
||||
],
|
||||
"description": "read the specified Snapshot",
|
||||
"operationId": "getSnapshot",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v0alpha1.Snapshot"
|
||||
}
|
||||
},
|
||||
"application/vnd.kubernetes.protobuf": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v0alpha1.Snapshot"
|
||||
}
|
||||
},
|
||||
"application/yaml": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v0alpha1.Snapshot"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"x-kubernetes-action": "get",
|
||||
"x-kubernetes-group-version-kind": {
|
||||
"group": "dashboard.grafana.app",
|
||||
"version": "v0alpha1",
|
||||
"kind": "Snapshot"
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"tags": [
|
||||
"Snapshot"
|
||||
],
|
||||
"description": "delete a Snapshot",
|
||||
"operationId": "deleteSnapshot",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "dryRun",
|
||||
"in": "query",
|
||||
"description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"uniqueItems": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "gracePeriodSeconds",
|
||||
"in": "query",
|
||||
"description": "The duration in seconds before the object should be deleted. Value must be non-negative integer. The value zero indicates delete immediately. If this value is nil, the default grace period for the specified type will be used. Defaults to a per object value if not specified. zero means delete immediately.",
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"uniqueItems": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "ignoreStoreReadErrorWithClusterBreakingPotential",
|
||||
"in": "query",
|
||||
"description": "if set to true, it will trigger an unsafe deletion of the resource in case the normal deletion flow fails with a corrupt object error. A resource is considered corrupt if it can not be retrieved from the underlying storage successfully because of a) its data can not be transformed e.g. decryption failure, or b) it fails to decode into an object. NOTE: unsafe deletion ignores finalizer constraints, skips precondition checks, and removes the object from the storage. WARNING: This may potentially break the cluster if the workload associated with the resource being unsafe-deleted relies on normal deletion flow. Use only if you REALLY know what you are doing. The default value is false, and the user must opt in to enable it",
|
||||
"schema": {
|
||||
"type": "boolean",
|
||||
"uniqueItems": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "orphanDependents",
|
||||
"in": "query",
|
||||
"description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.",
|
||||
"schema": {
|
||||
"type": "boolean",
|
||||
"uniqueItems": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "propagationPolicy",
|
||||
"in": "query",
|
||||
"description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy. Acceptable values are: 'Orphan' - orphan the dependents; 'Background' - allow the garbage collector to delete the dependents in the background; 'Foreground' - a cascading policy that deletes all dependents in the foreground.",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"uniqueItems": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status"
|
||||
}
|
||||
},
|
||||
"application/vnd.kubernetes.protobuf": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status"
|
||||
}
|
||||
},
|
||||
"application/yaml": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"202": {
|
||||
"description": "Accepted",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status"
|
||||
}
|
||||
},
|
||||
"application/vnd.kubernetes.protobuf": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status"
|
||||
}
|
||||
},
|
||||
"application/yaml": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"x-kubernetes-action": "delete",
|
||||
"x-kubernetes-group-version-kind": {
|
||||
"group": "dashboard.grafana.app",
|
||||
"version": "v0alpha1",
|
||||
"kind": "Snapshot"
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "name",
|
||||
"in": "path",
|
||||
"description": "name of the Snapshot",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"uniqueItems": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "namespace",
|
||||
"in": "path",
|
||||
"description": "object name and auth scope, such as for teams and projects",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"uniqueItems": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "pretty",
|
||||
"in": "query",
|
||||
"description": "If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget).",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"uniqueItems": true
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"/apis/dashboard.grafana.app/v0alpha1/namespaces/{namespace}/snapshots/{name}/dashboard": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"Snapshot"
|
||||
],
|
||||
"description": "connect GET requests to dashboard of Snapshot",
|
||||
"operationId": "getSnapshotDashboard",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"content": {
|
||||
"*/*": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v0alpha1.Dashboard"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"x-kubernetes-action": "connect",
|
||||
"x-kubernetes-group-version-kind": {
|
||||
"group": "dashboard.grafana.app",
|
||||
"version": "v0alpha1",
|
||||
"kind": "Dashboard"
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "name",
|
||||
"in": "path",
|
||||
"description": "name of the Dashboard",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"uniqueItems": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "namespace",
|
||||
"in": "path",
|
||||
"description": "object name and auth scope, such as for teams and projects",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"uniqueItems": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
@@ -2758,6 +3199,127 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v0alpha1.Snapshot": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"metadata",
|
||||
"spec"
|
||||
],
|
||||
"properties": {
|
||||
"apiVersion": {
|
||||
"description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
|
||||
"type": "string"
|
||||
},
|
||||
"kind": {
|
||||
"description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
|
||||
"type": "string"
|
||||
},
|
||||
"metadata": {
|
||||
"default": {},
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta"
|
||||
}
|
||||
]
|
||||
},
|
||||
"spec": {
|
||||
"description": "Spec is the spec of the Snapshot",
|
||||
"default": {},
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v0alpha1.SnapshotSpec"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"x-kubernetes-group-version-kind": [
|
||||
{
|
||||
"group": "dashboard.grafana.app",
|
||||
"kind": "Snapshot",
|
||||
"version": "v0alpha1"
|
||||
}
|
||||
]
|
||||
},
|
||||
"com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v0alpha1.SnapshotList": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"metadata",
|
||||
"items"
|
||||
],
|
||||
"properties": {
|
||||
"apiVersion": {
|
||||
"description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
|
||||
"type": "string"
|
||||
},
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"default": {},
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v0alpha1.Snapshot"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"kind": {
|
||||
"description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
|
||||
"type": "string"
|
||||
},
|
||||
"metadata": {
|
||||
"default": {},
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"x-kubernetes-group-version-kind": [
|
||||
{
|
||||
"group": "dashboard.grafana.app",
|
||||
"kind": "SnapshotList",
|
||||
"version": "v0alpha1"
|
||||
}
|
||||
]
|
||||
},
|
||||
"com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v0alpha1.SnapshotSpec": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"dashboard": {
|
||||
"description": "The raw dashboard (unstructured for now)",
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"expires": {
|
||||
"description": "Optionally auto-remove the snapshot at a future date (Unix timestamp in seconds)",
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
},
|
||||
"external": {
|
||||
"description": "When set to true, the snapshot exists in a remote server",
|
||||
"type": "boolean"
|
||||
},
|
||||
"externalUrl": {
|
||||
"description": "The external URL where the snapshot can be seen",
|
||||
"type": "string"
|
||||
},
|
||||
"originalUrl": {
|
||||
"description": "The URL that created the dashboard originally",
|
||||
"type": "string"
|
||||
},
|
||||
"timestamp": {
|
||||
"description": "Snapshot creation timestamp",
|
||||
"type": "string"
|
||||
},
|
||||
"title": {
|
||||
"description": "Snapshot title",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"com.github.grafana.grafana.pkg.apimachinery.apis.common.v0alpha1.Unstructured": {
|
||||
"type": "object",
|
||||
"additionalProperties": true,
|
||||
|
||||
Reference in New Issue
Block a user