Merge remote-tracking branch 'origin/main' into grafana-git-ui-sync

This commit is contained in:
Ryan McKinley committed 2025-02-21 13:29:41 +03:00
commit cfa4923fca
39 files changed
+326 -2752

No files matched your search

@@ -121,29 +121,6 @@ For more information about assigning dashboard folder permissions, refer to [Gra
For more information about assigning dashboard permissions, refer to [Grant dashboard permissions]({{< relref "../user-management/manage-dashboard-permissions/#grant-dashboard-permissions" >}}).
## Editors with administrator permissions
If you have access to the Grafana server, you can modify the default editor role so that editors can use administrator permissions to manage dashboard folders, dashboards, and teams that they create.
{{% admonition type="note" %}}
This permission does not allow editors to manage folders, dashboards, and teams that they do not create.
{{% /admonition %}}
This setting can be used to enable self-organizing teams to administer their own dashboards.
For more information about assigning administrator permissions to editors, refer to [Grant editors administrator permissions]({{< relref "../user-management/server-user-management/grant-editor-admin-permissions/" >}}).
## Viewers with dashboard preview and Explore permissions
If you have access to the Grafana server, you can modify the default viewer role so that viewers can:
- Edit and preview dashboards, but cannot save their changes or create new dashboards.
- Access and use [Explore]({{< relref "../../explore" >}}).
Extending the viewer role is useful for public Grafana installations where you want anonymous users to be able to edit panels and queries, but not be able to save or create new dashboards.
For more information about assigning dashboard preview permissions to viewers, refer to [Enable viewers to preview dashboards and use Explore]({{< relref "../user-management/manage-dashboard-permissions/#enable-viewers-to-edit-but-not-save-dashboards-and-use-explore" >}}).
## Teams and permissions
A team is a group of users within an organization that have common dashboard and data source permission needs. For example, instead of assigning five users access to the same dashboard, you can create a team that consists of those users and assign dashboard permissions to the team. A user can belong to multiple teams.
@@ -153,7 +130,7 @@ You can assign a team member one of the following permissions:
- **Member**: Includes the user as a member of the team. Members do not have team administrator privileges.
- **Admin**: Administrators have permission to manage various aspects of the team, including team membership, permissions, and settings.
Because teams exist inside an organization, the organization administrator can manage all teams. When the `editors_can_admin` setting is enabled, editors can create teams and manage teams that they create. For more information about the `editors_can_admin` setting, refer to [Grant editors administrator permissions]({{< relref "../user-management/server-user-management/grant-editor-admin-permissions/" >}}).
Because teams exist inside an organization, the organization administrator can manage all teams.
For details on managing teams, see [Team management]({{< relref "../team-management/" >}}).
@@ -58,8 +58,8 @@ The following tables list permissions associated with basic and fixed roles.
| ------------- | --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Grafana Admin | `basic_grafana_admin` | `fixed:roles:reader`<br>`fixed:roles:writer`<br>`fixed:users:reader`<br>`fixed:users:writer`<br>`fixed:org.users:reader`<br>`fixed:org.users:writer`<br>`fixed:ldap:reader`<br>`fixed:ldap:writer`<br>`fixed:stats:reader`<br>`fixed:settings:reader`<br>`fixed:settings:writer`<br>`fixed:provisioning:writer`<br>`fixed:organization:reader`<br>`fixed:organization:maintainer`<br>`fixed:licensing:reader`<br>`fixed:licensing:writer`<br>`fixed:datasources.caching:reader`<br>`fixed:datasources.caching:writer`<br>`fixed:dashboards.insights:reader`<br>`fixed:datasources.insights:reader`<br>`fixed:plugins:maintainer`<br>`fixed:authentication.config:writer`<br>`fixed:library.panels:creator`<br>`fixed:library.panels:reader`<br>`fixed:library.panels:general.reader`<br>`fixed:library.panels:writer`<br>`fixed:library.panels:general.writer`<br>`fixed:groupsync:writer`<br>`fixed:migrationassistant:migrator` | Default [Grafana server administrator](/docs/grafana/<GRAFANA_VERSION>/administration/roles-and-permissions/#grafana-server-administrators) assignments. |
| Admin | `basic_admin` | `fixed:reports:reader`<br>`fixed:reports:writer`<br>`fixed:datasources:reader`<br>`fixed:datasources:writer`<br>`fixed:organization:writer`<br>`fixed:datasources.permissions:reader`<br>`fixed:datasources.permissions:writer`<br>`fixed:teams:writer`<br>`fixed:dashboards:reader`<br>`fixed:dashboards:writer`<br>`fixed:dashboards.permissions:reader`<br>`fixed:dashboards.permissions:writer`<br>`fixed:dashboards.public:writer`<br>`fixed:folders:reader`<br>`fixed:folders:writer`<br>`fixed:folders.permissions:reader`<br>`fixed:folders.permissions:writer`<br>`fixed:alerting:writer`<br>`fixed:apikeys:reader`<br>`fixed:apikeys:writer`<br>`fixed:alerting.provisioning.secrets:reader`<br>`fixed:alerting.provisioning:writer`<br>`fixed:datasources.caching:reader`<br>`fixed:datasources.caching:writer`<br>`fixed:dashboards.insights:reader`<br>`fixed:datasources.insights:reader`<br>`fixed:plugins:writer`<br>`fixed:library.panels:creator`<br>`fixed:library.panels:reader`<br>`fixed:library.panels:general.reader`<br>`fixed:library.panels:writer`<br>`fixed:library.panels:general.writer`<br>`fixed:alerting.provisioning.status:writer`<br>`fixed:groupsync:writer` | Default [Grafana organization administrator](ref:rbac-basic-roles) assignments. |
| Editor | `basic_editor` | `fixed:datasources:explorer`<br>`fixed:dashboards:creator`<br>`fixed:folders:creator`<br>`fixed:annotations:writer`<br>`fixed:teams:creator` if the `editors_can_admin` configuration flag is enabled<br>`fixed:alerting:writer`<br>`fixed:dashboards.insights:reader`<br>`fixed:datasources.insights:reader`<br>`fixed:library.panels:creator`<br>`fixed:library.panels:general.reader`<br>`fixed:library.panels:general.writer`<br>`fixed:alerting.provisioning.status:writer` | Default [Editor](ref:rbac-basic-roles) assignments. |
| Viewer | `basic_viewer` | `fixed:datasources.id:reader`<br>`fixed:organization:reader`<br>`fixed:annotations:reader`<br>`fixed:annotations.dashboard:writer`<br>`fixed:alerting:reader`<br>`fixed:plugins.app:reader`<br>`fixed:dashboards.insights:reader`<br>`fixed:datasources.insights:reader`<br>`fixed:library.panels:general.reader`<br>`fixed:datasources:explorer` if the `viewers_can_edit` configuration flag is enabled | Default [Viewer](ref:rbac-basic-roles) assignments. |
| Editor | `basic_editor` | `fixed:datasources:explorer`<br>`fixed:dashboards:creator`<br>`fixed:folders:creator`<br>`fixed:annotations:writer`<br>`fixed:alerting:writer`<br>`fixed:dashboards.insights:reader`<br>`fixed:datasources.insights:reader`<br>`fixed:library.panels:creator`<br>`fixed:library.panels:general.reader`<br>`fixed:library.panels:general.writer`<br>`fixed:alerting.provisioning.status:writer` | Default [Editor](ref:rbac-basic-roles) assignments. |
| Viewer | `basic_viewer` | `fixed:datasources.id:reader`<br>`fixed:organization:reader`<br>`fixed:annotations:reader`<br>`fixed:annotations.dashboard:writer`<br>`fixed:alerting:reader`<br>`fixed:plugins.app:reader`<br>`fixed:dashboards.insights:reader`<br>`fixed:datasources.insights:reader`<br>`fixed:library.panels:general.reader` | Default [Viewer](ref:rbac-basic-roles) assignments. |
| No Basic Role | n/a | | Default [No Basic Role](ref:rbac-basic-roles) |
## Fixed role definitions
@@ -63,27 +63,7 @@ Grant dashboard permissions when you want to restrict or enhance dashboard acces
1. Select the user, service account, team, or role.
1. Select the permission and click **Save**.
## Enable viewers to edit (but not save) dashboards and use Explore
By default, the viewer organization role does not allow viewers to create dashboards or use the Explore feature. However, by modifying a configuration setting, you can allow viewers to edit a panel and make changes to a dashboard but not save those changes. This setting also enables viewers to use the Explore feature.
This modification is useful for public Grafana installations where you want anonymous users to be able to edit panels and queries but not save or create new dashboards.
### Before you begin
- Ensure that you have access to the Grafana server
**To enable viewers to preview dashboards and use Explore**:
1. Open the Grafana configuration file.
For more information about the Grafana configuration file and its location, refer to [Configuration]({{< relref "../../../setup-grafana/configure-grafana/" >}}).
1. Locate the `viewers_can_edit` parameter.
1. Set the `viewers_can_edit` value to `true`.
1. Save your changes and restart Grafana.
## Edit dashboard permissions
# Edit dashboard permissions
Edit dashboard permissions when you are want to enhance or restrict a user's access to a dashboard. For more information about dashboard permissions, refer to [Dashboard permissions]({{< relref "../../roles-and-permissions/#dashboard-permissions" >}}).
@@ -1,30 +0,0 @@
---
aliases:
- ../../manage-users-and-permissions/manage-server-users/grant-editor-admin-permissions/
labels:
products:
- enterprise
- oss
title: Grant editors team creator permissions
weight: 60
---
# Grant editors team creator permissions
By default, the editor organization role does not allow editors to creator and manage teams. You can allow them to do so using the `editors_can_admin` configuration option.
When `editors_can_admin` is enabled, users with the Editor role in an organization can create teams, and they are Administrators of the teams they create. To learn more about team permissions, refer to [Team management]({{< relref "../../../team-management/" >}}).
## Before you begin
- Ensure that you have access to the Grafana server
**To enable editors with team creator permissions**:
1. Log in to the Grafana server and open the Grafana configuration file.
For more information about the Grafana configuration file and its location, refer to [Configuration]({{< relref "../../../../setup-grafana/configure-grafana/" >}}).
1. Locate the `editors_can_admin` parameter.
1. Set the `editors_can_admin` value to `true`.
1. Save your changes and restart the Grafana server.
-2
View File
@@ -26,8 +26,6 @@ Access to these API endpoints is restricted as follows:
- All authenticated users are able to view details of teams they are a member of.
- Organization Admins are able to manage all teams and team members.
- If you enable `editors_can_admin` configuration flag, then Organization Editors can create teams and manage teams where they are Admin.
- If you enable `editors_can_admin` configuration flag, Editors can find out whether a team that they are not members of exists by trying to create a team with the same name.
> If you are running Grafana Enterprise, for some endpoints you'll need to have specific permissions. Refer to [Role-based access control permissions]({{< relref "/docs/grafana/latest/administration/roles-and-permissions/access-control/custom-role-actions-scopes" >}}) for more information.
@@ -38,14 +38,10 @@ Watch the following video to get started using Explore:
## Before you begin
In order to access Explore, you must have either the `editor` or `administrator` role, unless the [`viewers_can_edit` option](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/#viewers_can_edit) is enabled. Refer to [Role and permissions](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/administration/roles-and-permissions/) for more information on what each role can access.
In order to access Explore, you must have either the `editor` or `administrator` basic role or the `data sources explore` role. Refer to [Role and permissions](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/administration/roles-and-permissions/) for more information on what each role can access.
Refer to [Role-based access control (RBAC)](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/administration/roles-and-permissions/access-control/) in Grafana Enterprise to understand how you can manage Explore with role-based permissions.
{{< admonition type="note" >}}
If you are using Grafana Cloud, open a [support ticket in the Cloud Portal](https://grafana.com/auth/sign-in) to enable the `viewers_can_edit` option.
{{< /admonition >}}
## Explore elements
Explore consists of a toolbar, outline, query editor, the ability to add multiple queries, a query history and a query inspector.
@@ -950,10 +950,18 @@ If you manage users externally you can replace the user invite button for organi
#### `viewers_can_edit`
{{< admonition type="note" >}}
This option is deprecated - assign your viewers as editors, if you are using RBAC assign the data sources explorer role to your users.
{{< /admonition >}}
Viewers can access and use [Explore]({{< relref "../../explore" >}}) and perform temporary edits on panels in dashboards they have access to. They cannot save their changes. Default is `false`.
#### `editors_can_admin`
{{< admonition type="note" >}}
This option is deprecated - assign your editors as admins, if you are using RBAC assign the team creator role to your users.
{{< /admonition >}}
Editors can administrate dashboards, folders and teams they create.
Default is `false`.
+5
View File
@@ -618,6 +618,11 @@ github.com/grafana/cog v0.0.23/go.mod h1:jrS9indvWuDs60RHEZpLaAkmZdgyoLKMOEUT0ji
github.com/grafana/go-gelf/v2 v2.0.1 h1:BOChP0h/jLeD+7F9mL7tq10xVkDG15he3T1zHuQaWak=
github.com/grafana/go-gelf/v2 v2.0.1/go.mod h1:lexHie0xzYGwCgiRGcvZ723bSNyNI8ZRD4s0CLobh90=
github.com/grafana/grafana/apps/advisor v0.0.0-20250220154326-6e5de80ef295/go.mod h1:9I1dKV3Dqr0NPR9Af0WJGxOytp5/6W3JLiNChOz8r+c=
github.com/grafana/grafana/apps/advisor v0.0.0-20250220163425-b4c4b9abbdc8/go.mod h1:9I1dKV3Dqr0NPR9Af0WJGxOytp5/6W3JLiNChOz8r+c=
github.com/grafana/grafana/apps/alerting/notifications v0.0.0-20250220163425-b4c4b9abbdc8/go.mod h1:dHhFF484qs1cmdIShKCB3kl+tMJyc4yuwgTQ3Afz37o=
github.com/grafana/grafana/apps/investigations v0.0.0-20250220163425-b4c4b9abbdc8/go.mod h1:ygFcJP2McdSeMJVj/3YrKafZMc/lZBsp54HO51MtJYw=
github.com/grafana/grafana/apps/playlist v0.0.0-20250220164708-c8d4ff28a450/go.mod h1:KKIsWpbv88Lwwcvdjon73zFL7vNJvuXLtsSoUjJErTw=
github.com/grafana/grafana/pkg/build v0.0.0-20250220114259-be81314e2118/go.mod h1:STVpVboMYeBAfyn6Zw6XHhTHqUxzMy7pzRiVgk1l0W0=
github.com/grafana/tail v0.0.0-20230510142333-77b18831edf0 h1:bjh0PVYSVVFxzINqPFYJmAmJNrWPgnVjuSdYJGHmtFU=
github.com/grafana/tail v0.0.0-20230510142333-77b18831edf0/go.mod h1:7t5XR+2IA8P2qggOAHTj/GCZfoLBle3OvNSYh1VkRBU=
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA=
@@ -255,7 +255,6 @@ export function SelectBase<T, Rest = {}>({
menuIsOpen: isOpen,
menuPlacement: menuPlacement === 'auto' && closeToBottom ? 'top' : menuPlacement,
menuPosition,
menuShouldBlockScroll: true,
menuPortalTarget: menuShouldPortal && typeof document !== 'undefined' ? document.body : undefined,
menuShouldScrollIntoView: false,
onBlur,
@@ -1,3 +1,4 @@
// +k8s:deepcopy-gen=package
// +k8s:openapi-gen=true
// +k8s:defaulter-gen=TypeMeta
// +groupName=common.grafana.app
@@ -1,5 +1,9 @@
package v0alpha1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// Similar to
// https://dev-k8sref-io.web.app/docs/common-definitions/objectreference-/
// ObjectReference contains enough information to let you inspect or modify the referred object.
@@ -15,3 +19,41 @@ type ObjectReference struct {
// APIVersion is the version of the API group that contains the referred object.
APIVersion string `json:"apiVersion,omitempty"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type Scope struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec ScopeSpec `json:"spec,omitempty"`
}
type ScopeSpec struct {
Title string `json:"title"`
Description string `json:"description"`
// +listType=atomic
Filters []ScopeFilter `json:"filters"`
}
type ScopeFilter struct {
Key string `json:"key"`
Value string `json:"value"`
// Values is used for operators that require multiple values (e.g. one-of and not-one-of).
Values []string `json:"values,omitempty"`
Operator FilterOperator `json:"operator"`
}
// Type of the filter operator.
// +enum
type FilterOperator string
// Defines values for FilterOperator.
const (
FilterOperatorEquals FilterOperator = "equals"
FilterOperatorNotEquals FilterOperator = "not-equals"
FilterOperatorRegexMatch FilterOperator = "regex-match"
FilterOperatorRegexNotMatch FilterOperator = "regex-not-match"
FilterOperatorOneOf FilterOperator = "one-of"
FilterOperatorNotOneOf FilterOperator = "not-one-of"
)
@@ -0,0 +1,99 @@
//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 *ObjectReference) DeepCopyInto(out *ObjectReference) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ObjectReference.
func (in *ObjectReference) DeepCopy() *ObjectReference {
if in == nil {
return nil
}
out := new(ObjectReference)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Scope) DeepCopyInto(out *Scope) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Scope.
func (in *Scope) DeepCopy() *Scope {
if in == nil {
return nil
}
out := new(Scope)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *Scope) 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 *ScopeFilter) DeepCopyInto(out *ScopeFilter) {
*out = *in
if in.Values != nil {
in, out := &in.Values, &out.Values
*out = make([]string, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScopeFilter.
func (in *ScopeFilter) DeepCopy() *ScopeFilter {
if in == nil {
return nil
}
out := new(ScopeFilter)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ScopeSpec) DeepCopyInto(out *ScopeSpec) {
*out = *in
if in.Filters != nil {
in, out := &in.Filters, &out.Filters
*out = make([]ScopeFilter, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScopeSpec.
func (in *ScopeSpec) DeepCopy() *ScopeSpec {
if in == nil {
return nil
}
out := new(ScopeSpec)
in.DeepCopyInto(out)
return out
}
@@ -16,6 +16,9 @@ import (
func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition {
return map[string]common.OpenAPIDefinition{
"github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.ObjectReference": schema_apimachinery_apis_common_v0alpha1_ObjectReference(ref),
"github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.Scope": schema_apimachinery_apis_common_v0alpha1_Scope(ref),
"github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.ScopeFilter": schema_apimachinery_apis_common_v0alpha1_ScopeFilter(ref),
"github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.ScopeSpec": schema_apimachinery_apis_common_v0alpha1_ScopeSpec(ref),
"github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.Unstructured": Unstructured{}.OpenAPIDefinition(),
"k8s.io/apimachinery/pkg/apis/meta/v1.APIGroup": schema_pkg_apis_meta_v1_APIGroup(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.APIGroupList": schema_pkg_apis_meta_v1_APIGroupList(ref),
@@ -118,6 +121,144 @@ func schema_apimachinery_apis_common_v0alpha1_ObjectReference(ref common.Referen
}
}
func schema_apimachinery_apis_common_v0alpha1_Scope(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{
Default: map[string]interface{}{},
Ref: ref("github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.ScopeSpec"),
},
},
},
},
},
Dependencies: []string{
"github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.ScopeSpec", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"},
}
}
func schema_apimachinery_apis_common_v0alpha1_ScopeFilter(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"object"},
Properties: map[string]spec.Schema{
"key": {
SchemaProps: spec.SchemaProps{
Default: "",
Type: []string{"string"},
Format: "",
},
},
"value": {
SchemaProps: spec.SchemaProps{
Default: "",
Type: []string{"string"},
Format: "",
},
},
"values": {
SchemaProps: spec.SchemaProps{
Description: "Values is used for operators that require multiple values (e.g. one-of and not-one-of).",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Default: "",
Type: []string{"string"},
Format: "",
},
},
},
},
},
"operator": {
SchemaProps: spec.SchemaProps{
Description: "Possible enum values:\n - `\"equals\"`\n - `\"not-equals\"`\n - `\"not-one-of\"`\n - `\"one-of\"`\n - `\"regex-match\"`\n - `\"regex-not-match\"`",
Default: "",
Type: []string{"string"},
Format: "",
Enum: []interface{}{"equals", "not-equals", "not-one-of", "one-of", "regex-match", "regex-not-match"},
},
},
},
Required: []string{"key", "value", "operator"},
},
},
}
}
func schema_apimachinery_apis_common_v0alpha1_ScopeSpec(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{
Default: "",
Type: []string{"string"},
Format: "",
},
},
"description": {
SchemaProps: spec.SchemaProps{
Default: "",
Type: []string{"string"},
Format: "",
},
},
"filters": {
VendorExtensible: spec.VendorExtensible{
Extensions: spec.Extensions{
"x-kubernetes-list-type": "atomic",
},
},
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/apimachinery/apis/common/v0alpha1.ScopeFilter"),
},
},
},
},
},
},
Required: []string{"title", "description", "filters"},
},
},
Dependencies: []string{
"github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.ScopeFilter"},
}
}
func schema_pkg_apis_meta_v1_APIGroup(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
@@ -1,3 +1,4 @@
API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1,ScopeFilter,Values
API rule violation: names_match,github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1,Unstructured,Object
API rule violation: names_match,k8s.io/apimachinery/pkg/apis/meta/v1,APIResourceList,APIResources
API rule violation: names_match,k8s.io/apimachinery/pkg/apis/meta/v1,Duration,Duration
-6
View File
@@ -1,6 +0,0 @@
// +k8s:deepcopy-gen=package
// +k8s:openapi-gen=true
// +k8s:defaulter-gen=TypeMeta
// +groupName=scope.grafana.app
package v0alpha1 // import "github.com/grafana/grafana/pkg/apis/scope/v0alpha1"
-139
View File
@@ -1,139 +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 = "scope.grafana.app"
VERSION = "v0alpha1"
APIVERSION = GROUP + "/" + VERSION
)
var ScopeResourceInfo = utils.NewResourceInfo(GROUP, VERSION,
"scopes", "scope", "Scope",
func() runtime.Object { return &Scope{} },
func() runtime.Object { return &ScopeList{} },
utils.TableColumns{
Definition: []metav1.TableColumnDefinition{
{Name: "Name", Type: "string", Format: "name"},
{Name: "Created At", Type: "date"},
{Name: "Title", Type: "string"},
{Name: "Filters", Type: "array"},
},
Reader: func(obj any) ([]interface{}, error) {
m, ok := obj.(*Scope)
if !ok {
return nil, fmt.Errorf("expected scope")
}
return []interface{}{
m.Name,
m.CreationTimestamp.UTC().Format(time.RFC3339),
m.Spec.Title,
m.Spec.Filters,
}, nil
},
}, // default table converter
)
var ScopeDashboardBindingResourceInfo = utils.NewResourceInfo(GROUP, VERSION,
"scopedashboardbindings", "scopedashboardbinding", "ScopeDashboardBinding",
func() runtime.Object { return &ScopeDashboardBinding{} },
func() runtime.Object { return &ScopeDashboardBindingList{} },
utils.TableColumns{
Definition: []metav1.TableColumnDefinition{
{Name: "Name", Type: "string", Format: "name"},
{Name: "Created At", Type: "date"},
{Name: "Dashboard", Type: "string"},
{Name: "Scope", Type: "string"},
},
Reader: func(obj any) ([]interface{}, error) {
m, ok := obj.(*ScopeDashboardBinding)
if !ok {
return nil, fmt.Errorf("expected scope dashboard binding")
}
return []interface{}{
m.Name,
m.CreationTimestamp.UTC().Format(time.RFC3339),
m.Spec.Dashboard,
m.Spec.Scope,
}, nil
},
},
)
var ScopeNodeResourceInfo = utils.NewResourceInfo(GROUP, VERSION,
"scopenodes", "scopenode", "ScopeNode",
func() runtime.Object { return &ScopeNode{} },
func() runtime.Object { return &ScopeNodeList{} },
utils.TableColumns{
Definition: []metav1.TableColumnDefinition{
{Name: "Name", Type: "string", Format: "name"},
{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"},
},
Reader: func(obj any) ([]interface{}, error) {
m, ok := obj.(*ScopeNode)
if !ok {
return nil, fmt.Errorf("expected scope node")
}
return []interface{}{
m.Name,
m.CreationTimestamp.UTC().Format(time.RFC3339),
m.Spec.Title,
m.Spec.ParentName,
m.Spec.NodeType,
m.Spec.LinkType,
m.Spec.LinkID,
}, nil
},
}, // default table converter
)
var (
// SchemeGroupVersion is group version used to register these objects
SchemeGroupVersion = schema.GroupVersion{Group: GROUP, Version: VERSION}
InternalGroupVersion = schema.GroupVersion{Group: GROUP, Version: runtime.APIVersionInternal}
// SchemaBuilder is used by standard codegen
SchemeBuilder runtime.SchemeBuilder
localSchemeBuilder = &SchemeBuilder
AddToScheme = localSchemeBuilder.AddToScheme
)
func init() {
localSchemeBuilder.Register(func(s *runtime.Scheme) error {
return AddKnownTypes(SchemeGroupVersion, s)
})
}
// Adds the list of known types to the given scheme.
func AddKnownTypes(gv schema.GroupVersion, scheme *runtime.Scheme) error {
scheme.AddKnownTypes(gv,
&Scope{},
&ScopeList{},
&ScopeDashboardBinding{},
&ScopeDashboardBindingList{},
&ScopeNode{},
&ScopeNodeList{},
&FindScopeNodeChildrenResults{},
&FindScopeDashboardBindingsResults{},
)
//metav1.AddToGroupVersion(scheme, gv)
return nil
}
// Resource takes an unqualified resource and returns a Group qualified GroupResource
func Resource(resource string) schema.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}
-167
View File
@@ -1,167 +0,0 @@
package v0alpha1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
/*
Please keep pkg/promlib/models/query.go and pkg/promlib/models/scope.go in sync
with this file until this package is out of the grafana/grafana module.
*/
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type Scope struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec ScopeSpec `json:"spec,omitempty"`
}
type ScopeSpec struct {
Title string `json:"title"`
Description string `json:"description"`
// +listType=atomic
Filters []ScopeFilter `json:"filters"`
}
type ScopeFilter struct {
Key string `json:"key"`
Value string `json:"value"`
// Values is used for operators that require multiple values (e.g. one-of and not-one-of).
Values []string `json:"values,omitempty"`
Operator FilterOperator `json:"operator"`
}
// Type of the filter operator.
// +enum
type FilterOperator string
// Defines values for FilterOperator.
const (
FilterOperatorEquals FilterOperator = "equals"
FilterOperatorNotEquals FilterOperator = "not-equals"
FilterOperatorRegexMatch FilterOperator = "regex-match"
FilterOperatorRegexNotMatch FilterOperator = "regex-not-match"
FilterOperatorOneOf FilterOperator = "one-of"
FilterOperatorNotOneOf FilterOperator = "not-one-of"
)
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type ScopeList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []Scope `json:"items,omitempty"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type ScopeDashboardBinding struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec ScopeDashboardBindingSpec `json:"spec,omitempty"`
Status ScopeDashboardBindingStatus `json:"status,omitempty"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type ScopeDashboardBindingList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,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
type ScopeNode struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec ScopeNodeSpec `json:"spec,omitempty"`
}
type ScopeDashboardBindingSpec struct {
Dashboard string `json:"dashboard"`
Scope string `json:"scope"`
}
// Type of the item.
// +enum
// ScopeDashboardBindingStatus contains derived information about a ScopeDashboardBinding.
type ScopeDashboardBindingStatus struct {
// DashboardTitle should be populated and update from the dashboard
DashboardTitle string `json:"dashboardTitle"`
// Groups is used for the grouping of dashboards that are suggested based
// on a scope. The source of truth for this information has not been
// determined yet.
Groups []string `json:"groups,omitempty"`
// DashboardTitleConditions is a list of conditions that are used to determine if the dashboard title is valid.
// +optional
// +listType=map
// +listMapKey=type
DashboardTitleConditions []metav1.Condition `json:"dashboardTitleConditions,omitempty"`
// DashboardTitleConditions is a list of conditions that are used to determine if the list of groups is valid.
// +optional
// +listType=map
// +listMapKey=type
GroupsConditions []metav1.Condition `json:"groupsConditions,omitempty"`
}
type NodeType string
// Defines values for ItemType.
const (
NodeTypeContainer NodeType = "container"
NodeTypeLeaf NodeType = "leaf"
)
// Type of the item.
// +enum
type LinkType string
// Defines values for ItemType.
const (
LinkTypeScope LinkType = "scope"
)
type ScopeNodeSpec struct {
//+optional
ParentName string `json:"parentName,omitempty"`
NodeType NodeType `json:"nodeType"` // container | leaf
Title string `json:"title"`
Description string `json:"description,omitempty"`
DisableMultiSelect bool `json:"disableMultiSelect"`
LinkType LinkType `json:"linkType,omitempty"` // scope (later more things)
LinkID string `json:"linkId,omitempty"` // the k8s name
// ?? should this be a slice of links
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type ScopeNodeList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []ScopeNode `json:"items,omitempty"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type FindScopeNodeChildrenResults struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []ScopeNode `json:"items,omitempty"`
}
@@ -1,370 +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 (
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
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.
func (in *Scope) DeepCopyInto(out *Scope) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Scope.
func (in *Scope) DeepCopy() *Scope {
if in == nil {
return nil
}
out := new(Scope)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *Scope) 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 *ScopeDashboardBinding) DeepCopyInto(out *ScopeDashboardBinding) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
out.Spec = in.Spec
in.Status.DeepCopyInto(&out.Status)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScopeDashboardBinding.
func (in *ScopeDashboardBinding) DeepCopy() *ScopeDashboardBinding {
if in == nil {
return nil
}
out := new(ScopeDashboardBinding)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *ScopeDashboardBinding) 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 *ScopeDashboardBindingList) DeepCopyInto(out *ScopeDashboardBindingList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
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 ScopeDashboardBindingList.
func (in *ScopeDashboardBindingList) DeepCopy() *ScopeDashboardBindingList {
if in == nil {
return nil
}
out := new(ScopeDashboardBindingList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *ScopeDashboardBindingList) 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 *ScopeDashboardBindingSpec) DeepCopyInto(out *ScopeDashboardBindingSpec) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScopeDashboardBindingSpec.
func (in *ScopeDashboardBindingSpec) DeepCopy() *ScopeDashboardBindingSpec {
if in == nil {
return nil
}
out := new(ScopeDashboardBindingSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ScopeDashboardBindingStatus) DeepCopyInto(out *ScopeDashboardBindingStatus) {
*out = *in
if in.Groups != nil {
in, out := &in.Groups, &out.Groups
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.DashboardTitleConditions != nil {
in, out := &in.DashboardTitleConditions, &out.DashboardTitleConditions
*out = make([]v1.Condition, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.GroupsConditions != nil {
in, out := &in.GroupsConditions, &out.GroupsConditions
*out = make([]v1.Condition, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScopeDashboardBindingStatus.
func (in *ScopeDashboardBindingStatus) DeepCopy() *ScopeDashboardBindingStatus {
if in == nil {
return nil
}
out := new(ScopeDashboardBindingStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ScopeFilter) DeepCopyInto(out *ScopeFilter) {
*out = *in
if in.Values != nil {
in, out := &in.Values, &out.Values
*out = make([]string, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScopeFilter.
func (in *ScopeFilter) DeepCopy() *ScopeFilter {
if in == nil {
return nil
}
out := new(ScopeFilter)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ScopeList) DeepCopyInto(out *ScopeList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]Scope, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScopeList.
func (in *ScopeList) DeepCopy() *ScopeList {
if in == nil {
return nil
}
out := new(ScopeList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *ScopeList) 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 *ScopeNode) DeepCopyInto(out *ScopeNode) {
*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 ScopeNode.
func (in *ScopeNode) DeepCopy() *ScopeNode {
if in == nil {
return nil
}
out := new(ScopeNode)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *ScopeNode) 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 *ScopeNodeList) DeepCopyInto(out *ScopeNodeList) {
*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 ScopeNodeList.
func (in *ScopeNodeList) DeepCopy() *ScopeNodeList {
if in == nil {
return nil
}
out := new(ScopeNodeList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *ScopeNodeList) 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 *ScopeNodeSpec) DeepCopyInto(out *ScopeNodeSpec) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScopeNodeSpec.
func (in *ScopeNodeSpec) DeepCopy() *ScopeNodeSpec {
if in == nil {
return nil
}
out := new(ScopeNodeSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ScopeSpec) DeepCopyInto(out *ScopeSpec) {
*out = *in
if in.Filters != nil {
in, out := &in.Filters, &out.Filters
*out = make([]ScopeFilter, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScopeSpec.
func (in *ScopeSpec) DeepCopy() *ScopeSpec {
if in == nil {
return nil
}
out := new(ScopeSpec)
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,661 +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/scope/v0alpha1.FindScopeDashboardBindingsResults": schema_pkg_apis_scope_v0alpha1_FindScopeDashboardBindingsResults(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.Scope": schema_pkg_apis_scope_v0alpha1_Scope(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.ScopeDashboardBindingList": schema_pkg_apis_scope_v0alpha1_ScopeDashboardBindingList(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.ScopeDashboardBindingStatus": schema_pkg_apis_scope_v0alpha1_ScopeDashboardBindingStatus(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.ScopeList": schema_pkg_apis_scope_v0alpha1_ScopeList(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.ScopeNodeList": schema_pkg_apis_scope_v0alpha1_ScopeNodeList(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.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"},
}
}
func schema_pkg_apis_scope_v0alpha1_Scope(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{
Default: map[string]interface{}{},
Ref: ref("github.com/grafana/grafana/pkg/apis/scope/v0alpha1.ScopeSpec"),
},
},
},
},
},
Dependencies: []string{
"github.com/grafana/grafana/pkg/apis/scope/v0alpha1.ScopeSpec", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"},
}
}
func schema_pkg_apis_scope_v0alpha1_ScopeDashboardBinding(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{
Default: map[string]interface{}{},
Ref: ref("github.com/grafana/grafana/pkg/apis/scope/v0alpha1.ScopeDashboardBindingSpec"),
},
},
"status": {
SchemaProps: spec.SchemaProps{
Default: map[string]interface{}{},
Ref: ref("github.com/grafana/grafana/pkg/apis/scope/v0alpha1.ScopeDashboardBindingStatus"),
},
},
},
},
},
Dependencies: []string{
"github.com/grafana/grafana/pkg/apis/scope/v0alpha1.ScopeDashboardBindingSpec", "github.com/grafana/grafana/pkg/apis/scope/v0alpha1.ScopeDashboardBindingStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"},
}
}
func schema_pkg_apis_scope_v0alpha1_ScopeDashboardBindingList(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.ScopeDashboardBinding"),
},
},
},
},
},
},
},
},
Dependencies: []string{
"github.com/grafana/grafana/pkg/apis/scope/v0alpha1.ScopeDashboardBinding", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"},
}
}
func schema_pkg_apis_scope_v0alpha1_ScopeDashboardBindingSpec(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"object"},
Properties: map[string]spec.Schema{
"dashboard": {
SchemaProps: spec.SchemaProps{
Default: "",
Type: []string{"string"},
Format: "",
},
},
"scope": {
SchemaProps: spec.SchemaProps{
Default: "",
Type: []string{"string"},
Format: "",
},
},
},
Required: []string{"dashboard", "scope"},
},
},
}
}
func schema_pkg_apis_scope_v0alpha1_ScopeDashboardBindingStatus(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "Type of the item. ScopeDashboardBindingStatus contains derived information about a ScopeDashboardBinding.",
Type: []string{"object"},
Properties: map[string]spec.Schema{
"dashboardTitle": {
SchemaProps: spec.SchemaProps{
Description: "DashboardTitle should be populated and update from the dashboard",
Default: "",
Type: []string{"string"},
Format: "",
},
},
"groups": {
SchemaProps: spec.SchemaProps{
Description: "Groups is used for the grouping of dashboards that are suggested based on a scope. The source of truth for this information has not been determined yet.",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Default: "",
Type: []string{"string"},
Format: "",
},
},
},
},
},
"dashboardTitleConditions": {
VendorExtensible: spec.VendorExtensible{
Extensions: spec.Extensions{
"x-kubernetes-list-map-keys": []interface{}{
"type",
},
"x-kubernetes-list-type": "map",
},
},
SchemaProps: spec.SchemaProps{
Description: "DashboardTitleConditions is a list of conditions that are used to determine if the dashboard title is valid.",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Default: map[string]interface{}{},
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Condition"),
},
},
},
},
},
"groupsConditions": {
VendorExtensible: spec.VendorExtensible{
Extensions: spec.Extensions{
"x-kubernetes-list-map-keys": []interface{}{
"type",
},
"x-kubernetes-list-type": "map",
},
},
SchemaProps: spec.SchemaProps{
Description: "DashboardTitleConditions is a list of conditions that are used to determine if the list of groups is valid.",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Default: map[string]interface{}{},
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Condition"),
},
},
},
},
},
},
Required: []string{"dashboardTitle"},
},
},
Dependencies: []string{
"k8s.io/apimachinery/pkg/apis/meta/v1.Condition"},
}
}
func schema_pkg_apis_scope_v0alpha1_ScopeFilter(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"object"},
Properties: map[string]spec.Schema{
"key": {
SchemaProps: spec.SchemaProps{
Default: "",
Type: []string{"string"},
Format: "",
},
},
"value": {
SchemaProps: spec.SchemaProps{
Default: "",
Type: []string{"string"},
Format: "",
},
},
"values": {
SchemaProps: spec.SchemaProps{
Description: "Values is used for operators that require multiple values (e.g. one-of and not-one-of).",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Default: "",
Type: []string{"string"},
Format: "",
},
},
},
},
},
"operator": {
SchemaProps: spec.SchemaProps{
Description: "Possible enum values:\n - `\"equals\"`\n - `\"not-equals\"`\n - `\"not-one-of\"`\n - `\"one-of\"`\n - `\"regex-match\"`\n - `\"regex-not-match\"`",
Default: "",
Type: []string{"string"},
Format: "",
Enum: []interface{}{"equals", "not-equals", "not-one-of", "one-of", "regex-match", "regex-not-match"},
},
},
},
Required: []string{"key", "value", "operator"},
},
},
}
}
func schema_pkg_apis_scope_v0alpha1_ScopeList(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.Scope"),
},
},
},
},
},
},
},
},
Dependencies: []string{
"github.com/grafana/grafana/pkg/apis/scope/v0alpha1.Scope", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"},
}
}
func schema_pkg_apis_scope_v0alpha1_ScopeNode(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{
Default: map[string]interface{}{},
Ref: ref("github.com/grafana/grafana/pkg/apis/scope/v0alpha1.ScopeNodeSpec"),
},
},
},
},
},
Dependencies: []string{
"github.com/grafana/grafana/pkg/apis/scope/v0alpha1.ScopeNodeSpec", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"},
}
}
func schema_pkg_apis_scope_v0alpha1_ScopeNodeList(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"},
}
}
func schema_pkg_apis_scope_v0alpha1_ScopeNodeSpec(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"object"},
Properties: map[string]spec.Schema{
"parentName": {
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Format: "",
},
},
"nodeType": {
SchemaProps: spec.SchemaProps{
Default: "",
Type: []string{"string"},
Format: "",
},
},
"title": {
SchemaProps: spec.SchemaProps{
Default: "",
Type: []string{"string"},
Format: "",
},
},
"description": {
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Format: "",
},
},
"disableMultiSelect": {
SchemaProps: spec.SchemaProps{
Default: false,
Type: []string{"boolean"},
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", "disableMultiSelect"},
},
},
}
}
func schema_pkg_apis_scope_v0alpha1_ScopeSpec(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{
Default: "",
Type: []string{"string"},
Format: "",
},
},
"description": {
SchemaProps: spec.SchemaProps{
Default: "",
Type: []string{"string"},
Format: "",
},
},
"filters": {
VendorExtensible: spec.VendorExtensible{
Extensions: spec.Extensions{
"x-kubernetes-list-type": "atomic",
},
},
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.ScopeFilter"),
},
},
},
},
},
},
Required: []string{"title", "description", "filters"},
},
},
Dependencies: []string{
"github.com/grafana/grafana/pkg/apis/scope/v0alpha1.ScopeFilter"},
}
}
@@ -1,4 +0,0 @@
API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/scope/v0alpha1,FindScopeDashboardBindingsResults,Items
API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/scope/v0alpha1,ScopeDashboardBindingStatus,Groups
API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/scope/v0alpha1,ScopeFilter,Values
API rule violation: names_match,github.com/grafana/grafana/pkg/apis/scope/v0alpha1,ScopeNodeSpec,LinkID
-2
View File
@@ -13,7 +13,6 @@ import (
"github.com/grafana/grafana/pkg/registry/apis/iam"
"github.com/grafana/grafana/pkg/registry/apis/provisioning"
"github.com/grafana/grafana/pkg/registry/apis/query"
"github.com/grafana/grafana/pkg/registry/apis/scope"
"github.com/grafana/grafana/pkg/registry/apis/userstorage"
)
@@ -31,7 +30,6 @@ func ProvideRegistryServiceSink(
_ *datasource.DataSourceAPIBuilder,
_ *folders.FolderAPIBuilder,
_ *iam.IdentityAccessManagementAPIBuilder,
_ *scope.ScopeAPIBuilder,
_ *query.QueryAPIBuilder,
_ *notifications.NotificationsAPIBuilder,
_ *userstorage.UserStorageAPIBuilder,
-113
View File
@@ -1,113 +0,0 @@
package scope
import (
"context"
"fmt"
"net/http"
"strings"
"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"
"github.com/grafana/grafana/pkg/infra/log"
)
var logger = log.New("find-scopenode")
type findREST struct {
scopeNodeStorage *storage
}
var (
_ rest.Storage = (*findREST)(nil)
_ rest.SingularNameProvider = (*findREST)(nil)
_ rest.Connecter = (*findREST)(nil)
_ rest.Scoper = (*findREST)(nil)
_ rest.StorageMetadata = (*findREST)(nil)
)
func (r *findREST) New() runtime.Object {
// This is added as the "ResponseType" regarless what ProducesObject() says :)
return &scope.FindScopeNodeChildrenResults{}
}
func (r *findREST) Destroy() {}
func (r *findREST) NamespaceScoped() bool {
return true
}
func (r *findREST) GetSingularName() string {
return "FindScopeNodeChildrenResults" // Used for the
}
func (r *findREST) ProducesMIMETypes(verb string) []string {
return []string{"application/json"} // and parquet!
}
func (r *findREST) ProducesObject(verb string) interface{} {
return &scope.FindScopeNodeChildrenResults{}
}
func (r *findREST) ConnectMethods() []string {
return []string{"GET"}
}
func (r *findREST) NewConnectOptions() (runtime.Object, bool, string) {
return nil, false, "" // true means you can use the trailing path as a variable
}
func (r *findREST) Connect(ctx context.Context, name string, opts runtime.Object, responder rest.Responder) (http.Handler, error) {
// See: /pkg/services/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) {
parent := req.URL.Query().Get("parent")
query := req.URL.Query().Get("query")
results := &scope.FindScopeNodeChildrenResults{}
raw, err := r.scopeNodeStorage.List(ctx, &internalversion.ListOptions{
Limit: 10000,
})
if err != nil {
responder.Error(err)
return
}
all, ok := raw.(*scope.ScopeNodeList)
if !ok {
responder.Error(fmt.Errorf("expected ScopeNodeList"))
return
}
for _, item := range all.Items {
filterAndAppendItem(item, parent, query, results)
}
logger.FromContext(req.Context()).Debug("find scopenode", "raw", len(all.Items), "filtered", len(results.Items))
responder.Object(200, results)
}), nil
}
func filterAndAppendItem(item scope.ScopeNode, parent string, query string, results *scope.FindScopeNodeChildrenResults) {
if parent != item.Spec.ParentName {
return // Someday this will have an index in raw storage on parentName
}
// skip if query is passed and title doesn't contain the query.
if query != "" && !strings.Contains(item.Spec.Title, query) {
return
}
results.Items = append(results.Items, item)
}
@@ -1,106 +0,0 @@
package scope
import (
"context"
"fmt"
"net/http"
"slices"
"strings"
scope "github.com/grafana/grafana/pkg/apis/scope/v0alpha1"
"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"
)
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/services/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{
Limit: 10000,
})
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)
}
}
}
// sort the dashboard lists based on dashboard title.
slices.SortFunc(results.Items, func(i, j scope.ScopeDashboardBinding) int {
return strings.Compare(i.Status.DashboardTitle, j.Status.DashboardTitle)
})
logger.FromContext(req.Context()).Debug("find scopedashboardbinding", "raw", len(all.Items), "filtered", len(results.Items), "scopeQueryParams", strings.Join(scopes, ","))
responder.Object(200, results)
}), nil
}
-71
View File
@@ -1,71 +0,0 @@
package scope
import (
"testing"
scope "github.com/grafana/grafana/pkg/apis/scope/v0alpha1"
"github.com/stretchr/testify/require"
)
func TestFilterAndAppendItem(t *testing.T) {
tcs := []struct {
Description string
ParentName string
Title string
QueryParam string
ParentParam string
ExpectedMatches int
}{
{
Description: "Matching parent without query param",
ParentName: "ParentNumberOne",
Title: "item",
QueryParam: "",
ParentParam: "ParentNumberOne",
ExpectedMatches: 1,
},
{
Description: "Not matching parent",
ParentName: "ParentNumberOne",
Title: "itemOne",
QueryParam: "itemTwo",
ParentParam: "ParentNumberTwo",
ExpectedMatches: 0,
},
{
Description: "Matching parent and query param",
ParentName: "ParentNumberOne",
Title: "itemOne",
QueryParam: "itemOne",
ParentParam: "ParentNumberOne",
ExpectedMatches: 1,
},
{
Description: "matching parent but not matching query param",
ParentName: "ParentNumberOne",
Title: "itemOne",
QueryParam: "itemTwo",
ParentParam: "ParentNumberOne",
ExpectedMatches: 0,
},
}
for _, tc := range tcs {
results := &scope.FindScopeNodeChildrenResults{}
item := scope.ScopeNode{
Spec: scope.ScopeNodeSpec{
ParentName: tc.ParentName,
Title: tc.Title,
Description: "item description",
NodeType: "item type",
LinkType: "item link type",
LinkID: "item link ID",
},
}
filterAndAppendItem(item, tc.ParentParam, tc.QueryParam, results)
require.Equal(t, len(results.Items), tc.ExpectedMatches, tc.Description)
}
}
-224
View File
@@ -1,224 +0,0 @@
package scope
import (
"fmt"
"github.com/prometheus/client_golang/prometheus"
"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"
"k8s.io/kube-openapi/pkg/common"
"k8s.io/kube-openapi/pkg/spec3"
"k8s.io/kube-openapi/pkg/validation/spec"
scope "github.com/grafana/grafana/pkg/apis/scope/v0alpha1"
"github.com/grafana/grafana/pkg/services/apiserver/builder"
"github.com/grafana/grafana/pkg/services/featuremgmt"
)
var _ builder.APIGroupBuilder = (*ScopeAPIBuilder)(nil)
// This is used just so wire has something unique to return
type ScopeAPIBuilder struct{}
func NewScopeAPIBuilder() *ScopeAPIBuilder {
return &ScopeAPIBuilder{}
}
func RegisterAPIService(features featuremgmt.FeatureToggles, apiregistration builder.APIRegistrar, reg prometheus.Registerer) *ScopeAPIBuilder {
if !featuremgmt.AnyEnabled(features,
featuremgmt.FlagScopeApi,
featuremgmt.FlagGrafanaAPIServerWithExperimentalAPIs) {
return nil // skip registration unless opting into experimental apis
}
builder := NewScopeAPIBuilder()
apiregistration.RegisterAPI(builder)
return builder
}
func (b *ScopeAPIBuilder) GetAuthorizer() authorizer.Authorizer {
return nil // default authorizer is fine
}
func (b *ScopeAPIBuilder) GetGroupVersion() schema.GroupVersion {
return scope.SchemeGroupVersion
}
func (b *ScopeAPIBuilder) InstallSchema(scheme *runtime.Scheme) error {
err := scope.AddToScheme(scheme)
if err != nil {
return err
}
err = scheme.AddFieldLabelConversionFunc(
scope.ScopeResourceInfo.GroupVersionKind(),
func(label, value string) (string, string, error) {
fieldSet := SelectableScopeFields(&scope.Scope{})
for key := range fieldSet {
if label == key {
return label, value, nil
}
}
return "", "", fmt.Errorf("field label not supported for %s: %s", scope.ScopeResourceInfo.GroupVersionKind(), label)
},
)
if err != nil {
return err
}
err = scheme.AddFieldLabelConversionFunc(
scope.ScopeDashboardBindingResourceInfo.GroupVersionKind(),
func(label, value string) (string, string, error) {
fieldSet := SelectableScopeDashboardBindingFields(&scope.ScopeDashboardBinding{})
for key := range fieldSet {
if label == key {
return label, value, nil
}
}
return "", "", fmt.Errorf("field label not supported for %s: %s", scope.ScopeDashboardBindingResourceInfo.GroupVersionKind(), label)
},
)
if err != nil {
return err
}
err = scheme.AddFieldLabelConversionFunc(
scope.ScopeNodeResourceInfo.GroupVersionKind(),
func(label, value string) (string, string, error) {
fieldSet := SelectableScopeNodeFields(&scope.ScopeNode{})
for key := range fieldSet {
if label == key {
return label, value, nil
}
}
return "", "", fmt.Errorf("field label not supported for %s: %s", scope.ScopeNodeResourceInfo.GroupVersionKind(), label)
},
)
if err != nil {
return err
}
// This is required for --server-side apply
err = scope.AddKnownTypes(scope.InternalGroupVersion, scheme)
if err != nil {
return err
}
// Only one version right now
return scheme.SetVersionPriority(scope.SchemeGroupVersion)
}
func (b *ScopeAPIBuilder) UpdateAPIGroupInfo(apiGroupInfo *genericapiserver.APIGroupInfo, opts builder.APIGroupOptions) error {
scheme := opts.Scheme
optsGetter := opts.OptsGetter
scopeResourceInfo := scope.ScopeResourceInfo
scopeDashboardResourceInfo := scope.ScopeDashboardBindingResourceInfo
scopeNodeResourceInfo := scope.ScopeNodeResourceInfo
storage := map[string]rest.Storage{}
scopeStorage, err := newScopeStorage(scheme, optsGetter)
if err != nil {
return err
}
storage[scopeResourceInfo.StoragePath()] = scopeStorage
scopeDashboardStorage, scopedDashboardStatusStorage, err := newScopeDashboardBindingStorage(scheme, optsGetter)
if err != nil {
return err
}
storage[scopeDashboardResourceInfo.StoragePath()] = scopeDashboardStorage
storage[scopeDashboardResourceInfo.StoragePath()+"/status"] = scopedDashboardStatusStorage
scopeNodeStorage, err := newScopeNodeStorage(scheme, optsGetter)
if err != nil {
return err
}
storage[scopeNodeResourceInfo.StoragePath()] = 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_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
return nil
}
func (b *ScopeAPIBuilder) GetOpenAPIDefinitions() common.GetOpenAPIDefinitions {
return scope.GetOpenAPIDefinitions
}
func (b *ScopeAPIBuilder) PostProcessOpenAPI(oas *spec3.OpenAPI) (*spec3.OpenAPI, error) {
// The plugin description
oas.Info.Description = "Grafana scopes"
// The root api URL
root := "/apis/" + b.GetGroupVersion().String() + "/"
// Add query parameters to the rest.Connector
sub := oas.Paths.Paths[root+"namespaces/{namespace}/scope_node_children/{name}"]
if sub != 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,
Schema: spec.StringProperty().UniqueValues(),
},
},
}
sub.Get.Description = "Navigate the scopes tree"
sub.Get.Parameters = []*spec3.Parameter{
{
ParameterProps: spec3.ParameterProps{
Name: "parent",
In: "query",
Description: "The parent scope node",
},
},
}
delete(oas.Paths.Paths, root+"namespaces/{namespace}/scope_node_children/{name}")
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
}
return oas, nil
}
-142
View File
@@ -1,142 +0,0 @@
package scope
import (
"fmt"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/registry/generic"
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
apistore "k8s.io/apiserver/pkg/storage"
scope "github.com/grafana/grafana/pkg/apis/scope/v0alpha1"
grafanaregistry "github.com/grafana/grafana/pkg/apiserver/registry/generic"
grafanarest "github.com/grafana/grafana/pkg/apiserver/rest"
)
var _ grafanarest.Storage = (*storage)(nil)
type storage struct {
*genericregistry.Store
}
func newScopeStorage(scheme *runtime.Scheme, optsGetter generic.RESTOptionsGetter) (*storage, error) {
resourceInfo := scope.ScopeResourceInfo
strategy := grafanaregistry.NewStrategy(scheme, resourceInfo.GroupVersion())
store := &genericregistry.Store{
NewFunc: resourceInfo.NewFunc,
NewListFunc: resourceInfo.NewListFunc,
KeyRootFunc: grafanaregistry.KeyRootFunc(resourceInfo.GroupResource()),
KeyFunc: grafanaregistry.NamespaceKeyFunc(resourceInfo.GroupResource()),
PredicateFunc: Matcher,
DefaultQualifiedResource: resourceInfo.GroupResource(),
SingularQualifiedResource: resourceInfo.SingularGroupResource(),
TableConvertor: resourceInfo.TableConverter(),
CreateStrategy: strategy,
UpdateStrategy: strategy,
DeleteStrategy: strategy,
}
options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: GetAttrs}
if err := store.CompleteWithOptions(options); err != nil {
return nil, err
}
return &storage{Store: store}, nil
}
func newScopeDashboardBindingStorage(scheme *runtime.Scheme, optsGetter generic.RESTOptionsGetter) (*storage, *grafanaregistry.StatusREST, error) {
resourceInfo := scope.ScopeDashboardBindingResourceInfo
strategy := grafanaregistry.NewStrategy(scheme, resourceInfo.GroupVersion())
store := &genericregistry.Store{
NewFunc: resourceInfo.NewFunc,
NewListFunc: resourceInfo.NewListFunc,
KeyRootFunc: grafanaregistry.KeyRootFunc(resourceInfo.GroupResource()),
KeyFunc: grafanaregistry.NamespaceKeyFunc(resourceInfo.GroupResource()),
PredicateFunc: Matcher,
DefaultQualifiedResource: resourceInfo.GroupResource(),
SingularQualifiedResource: resourceInfo.SingularGroupResource(),
TableConvertor: resourceInfo.TableConverter(),
CreateStrategy: strategy,
UpdateStrategy: strategy,
DeleteStrategy: strategy,
}
options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: GetAttrs}
if err := store.CompleteWithOptions(options); err != nil {
return nil, nil, err
}
statusStrategy := grafanaregistry.NewStatusStrategy(scheme, resourceInfo.GroupVersion())
statusREST := grafanaregistry.NewStatusREST(store, statusStrategy)
return &storage{Store: store}, statusREST, nil
}
func newScopeNodeStorage(scheme *runtime.Scheme, optsGetter generic.RESTOptionsGetter) (*storage, error) {
resourceInfo := scope.ScopeNodeResourceInfo
strategy := grafanaregistry.NewStrategy(scheme, resourceInfo.GroupVersion())
store := &genericregistry.Store{
NewFunc: resourceInfo.NewFunc,
NewListFunc: resourceInfo.NewListFunc,
KeyRootFunc: grafanaregistry.KeyRootFunc(resourceInfo.GroupResource()),
KeyFunc: grafanaregistry.NamespaceKeyFunc(resourceInfo.GroupResource()),
PredicateFunc: Matcher,
DefaultQualifiedResource: resourceInfo.GroupResource(),
SingularQualifiedResource: resourceInfo.SingularGroupResource(),
TableConvertor: resourceInfo.TableConverter(),
CreateStrategy: strategy,
UpdateStrategy: strategy,
DeleteStrategy: strategy,
}
options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: GetAttrs}
if err := store.CompleteWithOptions(options); err != nil {
return nil, err
}
return &storage{Store: store}, nil
}
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
if s, ok := obj.(*scope.Scope); ok {
return labels.Set(s.Labels), SelectableScopeFields(s), nil
}
if s, ok := obj.(*scope.ScopeDashboardBinding); ok {
return labels.Set(s.Labels), SelectableScopeDashboardBindingFields(s), nil
}
if s, ok := obj.(*scope.ScopeNode); ok {
return labels.Set(s.Labels), SelectableScopeNodeFields(s), nil
}
return nil, nil, fmt.Errorf("not a scope or ScopeDashboardBinding object")
}
// Matcher returns a generic.SelectionPredicate that matches on label and field selectors.
func Matcher(label labels.Selector, field fields.Selector) apistore.SelectionPredicate {
return apistore.SelectionPredicate{
Label: label,
Field: field,
GetAttrs: GetAttrs,
}
}
func SelectableScopeFields(obj *scope.Scope) fields.Set {
return generic.MergeFieldsSets(generic.ObjectMetaFieldsSet(&obj.ObjectMeta, false), fields.Set{
"spec.title": obj.Spec.Title,
})
}
func SelectableScopeDashboardBindingFields(obj *scope.ScopeDashboardBinding) fields.Set {
return generic.MergeFieldsSets(generic.ObjectMetaFieldsSet(&obj.ObjectMeta, false), fields.Set{
"spec.scope": obj.Spec.Scope,
})
}
func SelectableScopeNodeFields(obj *scope.ScopeNode) fields.Set {
parentName := ""
if obj != nil {
parentName = obj.Spec.ParentName
}
return generic.MergeFieldsSets(generic.ObjectMetaFieldsSet(&obj.ObjectMeta, false), fields.Set{
"spec.parentName": parentName,
})
}
-2
View File
@@ -15,7 +15,6 @@ import (
"github.com/grafana/grafana/pkg/registry/apis/iam"
"github.com/grafana/grafana/pkg/registry/apis/provisioning"
"github.com/grafana/grafana/pkg/registry/apis/query"
"github.com/grafana/grafana/pkg/registry/apis/scope"
"github.com/grafana/grafana/pkg/registry/apis/service"
"github.com/grafana/grafana/pkg/registry/apis/userstorage"
"github.com/grafana/grafana/pkg/services/pluginsintegration/plugincontext"
@@ -42,7 +41,6 @@ var WireSet = wire.NewSet(
provisioning.RegisterAPIService,
service.RegisterAPIService,
query.RegisterAPIService,
scope.RegisterAPIService,
notifications.RegisterAPIService,
userstorage.RegisterAPIService,
)
@@ -12,6 +12,7 @@ import (
var (
allowedCoreActions = map[string]string{
"plugins:write": "plugins:id:",
"plugins.app:access": "plugins:id:",
"folders:create": "folders:uid:",
"folders:read": "folders:uid:",
@@ -172,6 +172,29 @@ func TestValidatePluginRole(t *testing.T) {
},
wantErr: &ac.ErrorInvalidRole{},
},
{
name: "valid core plugin permission targets plugin",
pluginID: "test-app",
role: ac.RoleDTO{
Name: "plugins:test-app:reader",
DisplayName: "Plugin Configurator",
Permissions: []ac.Permission{
{Action: "plugins:write", Scope: "plugins:id:test-app"},
},
},
},
{
name: "invalid core plugin permission targets other plugin",
pluginID: "test-app",
role: ac.RoleDTO{
Name: "plugins:test-app:reader",
DisplayName: "Plugin Configurator",
Permissions: []ac.Permission{
{Action: "plugins:write", Scope: "plugins:id:other-app"},
},
},
wantErr: &ac.ErrorInvalidRole{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
@@ -1,84 +0,0 @@
package scopes
import (
"context"
"encoding/json"
"os"
"testing"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/tests/apis"
"github.com/grafana/grafana/pkg/tests/testinfra"
"github.com/stretchr/testify/require"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
)
func TestIntegrationScopeNodesExample(t *testing.T) {
if testing.Short() {
t.Skip("skipping integration test in short mode.")
}
ctx := context.Background()
helper := apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
AppModeProduction: false, // required for experimental APIs
EnableFeatureToggles: []string{
featuremgmt.FlagGrafanaAPIServerWithExperimentalAPIs, // Required to start the example service
},
})
scopeClient := helper.GetResourceClient(apis.ResourceClientArgs{
User: helper.Org1.Admin,
Namespace: "default", // actually org1
GVR: schema.GroupVersionResource{
Group: "scope.grafana.app", Version: "v0alpha1", Resource: "scopes",
},
})
scopeNodesClient := helper.GetResourceClient(apis.ResourceClientArgs{
User: helper.Org1.Admin,
Namespace: "default", // actually org1
GVR: schema.GroupVersionResource{
Group: "scope.grafana.app", Version: "v0alpha1", Resource: "scopenodes",
},
})
createOptions := metav1.CreateOptions{FieldValidation: "Strict"}
t.Run("Create scopes", func(t *testing.T) {
ul := jsonListToUnstructuredList(t, "testdata/scopeNodesExample/scopes.json")
for _, item := range ul.Items {
_, err := scopeClient.Resource.Create(ctx, &item, createOptions)
require.NoError(t, err)
}
found, err := scopeClient.Resource.List(ctx, metav1.ListOptions{})
require.NoError(t, err)
require.Len(t, found.Items, 5)
})
t.Run("Create scopeNodes", func(t *testing.T) {
ul := jsonListToUnstructuredList(t, "testdata/scopeNodesExample/scopeNodes.json")
for _, item := range ul.Items {
_, err := scopeNodesClient.Resource.Create(ctx, &item, createOptions)
require.NoError(t, err)
}
found, err := scopeNodesClient.Resource.List(ctx, metav1.ListOptions{})
require.NoError(t, err)
require.Len(t, found.Items, 12)
})
}
func jsonListToUnstructuredList(t *testing.T, fname string) (ul unstructured.UnstructuredList) {
// nolint:gosec
f, err := os.ReadFile(fname)
require.NoError(t, err)
err = json.Unmarshal(f, &ul)
require.NoError(t, err)
return
}
-221
View File
@@ -1,221 +0,0 @@
package scopes
import (
"context"
"encoding/json"
"strings"
"testing"
"github.com/stretchr/testify/require"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"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"
)
func TestMain(m *testing.M) {
testsuite.Run(m)
}
func TestIntegrationScopes(t *testing.T) {
if testing.Short() {
t.Skip("skipping integration test")
}
ctx := context.Background()
helper := apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
AppModeProduction: true,
EnableFeatureToggles: []string{
featuremgmt.FlagScopeApi, // Required to register the API
},
})
t.Run("Check discovery client", func(t *testing.T) {
disco := helper.NewDiscoveryClient()
resources, err := disco.ServerResourcesForGroupVersion("scope.grafana.app/v0alpha1")
require.NoError(t, err)
v1Disco, err := json.MarshalIndent(resources, "", " ")
require.NoError(t, err)
//fmt.Printf("%s", string(v1Disco))
require.JSONEq(t, `{
"kind": "APIResourceList",
"apiVersion": "v1",
"groupVersion": "scope.grafana.app/v0alpha1",
"resources": [
{
"name": "scope_dashboard_bindings",
"singularName": "FindScopeDashboardsResult",
"namespaced": true,
"kind": "FindScopeDashboardBindingsResults",
"verbs": [
"get"
]
},
{
"name": "scope_node_children",
"singularName": "FindScopeNodeChildrenResults",
"namespaced": true,
"kind": "FindScopeNodeChildrenResults",
"verbs": [
"get"
]
},
{
"name": "scopedashboardbindings",
"singularName": "scopedashboardbinding",
"namespaced": true,
"kind": "ScopeDashboardBinding",
"verbs": [
"create",
"delete",
"deletecollection",
"get",
"list",
"patch",
"update",
"watch"
]
},
{
"name": "scopedashboardbindings/status",
"singularName": "",
"namespaced": true,
"kind": "ScopeDashboardBinding",
"verbs": [
"get",
"patch",
"update"
]
},
{
"name": "scopenodes",
"singularName": "scopenode",
"namespaced": true,
"kind": "ScopeNode",
"verbs": [
"create",
"delete",
"deletecollection",
"get",
"list",
"patch",
"update",
"watch"
]
},
{
"name": "scopes",
"singularName": "scope",
"namespaced": true,
"kind": "Scope",
"verbs": [
"create",
"delete",
"deletecollection",
"get",
"list",
"patch",
"update",
"watch"
]
}
]
}`, string(v1Disco))
})
t.Run("Check create and list", func(t *testing.T) {
// Scope create+get
scopeClient := helper.GetResourceClient(apis.ResourceClientArgs{
User: helper.Org1.Admin,
Namespace: "default", // actually org1
GVR: schema.GroupVersionResource{
Group: "scope.grafana.app", Version: "v0alpha1", Resource: "scopes",
},
})
createOptions := metav1.CreateOptions{FieldValidation: "Strict"}
s0, err := scopeClient.Resource.Create(ctx,
helper.LoadYAMLOrJSONFile("testdata/example-scope.yaml"),
createOptions,
)
require.NoError(t, err)
require.Equal(t, "example", s0.GetName())
s1, err := scopeClient.Resource.Get(ctx, "example", metav1.GetOptions{})
require.NoError(t, err)
require.Equal(t,
mustNestedString(s0.Object, "spec", "title"),
mustNestedString(s1.Object, "spec", "title"),
)
_, err = scopeClient.Resource.Create(ctx,
helper.LoadYAMLOrJSONFile("testdata/example-scope2.yaml"),
createOptions,
)
require.NoError(t, err)
// Name length test
scope3 := helper.LoadYAMLOrJSONFile("testdata/example-scope3.yaml")
// Name too long (>253)
scope3.SetName(strings.Repeat("0", 254))
_, err = scopeClient.Resource.Create(ctx,
scope3,
createOptions,
)
require.Error(t, err)
// Maximum allowed length for name (253)
scope3.SetName(strings.Repeat("0", 253))
_, err = scopeClient.Resource.Create(ctx,
scope3,
createOptions,
)
require.NoError(t, err)
// Field Selector test
found, err := scopeClient.Resource.List(ctx, metav1.ListOptions{
FieldSelector: "spec.title=foo-scope",
})
require.NoError(t, err)
require.Len(t, found.Items, 1)
require.Equal(t,
"example2",
mustNestedString(found.Items[0].Object, "metadata", "name"),
)
// Create bindings
scopeDashboardBindingClient := helper.GetResourceClient(apis.ResourceClientArgs{
User: helper.Org1.Admin,
Namespace: "default", // actually org1
GVR: schema.GroupVersionResource{
Group: "scope.grafana.app", Version: "v0alpha1", Resource: "scopedashboardbindings",
},
})
_, err = scopeDashboardBindingClient.Resource.Create(ctx,
helper.LoadYAMLOrJSONFile("testdata/example-scope-dashboard-binding-abc.yaml"),
createOptions,
)
require.NoError(t, err)
_, err = scopeDashboardBindingClient.Resource.Create(ctx,
helper.LoadYAMLOrJSONFile("testdata/example-scope-dashboard-binding-xyz.yaml"),
createOptions,
)
require.NoError(t, err)
found, err = scopeDashboardBindingClient.Resource.List(ctx, metav1.ListOptions{})
require.NoError(t, err)
require.Len(t, found.Items, 2)
})
}
func mustNestedString(obj map[string]interface{}, fields ...string) string {
v, _, _ := unstructured.NestedString(obj, fields...)
return v
}
@@ -1,10 +0,0 @@
apiVersion: scope.grafana.app/v0alpha1
kind: ScopeDashboardBinding
metadata:
name: example_abc
spec:
scope: example
dashboard: abc
status:
dashboardTitle: "Example Dashboard ABC"
groups: ["group1", "group2"]
@@ -1,10 +0,0 @@
apiVersion: scope.grafana.app/v0alpha1
kind: ScopeDashboardBinding
metadata:
name: example_xyz
spec:
scope: example
dashboard: xyz
status:
dashboardTitle: "Example Dashboard XYZ"
groups: ["group2", "group3"]
-14
View File
@@ -1,14 +0,0 @@
apiVersion: scope.grafana.app/v0alpha1
kind: Scope
metadata:
name: example
spec:
title: bar-scope
description: Longer description for a scope
filters:
- key: aaa
operator: equals
value: bbb
- key: ccc
operator: not-equals
value: ddd
-14
View File
@@ -1,14 +0,0 @@
apiVersion: scope.grafana.app/v0alpha1
kind: Scope
metadata:
name: example2
spec:
title: foo-scope
description: Longer description for a scope
filters:
- key: aaa
operator: equals
value: zzz
- key: ccc
operator: not-equals
value: yyy
-14
View File
@@ -1,14 +0,0 @@
apiVersion: scope.grafana.app/v0alpha1
kind: Scope
metadata:
name: example-long
spec:
title: baz-scope
description: Longer description for a scope
filters:
- key: aaa
operator: equals
value: eee
- key: ccc
operator: not-equals
value: fff
@@ -1,179 +0,0 @@
{
"kind": "List",
"items": [
{
"apiVersion": "scope.grafana.app/v0alpha1",
"kind": "ScopeNode",
"metadata": {
"name": "applications"
},
"spec": {
"description": "Application Scopes",
"title": "Applications",
"nodeType": "container"
}
},
{
"apiVersion": "scope.grafana.app/v0alpha1",
"kind": "ScopeNode",
"metadata": {
"name": "applications-clusters"
},
"spec": {
"description": "Application/Clusters Scopes",
"title": "Clusters",
"nodeType": "container",
"parentName": "applications",
"linkId": "indexHelperCluster",
"linkType": "scope"
}
},
{
"apiVersion": "scope.grafana.app/v0alpha1",
"kind": "ScopeNode",
"metadata": {
"name": "clusters"
},
"spec": {
"description": "Cluster Scopes",
"title": "Clusters",
"nodeType": "container",
"linkId": "indexHelperCluster",
"linkType": "scope"
}
},
{
"apiVersion": "scope.grafana.app/v0alpha1",
"kind": "ScopeNode",
"metadata": {
"name": "clusters-application"
},
"spec": {
"description": "Clusters/Application Scopes",
"title": "Applications",
"nodeType": "container",
"parentName": "clusters"
}
},
{
"apiVersion": "scope.grafana.app/v0alpha1",
"kind": "ScopeNode",
"metadata": {
"name": "applications-slothPictureFactory"
},
"spec": {
"description": "slothPictureFactory",
"title": "slothPictureFactory",
"nodeType": "leaf",
"parentName": "applications",
"linkId": "slothPictureFactory",
"linkType": "scope"
}
},
{
"apiVersion": "scope.grafana.app/v0alpha1",
"kind": "ScopeNode",
"metadata": {
"name": "applications-slothVoteTracker"
},
"spec": {
"description": "slothVoteTracker",
"title": "slothVoteTracker",
"nodeType": "leaf",
"parentName": "applications",
"linkId": "slothVoteTracker",
"linkType": "scope"
}
},
{
"apiVersion": "scope.grafana.app/v0alpha1",
"kind": "ScopeNode",
"metadata": {
"name": "applications-clusters-slothClusterNorth"
},
"spec": {
"description": "slothClusterNorth",
"title": "slothClusterNorth",
"nodeType": "leaf",
"parentName": "applications-clusters",
"linkId": "slothClusterNorth",
"linkType": "scope"
}
},
{
"apiVersion": "scope.grafana.app/v0alpha1",
"kind": "ScopeNode",
"metadata": {
"name": "applications-clusters-slothClusterSouth"
},
"spec": {
"description": "slothClusterSouth",
"title": "slothClusterSouth",
"nodeType": "leaf",
"parentName": "applications-clusters",
"linkId": "slothClusterSouth",
"linkType": "scope"
}
},
{
"apiVersion": "scope.grafana.app/v0alpha1",
"kind": "ScopeNode",
"metadata": {
"name": "clusters-slothClusterNorth"
},
"spec": {
"description": "slothClusterNorth",
"title": "slothClusterNorth",
"nodeType": "leaf",
"parentName": "clusters",
"linkId": "slothClusterNorth",
"linkType": "scope"
}
},
{
"apiVersion": "scope.grafana.app/v0alpha1",
"kind": "ScopeNode",
"metadata": {
"name": "clusters-slothClusterSouth"
},
"spec": {
"description": "slothClusterSouth",
"title": "slothClusterSouth",
"nodeType": "leaf",
"parentName": "clusters",
"linkId": "slothClusterSouth",
"linkType": "scope"
}
},
{
"apiVersion": "scope.grafana.app/v0alpha1",
"kind": "ScopeNode",
"metadata": {
"name": "clusters-applications-slothPictureFactory"
},
"spec": {
"description": "slothPictureFactory",
"title": "slothPictureFactory",
"nodeType": "leaf",
"parentName": "clusters-applications",
"linkId": "slothPictureFactory",
"linkType": "scope"
}
},
{
"apiVersion": "scope.grafana.app/v0alpha1",
"kind": "ScopeNode",
"metadata": {
"name": "clusters-applications-slothVoteTracker"
},
"spec": {
"description": "slothVoteTracker",
"title": "slothVoteTracker",
"nodeType": "leaf",
"parentName": "clusters-applications",
"linkId": "slothVoteTracker",
"linkType": "scope"
}
}
]
}
@@ -1,95 +0,0 @@
{
"kind": "List",
"items": [
{
"apiVersion": "scope.grafana.app/v0alpha1",
"kind": "Scope",
"metadata": {
"name": "slothClusterNorth"
},
"spec": {
"description": "slothClusterNorth",
"filters": [
{
"key": "cluster",
"operator": "equals",
"value": "slothClusterNorth"
}
],
"title": "slothClusterNorth"
}
},
{
"apiVersion": "scope.grafana.app/v0alpha1",
"kind": "Scope",
"metadata": {
"name": "slothClusterSouth"
},
"spec": {
"description": "slothClusterSouth",
"filters": [
{
"key": "cluster",
"operator": "equals",
"value": "slothClusterSouth"
}
],
"title": "slothClusterSouth"
}
},
{
"apiVersion": "scope.grafana.app/v0alpha1",
"kind": "Scope",
"metadata": {
"name": "slothPictureFactory"
},
"spec": {
"description": "slothPictureFactory",
"filters": [
{
"key": "app",
"operator": "equals",
"value": "slothPictureFactory"
}
],
"title": "slothPictureFactory"
}
},
{
"apiVersion": "scope.grafana.app/v0alpha1",
"kind": "Scope",
"metadata": {
"name": "slothVoteTracker"
},
"spec": {
"description": "slothVoteTracker",
"filters": [
{
"key": "app",
"operator": "equals",
"value": "slothVoteTracker"
}
],
"title": "slothVoteTracker"
}
},
{
"apiVersion": "scope.grafana.app/v0alpha1",
"kind": "Scope",
"metadata": {
"name": "indexHelperCluster"
},
"spec": {
"description": "redundant label filter but makes queries faster",
"filters": [
{
"key": "indexHelper",
"operator": "equals",
"value": "cluster"
}
],
"title": "Cluster Index Helper"
}
}
]
}