mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Scopes: Add Handler for returning dashboards related to a list of scopes. (#87758)
- Adds a find endpoint to return dashboard bindings that match any of the scopes. For example /apis/scope.grafana.app/v0alpha1/namespaces/default/find/scope_dashboard_bindings?scope=s1&scope=s2 - Updates the ScopeNode find endpoint to a new path, /find/scope_node_children , makes the key "items" for all find endpoints (instead of mix of "found" and "items"), and makes the list item type a ScopeNode instead of its own type. - Updates kubectl get commands to return more information about scopes, scopenodes, and scopedashboard bindings to display more fields in table output --------- Signed-off-by: bergquist <carl.bergquist@gmail.com> Co-authored-by: Ryan McKinley <ryantxu@gmail.com> Co-authored-by: Kyle Brandt <kyle@grafana.com> Co-authored-by: Todd Treece <todd.treece@grafana.com>
This commit is contained in:
parent
023857625a
commit
16cc75b02c
@ -57,7 +57,8 @@ func AddKnownTypes(gv schema.GroupVersion, scheme *runtime.Scheme) error {
|
|||||||
&ScopeDashboardBindingList{},
|
&ScopeDashboardBindingList{},
|
||||||
&ScopeNode{},
|
&ScopeNode{},
|
||||||
&ScopeNodeList{},
|
&ScopeNodeList{},
|
||||||
&TreeResults{},
|
&FindScopeNodeChildrenResults{},
|
||||||
|
&FindScopeDashboardBindingsResults{},
|
||||||
)
|
)
|
||||||
//metav1.AddToGroupVersion(scheme, gv)
|
//metav1.AddToGroupVersion(scheme, gv)
|
||||||
return nil
|
return nil
|
||||||
|
@ -74,6 +74,14 @@ type ScopeDashboardBindingList struct {
|
|||||||
Items []ScopeDashboardBinding `json:"items,omitempty"`
|
Items []ScopeDashboardBinding `json:"items,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||||
|
type FindScopeDashboardBindingsResults struct {
|
||||||
|
metav1.TypeMeta `json:",inline"`
|
||||||
|
|
||||||
|
Items []ScopeDashboardBinding `json:"items,omitempty"`
|
||||||
|
Message string `json:"message,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||||
type ScopeNode struct {
|
type ScopeNode struct {
|
||||||
metav1.TypeMeta `json:",inline"`
|
metav1.TypeMeta `json:",inline"`
|
||||||
@ -125,20 +133,9 @@ type ScopeNodeList struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||||
type TreeResults struct {
|
type FindScopeNodeChildrenResults struct {
|
||||||
metav1.TypeMeta `json:",inline"`
|
metav1.TypeMeta `json:",inline"`
|
||||||
metav1.ListMeta `json:"metadata,omitempty"`
|
metav1.ListMeta `json:"metadata,omitempty"`
|
||||||
|
|
||||||
Items []TreeItem `json:"items,omitempty"`
|
Items []ScopeNode `json:"items,omitempty"`
|
||||||
}
|
|
||||||
|
|
||||||
type TreeItem struct {
|
|
||||||
NodeID string `json:"nodeId,omitempty"`
|
|
||||||
NodeType NodeType `json:"nodeType"` // container | leaf
|
|
||||||
|
|
||||||
Title string `json:"title"`
|
|
||||||
Description string `json:"description,omitempty"`
|
|
||||||
|
|
||||||
LinkType LinkType `json:"linkType,omitempty"` // scope (later more things)
|
|
||||||
LinkID string `json:"linkId,omitempty"` // the k8s name
|
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,71 @@ import (
|
|||||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
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 *FindScopeDashboardBindingsResults) DeepCopyInto(out *FindScopeDashboardBindingsResults) {
|
||||||
|
*out = *in
|
||||||
|
out.TypeMeta = in.TypeMeta
|
||||||
|
if in.Items != nil {
|
||||||
|
in, out := &in.Items, &out.Items
|
||||||
|
*out = make([]ScopeDashboardBinding, len(*in))
|
||||||
|
for i := range *in {
|
||||||
|
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FindScopeDashboardBindingsResults.
|
||||||
|
func (in *FindScopeDashboardBindingsResults) DeepCopy() *FindScopeDashboardBindingsResults {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(FindScopeDashboardBindingsResults)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||||
|
func (in *FindScopeDashboardBindingsResults) 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 *FindScopeNodeChildrenResults) DeepCopyInto(out *FindScopeNodeChildrenResults) {
|
||||||
|
*out = *in
|
||||||
|
out.TypeMeta = in.TypeMeta
|
||||||
|
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||||
|
if in.Items != nil {
|
||||||
|
in, out := &in.Items, &out.Items
|
||||||
|
*out = make([]ScopeNode, len(*in))
|
||||||
|
for i := range *in {
|
||||||
|
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FindScopeNodeChildrenResults.
|
||||||
|
func (in *FindScopeNodeChildrenResults) DeepCopy() *FindScopeNodeChildrenResults {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(FindScopeNodeChildrenResults)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||||
|
func (in *FindScopeNodeChildrenResults) 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.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *Scope) DeepCopyInto(out *Scope) {
|
func (in *Scope) DeepCopyInto(out *Scope) {
|
||||||
*out = *in
|
*out = *in
|
||||||
@ -259,50 +324,3 @@ func (in *ScopeSpec) DeepCopy() *ScopeSpec {
|
|||||||
in.DeepCopyInto(out)
|
in.DeepCopyInto(out)
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
|
||||||
func (in *TreeItem) DeepCopyInto(out *TreeItem) {
|
|
||||||
*out = *in
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TreeItem.
|
|
||||||
func (in *TreeItem) DeepCopy() *TreeItem {
|
|
||||||
if in == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
out := new(TreeItem)
|
|
||||||
in.DeepCopyInto(out)
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
|
||||||
func (in *TreeResults) DeepCopyInto(out *TreeResults) {
|
|
||||||
*out = *in
|
|
||||||
out.TypeMeta = in.TypeMeta
|
|
||||||
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
|
||||||
if in.Items != nil {
|
|
||||||
in, out := &in.Items, &out.Items
|
|
||||||
*out = make([]TreeItem, len(*in))
|
|
||||||
copy(*out, *in)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TreeResults.
|
|
||||||
func (in *TreeResults) DeepCopy() *TreeResults {
|
|
||||||
if in == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
out := new(TreeResults)
|
|
||||||
in.DeepCopyInto(out)
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
|
||||||
func (in *TreeResults) DeepCopyObject() runtime.Object {
|
|
||||||
if c := in.DeepCopy(); c != nil {
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
@ -16,18 +16,112 @@ import (
|
|||||||
|
|
||||||
func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition {
|
func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition {
|
||||||
return map[string]common.OpenAPIDefinition{
|
return map[string]common.OpenAPIDefinition{
|
||||||
"github.com/grafana/grafana/pkg/apis/scope/v0alpha1.Scope": schema_pkg_apis_scope_v0alpha1_Scope(ref),
|
"github.com/grafana/grafana/pkg/apis/scope/v0alpha1.FindScopeDashboardBindingsResults": schema_pkg_apis_scope_v0alpha1_FindScopeDashboardBindingsResults(ref),
|
||||||
"github.com/grafana/grafana/pkg/apis/scope/v0alpha1.ScopeDashboardBinding": schema_pkg_apis_scope_v0alpha1_ScopeDashboardBinding(ref),
|
"github.com/grafana/grafana/pkg/apis/scope/v0alpha1.FindScopeNodeChildrenResults": schema_pkg_apis_scope_v0alpha1_FindScopeNodeChildrenResults(ref),
|
||||||
"github.com/grafana/grafana/pkg/apis/scope/v0alpha1.ScopeDashboardBindingList": schema_pkg_apis_scope_v0alpha1_ScopeDashboardBindingList(ref),
|
"github.com/grafana/grafana/pkg/apis/scope/v0alpha1.Scope": schema_pkg_apis_scope_v0alpha1_Scope(ref),
|
||||||
"github.com/grafana/grafana/pkg/apis/scope/v0alpha1.ScopeDashboardBindingSpec": schema_pkg_apis_scope_v0alpha1_ScopeDashboardBindingSpec(ref),
|
"github.com/grafana/grafana/pkg/apis/scope/v0alpha1.ScopeDashboardBinding": schema_pkg_apis_scope_v0alpha1_ScopeDashboardBinding(ref),
|
||||||
"github.com/grafana/grafana/pkg/apis/scope/v0alpha1.ScopeFilter": schema_pkg_apis_scope_v0alpha1_ScopeFilter(ref),
|
"github.com/grafana/grafana/pkg/apis/scope/v0alpha1.ScopeDashboardBindingList": schema_pkg_apis_scope_v0alpha1_ScopeDashboardBindingList(ref),
|
||||||
"github.com/grafana/grafana/pkg/apis/scope/v0alpha1.ScopeList": schema_pkg_apis_scope_v0alpha1_ScopeList(ref),
|
"github.com/grafana/grafana/pkg/apis/scope/v0alpha1.ScopeDashboardBindingSpec": schema_pkg_apis_scope_v0alpha1_ScopeDashboardBindingSpec(ref),
|
||||||
"github.com/grafana/grafana/pkg/apis/scope/v0alpha1.ScopeNode": schema_pkg_apis_scope_v0alpha1_ScopeNode(ref),
|
"github.com/grafana/grafana/pkg/apis/scope/v0alpha1.ScopeFilter": schema_pkg_apis_scope_v0alpha1_ScopeFilter(ref),
|
||||||
"github.com/grafana/grafana/pkg/apis/scope/v0alpha1.ScopeNodeList": schema_pkg_apis_scope_v0alpha1_ScopeNodeList(ref),
|
"github.com/grafana/grafana/pkg/apis/scope/v0alpha1.ScopeList": schema_pkg_apis_scope_v0alpha1_ScopeList(ref),
|
||||||
"github.com/grafana/grafana/pkg/apis/scope/v0alpha1.ScopeNodeSpec": schema_pkg_apis_scope_v0alpha1_ScopeNodeSpec(ref),
|
"github.com/grafana/grafana/pkg/apis/scope/v0alpha1.ScopeNode": schema_pkg_apis_scope_v0alpha1_ScopeNode(ref),
|
||||||
"github.com/grafana/grafana/pkg/apis/scope/v0alpha1.ScopeSpec": schema_pkg_apis_scope_v0alpha1_ScopeSpec(ref),
|
"github.com/grafana/grafana/pkg/apis/scope/v0alpha1.ScopeNodeList": schema_pkg_apis_scope_v0alpha1_ScopeNodeList(ref),
|
||||||
"github.com/grafana/grafana/pkg/apis/scope/v0alpha1.TreeItem": schema_pkg_apis_scope_v0alpha1_TreeItem(ref),
|
"github.com/grafana/grafana/pkg/apis/scope/v0alpha1.ScopeNodeSpec": schema_pkg_apis_scope_v0alpha1_ScopeNodeSpec(ref),
|
||||||
"github.com/grafana/grafana/pkg/apis/scope/v0alpha1.TreeResults": schema_pkg_apis_scope_v0alpha1_TreeResults(ref),
|
"github.com/grafana/grafana/pkg/apis/scope/v0alpha1.ScopeSpec": schema_pkg_apis_scope_v0alpha1_ScopeSpec(ref),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func schema_pkg_apis_scope_v0alpha1_FindScopeDashboardBindingsResults(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: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"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/scope/v0alpha1.ScopeDashboardBinding"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"message": {
|
||||||
|
SchemaProps: spec.SchemaProps{
|
||||||
|
Type: []string{"string"},
|
||||||
|
Format: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Dependencies: []string{
|
||||||
|
"github.com/grafana/grafana/pkg/apis/scope/v0alpha1.ScopeDashboardBinding"},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func schema_pkg_apis_scope_v0alpha1_FindScopeNodeChildrenResults(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/scope/v0alpha1.ScopeNode"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Dependencies: []string{
|
||||||
|
"github.com/grafana/grafana/pkg/apis/scope/v0alpha1.ScopeNode", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -471,106 +565,3 @@ func schema_pkg_apis_scope_v0alpha1_ScopeSpec(ref common.ReferenceCallback) comm
|
|||||||
"github.com/grafana/grafana/pkg/apis/scope/v0alpha1.ScopeFilter"},
|
"github.com/grafana/grafana/pkg/apis/scope/v0alpha1.ScopeFilter"},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func schema_pkg_apis_scope_v0alpha1_TreeItem(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
|
||||||
return common.OpenAPIDefinition{
|
|
||||||
Schema: spec.Schema{
|
|
||||||
SchemaProps: spec.SchemaProps{
|
|
||||||
Type: []string{"object"},
|
|
||||||
Properties: map[string]spec.Schema{
|
|
||||||
"nodeId": {
|
|
||||||
SchemaProps: spec.SchemaProps{
|
|
||||||
Type: []string{"string"},
|
|
||||||
Format: "",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"nodeType": {
|
|
||||||
SchemaProps: spec.SchemaProps{
|
|
||||||
Description: "Possible enum values:\n - `\"container\"`\n - `\"leaf\"`",
|
|
||||||
Default: "",
|
|
||||||
Type: []string{"string"},
|
|
||||||
Format: "",
|
|
||||||
Enum: []interface{}{"container", "leaf"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"title": {
|
|
||||||
SchemaProps: spec.SchemaProps{
|
|
||||||
Default: "",
|
|
||||||
Type: []string{"string"},
|
|
||||||
Format: "",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"description": {
|
|
||||||
SchemaProps: spec.SchemaProps{
|
|
||||||
Type: []string{"string"},
|
|
||||||
Format: "",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"linkType": {
|
|
||||||
SchemaProps: spec.SchemaProps{
|
|
||||||
Description: "Possible enum values:\n - `\"scope\"`",
|
|
||||||
Type: []string{"string"},
|
|
||||||
Format: "",
|
|
||||||
Enum: []interface{}{"scope"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"linkId": {
|
|
||||||
SchemaProps: spec.SchemaProps{
|
|
||||||
Description: "scope (later more things)",
|
|
||||||
Type: []string{"string"},
|
|
||||||
Format: "",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Required: []string{"nodeType", "title"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func schema_pkg_apis_scope_v0alpha1_TreeResults(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/scope/v0alpha1.TreeItem"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Dependencies: []string{
|
|
||||||
"github.com/grafana/grafana/pkg/apis/scope/v0alpha1.TreeItem", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,3 +1,2 @@
|
|||||||
|
API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/scope/v0alpha1,FindScopeDashboardBindingsResults,Items
|
||||||
API rule violation: names_match,github.com/grafana/grafana/pkg/apis/scope/v0alpha1,ScopeNodeSpec,LinkID
|
API rule violation: names_match,github.com/grafana/grafana/pkg/apis/scope/v0alpha1,ScopeNodeSpec,LinkID
|
||||||
API rule violation: names_match,github.com/grafana/grafana/pkg/apis/scope/v0alpha1,TreeItem,LinkID
|
|
||||||
API rule violation: names_match,github.com/grafana/grafana/pkg/apis/scope/v0alpha1,TreeItem,NodeID
|
|
||||||
|
@ -30,9 +30,9 @@ import (
|
|||||||
// TODO: this is a temporary hack to make rest.Connecter work with resource level routes
|
// TODO: this is a temporary hack to make rest.Connecter work with resource level routes
|
||||||
var pathRewriters = []filters.PathRewriter{
|
var pathRewriters = []filters.PathRewriter{
|
||||||
{
|
{
|
||||||
Pattern: regexp.MustCompile(`(/apis/scope.grafana.app/v0alpha1/namespaces/.*/find$)`),
|
Pattern: regexp.MustCompile(`(/apis/scope.grafana.app/v0alpha1/namespaces/.*/)find/(.*)$`),
|
||||||
ReplaceFunc: func(matches []string) string {
|
ReplaceFunc: func(matches []string) string {
|
||||||
return matches[1] + "/name" // connector requires a name
|
return matches[1] + matches[2] + "/name" // connector requires a name
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -29,7 +29,7 @@ var (
|
|||||||
|
|
||||||
func (r *findREST) New() runtime.Object {
|
func (r *findREST) New() runtime.Object {
|
||||||
// This is added as the "ResponseType" regarless what ProducesObject() says :)
|
// This is added as the "ResponseType" regarless what ProducesObject() says :)
|
||||||
return &scope.TreeResults{}
|
return &scope.FindScopeNodeChildrenResults{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *findREST) Destroy() {}
|
func (r *findREST) Destroy() {}
|
||||||
@ -39,7 +39,7 @@ func (r *findREST) NamespaceScoped() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *findREST) GetSingularName() string {
|
func (r *findREST) GetSingularName() string {
|
||||||
return "TreeResult" // Used for the
|
return "FindScopeNodeChildrenResults" // Used for the
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *findREST) ProducesMIMETypes(verb string) []string {
|
func (r *findREST) ProducesMIMETypes(verb string) []string {
|
||||||
@ -47,7 +47,7 @@ func (r *findREST) ProducesMIMETypes(verb string) []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *findREST) ProducesObject(verb string) interface{} {
|
func (r *findREST) ProducesObject(verb string) interface{} {
|
||||||
return &scope.TreeResults{}
|
return &scope.FindScopeNodeChildrenResults{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *findREST) ConnectMethods() []string {
|
func (r *findREST) ConnectMethods() []string {
|
||||||
@ -68,7 +68,7 @@ func (r *findREST) Connect(ctx context.Context, name string, opts runtime.Object
|
|||||||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||||
parent := req.URL.Query().Get("parent")
|
parent := req.URL.Query().Get("parent")
|
||||||
query := req.URL.Query().Get("query")
|
query := req.URL.Query().Get("query")
|
||||||
results := &scope.TreeResults{}
|
results := &scope.FindScopeNodeChildrenResults{}
|
||||||
|
|
||||||
raw, err := r.scopeNodeStorage.List(ctx, &internalversion.ListOptions{
|
raw, err := r.scopeNodeStorage.List(ctx, &internalversion.ListOptions{
|
||||||
Limit: 1000,
|
Limit: 1000,
|
||||||
@ -93,7 +93,7 @@ func (r *findREST) Connect(ctx context.Context, name string, opts runtime.Object
|
|||||||
}), nil
|
}), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func filterAndAppendItem(item scope.ScopeNode, parent string, query string, results *scope.TreeResults) {
|
func filterAndAppendItem(item scope.ScopeNode, parent string, query string, results *scope.FindScopeNodeChildrenResults) {
|
||||||
if parent != item.Spec.ParentName {
|
if parent != item.Spec.ParentName {
|
||||||
return // Someday this will have an index in raw storage on parentName
|
return // Someday this will have an index in raw storage on parentName
|
||||||
}
|
}
|
||||||
@ -104,12 +104,5 @@ func filterAndAppendItem(item scope.ScopeNode, parent string, query string, resu
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
results.Items = append(results.Items, scope.TreeItem{
|
results.Items = append(results.Items, item)
|
||||||
NodeID: item.Name,
|
|
||||||
NodeType: item.Spec.NodeType,
|
|
||||||
Title: item.Spec.Title,
|
|
||||||
Description: item.Spec.Description,
|
|
||||||
LinkType: item.Spec.LinkType,
|
|
||||||
LinkID: item.Spec.LinkID,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
96
pkg/registry/apis/scope/find_scope_dashboards.go
Normal file
96
pkg/registry/apis/scope/find_scope_dashboards.go
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
package scope
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
"k8s.io/apimachinery/pkg/apis/meta/internalversion"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
"k8s.io/apiserver/pkg/registry/rest"
|
||||||
|
|
||||||
|
scope "github.com/grafana/grafana/pkg/apis/scope/v0alpha1"
|
||||||
|
)
|
||||||
|
|
||||||
|
type findScopeDashboardsREST struct {
|
||||||
|
scopeDashboardStorage *storage
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ rest.Storage = (*findScopeDashboardsREST)(nil)
|
||||||
|
_ rest.SingularNameProvider = (*findScopeDashboardsREST)(nil)
|
||||||
|
_ rest.Connecter = (*findScopeDashboardsREST)(nil)
|
||||||
|
_ rest.Scoper = (*findScopeDashboardsREST)(nil)
|
||||||
|
_ rest.StorageMetadata = (*findScopeDashboardsREST)(nil)
|
||||||
|
)
|
||||||
|
|
||||||
|
func (f *findScopeDashboardsREST) New() runtime.Object {
|
||||||
|
return &scope.FindScopeDashboardBindingsResults{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *findScopeDashboardsREST) Destroy() {}
|
||||||
|
|
||||||
|
func (f *findScopeDashboardsREST) NamespaceScoped() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *findScopeDashboardsREST) GetSingularName() string {
|
||||||
|
return "FindScopeDashboardsResult" // not sure if this is actually used, but it is required to exist
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *findScopeDashboardsREST) ProducesMIMETypes(verb string) []string {
|
||||||
|
return []string{"application/json"} // and parquet!
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *findScopeDashboardsREST) ProducesObject(verb string) interface{} {
|
||||||
|
return &scope.FindScopeDashboardBindingsResults{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *findScopeDashboardsREST) ConnectMethods() []string {
|
||||||
|
return []string{"GET"}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *findScopeDashboardsREST) NewConnectOptions() (runtime.Object, bool, string) {
|
||||||
|
return nil, false, "" // true means you can use the trailing path as a variable
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *findScopeDashboardsREST) Connect(ctx context.Context, name string, opts runtime.Object, responder rest.Responder) (http.Handler, error) {
|
||||||
|
// See: /pkg/apiserver/builder/helper.go#L34
|
||||||
|
// The name is set with a rewriter hack
|
||||||
|
if name != "name" {
|
||||||
|
return nil, errors.NewNotFound(schema.GroupResource{}, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||||
|
raw, err := f.scopeDashboardStorage.List(ctx, &internalversion.ListOptions{})
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
all, ok := raw.(*scope.ScopeDashboardBindingList)
|
||||||
|
if !ok {
|
||||||
|
w.WriteHeader(500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
scopes := req.URL.Query()["scope"]
|
||||||
|
results := &scope.FindScopeDashboardBindingsResults{
|
||||||
|
Message: fmt.Sprintf("Find: %s", scopes),
|
||||||
|
Items: make([]scope.ScopeDashboardBinding, 0),
|
||||||
|
}
|
||||||
|
|
||||||
|
// we can improve the performance by calling .List once per scope if they are index by labels.
|
||||||
|
// The API stays the same thou.
|
||||||
|
for _, item := range all.Items {
|
||||||
|
for _, s := range scopes {
|
||||||
|
if item.Spec.Scope == s {
|
||||||
|
results.Items = append(results.Items, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
responder.Object(200, results)
|
||||||
|
}), nil
|
||||||
|
}
|
@ -54,7 +54,7 @@ func TestFilterAndAppendItem(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range tcs {
|
for _, tc := range tcs {
|
||||||
results := &scope.TreeResults{}
|
results := &scope.FindScopeNodeChildrenResults{}
|
||||||
item := scope.ScopeNode{
|
item := scope.ScopeNode{
|
||||||
Spec: scope.ScopeNodeSpec{
|
Spec: scope.ScopeNodeSpec{
|
||||||
ParentName: tc.ParentName,
|
ParentName: tc.ParentName,
|
||||||
|
@ -151,7 +151,12 @@ func (b *ScopeAPIBuilder) GetAPIGroupInfo(
|
|||||||
// Adds a rest.Connector
|
// Adds a rest.Connector
|
||||||
// NOTE! the server has a hardcoded rewrite filter that fills in a name
|
// NOTE! the server has a hardcoded rewrite filter that fills in a name
|
||||||
// so the standard k8s plumbing continues to work
|
// so the standard k8s plumbing continues to work
|
||||||
storage["find"] = &findREST{scopeNodeStorage: scopeNodeStorage}
|
storage["scope_node_children"] = &findREST{scopeNodeStorage: scopeNodeStorage}
|
||||||
|
|
||||||
|
// Adds a rest.Connector
|
||||||
|
// NOTE! the server has a hardcoded rewrite filter that fills in a name
|
||||||
|
// so the standard k8s plumbing continues to work
|
||||||
|
storage["scope_dashboard_bindings"] = &findScopeDashboardsREST{scopeDashboardStorage: scopeDashboardStorage}
|
||||||
|
|
||||||
apiGroupInfo.VersionedResourcesStorageMap[scope.VERSION] = storage
|
apiGroupInfo.VersionedResourcesStorageMap[scope.VERSION] = storage
|
||||||
return &apiGroupInfo, nil
|
return &apiGroupInfo, nil
|
||||||
@ -174,7 +179,7 @@ func (b *ScopeAPIBuilder) PostProcessOpenAPI(oas *spec3.OpenAPI) (*spec3.OpenAPI
|
|||||||
root := "/apis/" + b.GetGroupVersion().String() + "/"
|
root := "/apis/" + b.GetGroupVersion().String() + "/"
|
||||||
|
|
||||||
// Add query parameters to the rest.Connector
|
// Add query parameters to the rest.Connector
|
||||||
sub := oas.Paths.Paths[root+"namespaces/{namespace}/find/{name}"]
|
sub := oas.Paths.Paths[root+"namespaces/{namespace}/scope_node_children/{name}"]
|
||||||
if sub != nil && sub.Get != nil {
|
if sub != nil && sub.Get != nil {
|
||||||
sub.Parameters = []*spec3.Parameter{
|
sub.Parameters = []*spec3.Parameter{
|
||||||
{
|
{
|
||||||
@ -198,8 +203,35 @@ func (b *ScopeAPIBuilder) PostProcessOpenAPI(oas *spec3.OpenAPI) (*spec3.OpenAPI
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
delete(oas.Paths.Paths, root+"namespaces/{namespace}/find/{name}")
|
delete(oas.Paths.Paths, root+"namespaces/{namespace}/scope_node_children/{name}")
|
||||||
oas.Paths.Paths[root+"namespaces/{namespace}/find"] = sub
|
oas.Paths.Paths[root+"namespaces/{namespace}/find/scope_node_children"] = sub
|
||||||
|
}
|
||||||
|
|
||||||
|
findDashboardPath := oas.Paths.Paths[root+"namespaces/{namespace}/scope_dashboard_bindings/{name}"]
|
||||||
|
if findDashboardPath != nil && sub.Get != nil {
|
||||||
|
sub.Parameters = []*spec3.Parameter{
|
||||||
|
{
|
||||||
|
ParameterProps: spec3.ParameterProps{
|
||||||
|
Name: "namespace",
|
||||||
|
In: "path",
|
||||||
|
Description: "object name and auth scope, such as for teams and projects",
|
||||||
|
Example: "default",
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
findDashboardPath.Get.Description = "find scope dashboard bindings that match any of the given scopes."
|
||||||
|
findDashboardPath.Get.Parameters = []*spec3.Parameter{
|
||||||
|
{
|
||||||
|
ParameterProps: spec3.ParameterProps{
|
||||||
|
Name: "scope",
|
||||||
|
In: "query",
|
||||||
|
Description: "A scope name (id) to match against, this parameter may be repeated",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
delete(oas.Paths.Paths, root+"namespaces/{namespace}/scope_dashboard_bindings/{name}")
|
||||||
|
oas.Paths.Paths[root+"namespaces/{namespace}/find/scope_dashboard_bindings"] = findDashboardPath
|
||||||
}
|
}
|
||||||
|
|
||||||
// The root API discovery list
|
// The root API discovery list
|
||||||
|
@ -39,6 +39,8 @@ func newScopeStorage(scheme *runtime.Scheme, optsGetter generic.RESTOptionsGette
|
|||||||
[]metav1.TableColumnDefinition{
|
[]metav1.TableColumnDefinition{
|
||||||
{Name: "Name", Type: "string", Format: "name"},
|
{Name: "Name", Type: "string", Format: "name"},
|
||||||
{Name: "Created At", Type: "date"},
|
{Name: "Created At", Type: "date"},
|
||||||
|
{Name: "Title", Type: "string"},
|
||||||
|
{Name: "Filters", Type: "array"},
|
||||||
},
|
},
|
||||||
func(obj any) ([]interface{}, error) {
|
func(obj any) ([]interface{}, error) {
|
||||||
m, ok := obj.(*scope.Scope)
|
m, ok := obj.(*scope.Scope)
|
||||||
@ -48,6 +50,8 @@ func newScopeStorage(scheme *runtime.Scheme, optsGetter generic.RESTOptionsGette
|
|||||||
return []interface{}{
|
return []interface{}{
|
||||||
m.Name,
|
m.Name,
|
||||||
m.CreationTimestamp.UTC().Format(time.RFC3339),
|
m.CreationTimestamp.UTC().Format(time.RFC3339),
|
||||||
|
m.Spec.Title,
|
||||||
|
m.Spec.Filters,
|
||||||
}, nil
|
}, nil
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@ -77,15 +81,19 @@ func newScopeDashboardBindingStorage(scheme *runtime.Scheme, optsGetter generic.
|
|||||||
[]metav1.TableColumnDefinition{
|
[]metav1.TableColumnDefinition{
|
||||||
{Name: "Name", Type: "string", Format: "name"},
|
{Name: "Name", Type: "string", Format: "name"},
|
||||||
{Name: "Created At", Type: "date"},
|
{Name: "Created At", Type: "date"},
|
||||||
|
{Name: "Dashboard", Type: "string"},
|
||||||
|
{Name: "Scope", Type: "string"},
|
||||||
},
|
},
|
||||||
func(obj any) ([]interface{}, error) {
|
func(obj any) ([]interface{}, error) {
|
||||||
m, ok := obj.(*scope.Scope)
|
m, ok := obj.(*scope.ScopeDashboardBinding)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("expected scope")
|
return nil, fmt.Errorf("expected scope dashboard binding")
|
||||||
}
|
}
|
||||||
return []interface{}{
|
return []interface{}{
|
||||||
m.Name,
|
m.Name,
|
||||||
m.CreationTimestamp.UTC().Format(time.RFC3339),
|
m.CreationTimestamp.UTC().Format(time.RFC3339),
|
||||||
|
m.Spec.Dashboard,
|
||||||
|
m.Spec.Scope,
|
||||||
}, nil
|
}, nil
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@ -115,6 +123,11 @@ func newScopeNodeStorage(scheme *runtime.Scheme, optsGetter generic.RESTOptionsG
|
|||||||
[]metav1.TableColumnDefinition{
|
[]metav1.TableColumnDefinition{
|
||||||
{Name: "Name", Type: "string", Format: "name"},
|
{Name: "Name", Type: "string", Format: "name"},
|
||||||
{Name: "Created At", Type: "date"},
|
{Name: "Created At", Type: "date"},
|
||||||
|
{Name: "Title", Type: "string"},
|
||||||
|
{Name: "Parent Name", Type: "string"},
|
||||||
|
{Name: "Node Type", Type: "string"},
|
||||||
|
{Name: "Link Type", Type: "string"},
|
||||||
|
{Name: "Link ID", Type: "string"},
|
||||||
},
|
},
|
||||||
func(obj any) ([]interface{}, error) {
|
func(obj any) ([]interface{}, error) {
|
||||||
m, ok := obj.(*scope.ScopeNode)
|
m, ok := obj.(*scope.ScopeNode)
|
||||||
@ -124,6 +137,11 @@ func newScopeNodeStorage(scheme *runtime.Scheme, optsGetter generic.RESTOptionsG
|
|||||||
return []interface{}{
|
return []interface{}{
|
||||||
m.Name,
|
m.Name,
|
||||||
m.CreationTimestamp.UTC().Format(time.RFC3339),
|
m.CreationTimestamp.UTC().Format(time.RFC3339),
|
||||||
|
m.Spec.Title,
|
||||||
|
m.Spec.ParentName,
|
||||||
|
m.Spec.NodeType,
|
||||||
|
m.Spec.LinkType,
|
||||||
|
m.Spec.LinkID,
|
||||||
}, nil
|
}, nil
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -48,10 +48,19 @@ func TestIntegrationScopes(t *testing.T) {
|
|||||||
"groupVersion": "scope.grafana.app/v0alpha1",
|
"groupVersion": "scope.grafana.app/v0alpha1",
|
||||||
"resources": [
|
"resources": [
|
||||||
{
|
{
|
||||||
"name": "find",
|
"name": "scope_dashboard_bindings",
|
||||||
"singularName": "TreeResult",
|
"singularName": "FindScopeDashboardsResult",
|
||||||
"namespaced": true,
|
"namespaced": true,
|
||||||
"kind": "TreeResults",
|
"kind": "FindScopeDashboardBindingsResults",
|
||||||
|
"verbs": [
|
||||||
|
"get"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "scope_node_children",
|
||||||
|
"singularName": "FindScopeNodeChildrenResults",
|
||||||
|
"namespaced": true,
|
||||||
|
"kind": "FindScopeNodeChildrenResults",
|
||||||
"verbs": [
|
"verbs": [
|
||||||
"get"
|
"get"
|
||||||
]
|
]
|
||||||
|
Loading…
Reference in New Issue
Block a user