mirror of https://github.com/grafana/grafana
Playlist: Use a different go struct for sql service vs k8s (#76393)
parent
7562607319
commit
29cf60988b
@ -1,117 +0,0 @@ |
||||
--- |
||||
keywords: |
||||
- grafana |
||||
- schema |
||||
labels: |
||||
products: |
||||
- cloud |
||||
- enterprise |
||||
- oss |
||||
title: Playlist kind |
||||
--- |
||||
> Both documentation generation and kinds schemas are in active development and subject to change without prior notice. |
||||
|
||||
## Playlist |
||||
|
||||
#### Maturity: [merged](../../../maturity/#merged) |
||||
#### Version: 0.0 |
||||
|
||||
A playlist is a series of dashboards that is automatically rotated in the browser, on a configurable interval. |
||||
|
||||
| Property | Type | Required | Default | Description | |
||||
|------------|---------------------|----------|---------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| |
||||
| `metadata` | [object](#metadata) | **Yes** | | metadata contains embedded CommonMetadata and can be extended with custom string fields<br/>TODO: use CommonMetadata instead of redefining here; currently needs to be defined here<br/>without external reference as using the CommonMetadata reference breaks thema codegen. | |
||||
| `spec` | [object](#spec) | **Yes** | | | |
||||
| `status` | [object](#status) | **Yes** | | | |
||||
|
||||
### Metadata |
||||
|
||||
metadata contains embedded CommonMetadata and can be extended with custom string fields |
||||
TODO: use CommonMetadata instead of redefining here; currently needs to be defined here |
||||
without external reference as using the CommonMetadata reference breaks thema codegen. |
||||
|
||||
It extends [_kubeObjectMetadata](#_kubeobjectmetadata). |
||||
|
||||
| Property | Type | Required | Default | Description | |
||||
|---------------------|------------------------|----------|---------|-----------------------------------------------------------------------------------------------------------------------------------------| |
||||
| `createdBy` | string | **Yes** | | | |
||||
| `creationTimestamp` | string | **Yes** | | *(Inherited from [_kubeObjectMetadata](#_kubeobjectmetadata))* | |
||||
| `extraFields` | [object](#extrafields) | **Yes** | | extraFields is reserved for any fields that are pulled from the API server metadata but do not have concrete fields in the CUE metadata | |
||||
| `finalizers` | string[] | **Yes** | | *(Inherited from [_kubeObjectMetadata](#_kubeobjectmetadata))* | |
||||
| `labels` | map[string]string | **Yes** | | *(Inherited from [_kubeObjectMetadata](#_kubeobjectmetadata))* | |
||||
| `resourceVersion` | string | **Yes** | | *(Inherited from [_kubeObjectMetadata](#_kubeobjectmetadata))* | |
||||
| `uid` | string | **Yes** | | *(Inherited from [_kubeObjectMetadata](#_kubeobjectmetadata))* | |
||||
| `updateTimestamp` | string | **Yes** | | | |
||||
| `updatedBy` | string | **Yes** | | | |
||||
| `deletionTimestamp` | string | No | | *(Inherited from [_kubeObjectMetadata](#_kubeobjectmetadata))* | |
||||
|
||||
### _kubeObjectMetadata |
||||
|
||||
_kubeObjectMetadata is metadata found in a kubernetes object's metadata field. |
||||
It is not exhaustive and only includes fields which may be relevant to a kind's implementation, |
||||
As it is also intended to be generic enough to function with any API Server. |
||||
|
||||
| Property | Type | Required | Default | Description | |
||||
|---------------------|-------------------|----------|---------|-------------| |
||||
| `creationTimestamp` | string | **Yes** | | | |
||||
| `finalizers` | string[] | **Yes** | | | |
||||
| `labels` | map[string]string | **Yes** | | | |
||||
| `resourceVersion` | string | **Yes** | | | |
||||
| `uid` | string | **Yes** | | | |
||||
| `deletionTimestamp` | string | No | | | |
||||
|
||||
### ExtraFields |
||||
|
||||
extraFields is reserved for any fields that are pulled from the API server metadata but do not have concrete fields in the CUE metadata |
||||
|
||||
| Property | Type | Required | Default | Description | |
||||
|----------|------|----------|---------|-------------| |
||||
|
||||
### Spec |
||||
|
||||
| Property | Type | Required | Default | Description | |
||||
|------------|---------------------------------|----------|---------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------| |
||||
| `interval` | string | **Yes** | `5m` | Interval sets the time between switching views in a playlist.<br/>FIXME: Is this based on a standardized format or what options are available? Can datemath be used? | |
||||
| `name` | string | **Yes** | | Name of the playlist. | |
||||
| `uid` | string | **Yes** | | Unique playlist identifier. Generated on creation, either by the<br/>creator of the playlist of by the application. | |
||||
| `items` | [PlaylistItem](#playlistitem)[] | No | | The ordered list of items that the playlist will iterate over.<br/>FIXME! This should not be optional, but changing it makes the godegen awkward | |
||||
|
||||
### PlaylistItem |
||||
|
||||
| Property | Type | Required | Default | Description | |
||||
|----------|--------|----------|---------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| |
||||
| `type` | string | **Yes** | | Type of the item.<br/>Possible values are: `dashboard_by_uid`, `dashboard_by_id`, `dashboard_by_tag`. | |
||||
| `value` | string | **Yes** | | Value depends on type and describes the playlist item.<br/><br/> - dashboard_by_id: The value is an internal numerical identifier set by Grafana. This<br/> is not portable as the numerical identifier is non-deterministic between different instances.<br/> Will be replaced by dashboard_by_uid in the future. (deprecated)<br/> - dashboard_by_tag: The value is a tag which is set on any number of dashboards. All<br/> dashboards behind the tag will be added to the playlist.<br/> - dashboard_by_uid: The value is the dashboard UID | |
||||
| `title` | string | No | | Title is an unused property -- it will be removed in the future | |
||||
|
||||
### Status |
||||
|
||||
| Property | Type | Required | Default | Description | |
||||
|--------------------|------------------------------------------------------------|----------|---------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------| |
||||
| `additionalFields` | [object](#additionalfields) | No | | additionalFields is reserved for future use | |
||||
| `operatorStates` | map[string][status.#OperatorState](#status.#operatorstate) | No | | operatorStates is a map of operator ID to operator state evaluations.<br/>Any operator which consumes this kind SHOULD add its state evaluation information to this field. | |
||||
|
||||
### AdditionalFields |
||||
|
||||
additionalFields is reserved for future use |
||||
|
||||
| Property | Type | Required | Default | Description | |
||||
|----------|------|----------|---------|-------------| |
||||
|
||||
### Status.#OperatorState |
||||
|
||||
| Property | Type | Required | Default | Description | |
||||
|--------------------|--------------------|----------|---------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| |
||||
| `lastEvaluation` | string | **Yes** | | lastEvaluation is the ResourceVersion last evaluated | |
||||
| `state` | string | **Yes** | | state describes the state of the lastEvaluation.<br/>It is limited to three possible states for machine evaluation.<br/>Possible values are: `success`, `in_progress`, `failed`. | |
||||
| `descriptiveState` | string | No | | descriptiveState is an optional more descriptive state field which has no requirements on format | |
||||
| `details` | [object](#details) | No | | details contains any extra information that is operator-specific | |
||||
|
||||
### Details |
||||
|
||||
details contains any extra information that is operator-specific |
||||
|
||||
| Property | Type | Required | Default | Description | |
||||
|----------|------|----------|---------|-------------| |
||||
|
||||
|
@ -1,47 +0,0 @@ |
||||
package kind |
||||
|
||||
name: "Playlist" |
||||
maturity: "merged" |
||||
description: "A playlist is a series of dashboards that is automatically rotated in the browser, on a configurable interval." |
||||
|
||||
lineage: schemas: [{ |
||||
version: [0, 0] |
||||
schema: { |
||||
spec: { |
||||
// Unique playlist identifier. Generated on creation, either by the |
||||
// creator of the playlist of by the application. |
||||
uid: string |
||||
|
||||
// Name of the playlist. |
||||
name: string |
||||
|
||||
// Interval sets the time between switching views in a playlist. |
||||
// FIXME: Is this based on a standardized format or what options are available? Can datemath be used? |
||||
interval: string | *"5m" |
||||
|
||||
// The ordered list of items that the playlist will iterate over. |
||||
// FIXME! This should not be optional, but changing it makes the godegen awkward |
||||
items?: [...#PlaylistItem] |
||||
} @cuetsy(kind="interface") |
||||
|
||||
/////////////////////////////////////// |
||||
// Definitions (referenced above) are declared below |
||||
|
||||
#PlaylistItem: { |
||||
// Type of the item. |
||||
type: "dashboard_by_uid" | "dashboard_by_id" | "dashboard_by_tag" |
||||
// Value depends on type and describes the playlist item. |
||||
// |
||||
// - dashboard_by_id: The value is an internal numerical identifier set by Grafana. This |
||||
// is not portable as the numerical identifier is non-deterministic between different instances. |
||||
// Will be replaced by dashboard_by_uid in the future. (deprecated) |
||||
// - dashboard_by_tag: The value is a tag which is set on any number of dashboards. All |
||||
// dashboards behind the tag will be added to the playlist. |
||||
// - dashboard_by_uid: The value is the dashboard UID |
||||
value: string |
||||
|
||||
// Title is an unused property -- it will be removed in the future |
||||
title?: string |
||||
} @cuetsy(kind="interface") |
||||
} |
||||
}] |
@ -1,58 +0,0 @@ |
||||
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
|
||||
//
|
||||
// Generated by:
|
||||
// kinds/gen.go
|
||||
// Using jennies:
|
||||
// TSResourceJenny
|
||||
// LatestMajorsOrXJenny
|
||||
//
|
||||
// Run 'make gen-cue' from repository root to regenerate.
|
||||
|
||||
export interface PlaylistItem { |
||||
/** |
||||
* Title is an unused property -- it will be removed in the future |
||||
*/ |
||||
title?: string; |
||||
/** |
||||
* Type of the item. |
||||
*/ |
||||
type: ('dashboard_by_uid' | 'dashboard_by_id' | 'dashboard_by_tag'); |
||||
/** |
||||
* Value depends on type and describes the playlist item. |
||||
*
|
||||
* - dashboard_by_id: The value is an internal numerical identifier set by Grafana. This |
||||
* is not portable as the numerical identifier is non-deterministic between different instances. |
||||
* Will be replaced by dashboard_by_uid in the future. (deprecated) |
||||
* - dashboard_by_tag: The value is a tag which is set on any number of dashboards. All |
||||
* dashboards behind the tag will be added to the playlist. |
||||
* - dashboard_by_uid: The value is the dashboard UID |
||||
*/ |
||||
value: string; |
||||
} |
||||
|
||||
export interface Playlist { |
||||
/** |
||||
* Interval sets the time between switching views in a playlist. |
||||
* FIXME: Is this based on a standardized format or what options are available? Can datemath be used? |
||||
*/ |
||||
interval: string; |
||||
/** |
||||
* The ordered list of items that the playlist will iterate over. |
||||
* FIXME! This should not be optional, but changing it makes the godegen awkward |
||||
*/ |
||||
items?: Array<PlaylistItem>; |
||||
/** |
||||
* Name of the playlist. |
||||
*/ |
||||
name: string; |
||||
/** |
||||
* Unique playlist identifier. Generated on creation, either by the |
||||
* creator of the playlist of by the application. |
||||
*/ |
||||
uid: string; |
||||
} |
||||
|
||||
export const defaultPlaylist: Partial<Playlist> = { |
||||
interval: '5m', |
||||
items: [], |
||||
}; |
@ -0,0 +1,47 @@ |
||||
package v0alpha1 |
||||
|
||||
import ( |
||||
"fmt" |
||||
"time" |
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" |
||||
"k8s.io/apimachinery/pkg/types" |
||||
|
||||
"github.com/grafana/grafana/pkg/services/playlist" |
||||
) |
||||
|
||||
type namespaceMapper = func(orgId int64) string |
||||
|
||||
func orgNamespaceMapper(orgId int64) string { |
||||
if orgId == 1 { |
||||
return "default" |
||||
} |
||||
return fmt.Sprintf("org-%d", orgId) |
||||
} |
||||
|
||||
func convertToK8sResource(v *playlist.PlaylistDTO, namespacer namespaceMapper) *Playlist { |
||||
spec := Spec{ |
||||
Title: v.Name, |
||||
Interval: v.Interval, |
||||
} |
||||
for _, item := range v.Items { |
||||
spec.Items = append(spec.Items, Item{ |
||||
Type: ItemType(item.Type), |
||||
Value: item.Value, |
||||
}) |
||||
} |
||||
return &Playlist{ |
||||
TypeMeta: metav1.TypeMeta{ |
||||
Kind: "Playlist", |
||||
APIVersion: APIVersion, |
||||
}, |
||||
ObjectMeta: metav1.ObjectMeta{ |
||||
Name: v.Uid, |
||||
UID: types.UID(v.Uid), |
||||
ResourceVersion: fmt.Sprintf("%d", v.UpdatedAt), |
||||
CreationTimestamp: metav1.NewTime(time.UnixMilli(v.CreatedAt)), |
||||
Namespace: namespacer(v.OrgID), |
||||
}, |
||||
Spec: spec, |
||||
} |
||||
} |
@ -0,0 +1,64 @@ |
||||
package v0alpha1 |
||||
|
||||
import ( |
||||
"encoding/json" |
||||
"testing" |
||||
|
||||
"github.com/stretchr/testify/require" |
||||
|
||||
"github.com/grafana/grafana/pkg/services/playlist" |
||||
) |
||||
|
||||
func TestPlaylistConversion(t *testing.T) { |
||||
src := &playlist.PlaylistDTO{ |
||||
OrgID: 3, |
||||
Uid: "abc", // becomes k8s name
|
||||
Name: "MyPlaylists", // becomes title
|
||||
Interval: "10s", |
||||
CreatedAt: 12345, |
||||
UpdatedAt: 54321, |
||||
Items: []playlist.PlaylistItemDTO{ |
||||
{Type: "dashboard_by_uid", Value: "UID0"}, |
||||
{Type: "dashboard_by_tag", Value: "tagA"}, |
||||
{Type: "dashboard_by_id", Value: "123"}, // deprecated
|
||||
}, |
||||
} |
||||
dst := convertToK8sResource(src, orgNamespaceMapper) |
||||
|
||||
require.Equal(t, "abc", src.Uid) |
||||
require.Equal(t, "abc", dst.Name) |
||||
require.Equal(t, src.Name, dst.Spec.Title) |
||||
|
||||
out, err := json.MarshalIndent(dst, "", " ") |
||||
require.NoError(t, err) |
||||
//fmt.Printf("%s", string(out))
|
||||
require.JSONEq(t, `{ |
||||
"kind": "Playlist", |
||||
"apiVersion": "playlist.x.grafana.com/v0alpha1", |
||||
"metadata": { |
||||
"name": "abc", |
||||
"namespace": "org-3", |
||||
"uid": "abc", |
||||
"resourceVersion": "54321", |
||||
"creationTimestamp": "1970-01-01T00:00:12Z" |
||||
}, |
||||
"spec": { |
||||
"title": "MyPlaylists", |
||||
"interval": "10s", |
||||
"items": [ |
||||
{ |
||||
"type": "dashboard_by_uid", |
||||
"value": "UID0" |
||||
}, |
||||
{ |
||||
"type": "dashboard_by_tag", |
||||
"value": "tagA" |
||||
}, |
||||
{ |
||||
"type": "dashboard_by_id", |
||||
"value": "123" |
||||
} |
||||
] |
||||
} |
||||
}`, string(out)) |
||||
} |
@ -1,115 +0,0 @@ |
||||
package v0alpha1 |
||||
|
||||
import ( |
||||
common "k8s.io/kube-openapi/pkg/common" |
||||
spec "k8s.io/kube-openapi/pkg/validation/spec" |
||||
) |
||||
|
||||
// NOTE: this must match the golang fully qualifid name!
|
||||
const kindKey = "github.com/grafana/grafana/pkg/apis/playlist/v0alpha1.Playlist" |
||||
|
||||
func getOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition { |
||||
return map[string]common.OpenAPIDefinition{ |
||||
kindKey: schema_pkg_Playlist(ref), |
||||
kindKey + "List": schema_pkg_PlaylistList(ref), |
||||
} |
||||
} |
||||
|
||||
func schema_pkg_Playlist(ref common.ReferenceCallback) common.OpenAPIDefinition { |
||||
return common.OpenAPIDefinition{ |
||||
Schema: spec.Schema{ |
||||
SchemaProps: spec.SchemaProps{ |
||||
Description: "Playlist", |
||||
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"), |
||||
}, |
||||
}, |
||||
// TODO! add a real spec here
|
||||
// "spec": {
|
||||
// SchemaProps: spec.SchemaProps{
|
||||
// Default: map[string]interface{}{},
|
||||
// Ref: ref("github.com/grafana/google-sheets-datasource/pkg/apis/googlesheets/v1.DatasourceSpec"),
|
||||
// },
|
||||
// },
|
||||
// "status": {
|
||||
// SchemaProps: spec.SchemaProps{
|
||||
// Default: map[string]interface{}{},
|
||||
// Ref: ref("github.com/grafana/google-sheets-datasource/pkg/apis/googlesheets/v1.DatasourceStatus"),
|
||||
// },
|
||||
// },
|
||||
}, |
||||
}, |
||||
}, |
||||
Dependencies: []string{ |
||||
"k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta", |
||||
}, |
||||
} |
||||
} |
||||
|
||||
func schema_pkg_PlaylistList(ref common.ReferenceCallback) common.OpenAPIDefinition { |
||||
return common.OpenAPIDefinition{ |
||||
Schema: spec.Schema{ |
||||
SchemaProps: spec.SchemaProps{ |
||||
Description: "PlaylistList", |
||||
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(kindKey), |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
Required: []string{"items"}, |
||||
}, |
||||
}, |
||||
Dependencies: []string{ |
||||
kindKey, |
||||
"k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, |
||||
} |
||||
} |
@ -0,0 +1,42 @@ |
||||
package v0alpha1 |
||||
|
||||
import ( |
||||
"encoding/json" |
||||
"testing" |
||||
"time" |
||||
|
||||
"github.com/stretchr/testify/require" |
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" |
||||
) |
||||
|
||||
func TestPlaylistClone(t *testing.T) { |
||||
src := Playlist{ |
||||
TypeMeta: metav1.TypeMeta{ |
||||
Kind: "Playlist", |
||||
APIVersion: APIVersion, |
||||
}, |
||||
ObjectMeta: metav1.ObjectMeta{ |
||||
Name: "TheUID", |
||||
ResourceVersion: "12345", |
||||
CreationTimestamp: metav1.NewTime(time.Now()), |
||||
Annotations: map[string]string{ |
||||
"grafana.com/updatedTime": time.Now().Format(time.RFC3339), |
||||
}, |
||||
}, |
||||
Spec: Spec{ |
||||
Title: "A title", |
||||
Interval: "20s", |
||||
Items: []Item{ |
||||
{Type: ItemTypeDashboardByTag, Value: "graph-ng"}, |
||||
}, |
||||
}, |
||||
} |
||||
copy := src.DeepCopyObject() |
||||
|
||||
json0, err := json.Marshal(src) |
||||
require.NoError(t, err) |
||||
json1, err := json.Marshal(copy) |
||||
require.NoError(t, err) |
||||
|
||||
require.JSONEq(t, string(json0), string(json1)) |
||||
} |
@ -0,0 +1,188 @@ |
||||
//go:build !ignore_autogenerated
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
// Code generated by openapi-gen. DO NOT EDIT.
|
||||
|
||||
// This file was autogenerated by openapi-gen. Do not edit it manually!
|
||||
|
||||
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/playlist/v0alpha1.Item": schema_pkg_apis_playlist_v0alpha1_Item(ref), |
||||
"github.com/grafana/grafana/pkg/apis/playlist/v0alpha1.Playlist": schema_pkg_apis_playlist_v0alpha1_Playlist(ref), |
||||
"github.com/grafana/grafana/pkg/apis/playlist/v0alpha1.PlaylistList": schema_pkg_apis_playlist_v0alpha1_PlaylistList(ref), |
||||
"github.com/grafana/grafana/pkg/apis/playlist/v0alpha1.Spec": schema_pkg_apis_playlist_v0alpha1_Spec(ref), |
||||
} |
||||
} |
||||
|
||||
func schema_pkg_apis_playlist_v0alpha1_Item(ref common.ReferenceCallback) common.OpenAPIDefinition { |
||||
return common.OpenAPIDefinition{ |
||||
Schema: spec.Schema{ |
||||
SchemaProps: spec.SchemaProps{ |
||||
Description: "Item defines model for Item.", |
||||
Type: []string{"object"}, |
||||
Properties: map[string]spec.Schema{ |
||||
"type": { |
||||
SchemaProps: spec.SchemaProps{ |
||||
Description: "Type of the item.", |
||||
Default: "", |
||||
Type: []string{"string"}, |
||||
Format: "", |
||||
}, |
||||
}, |
||||
"value": { |
||||
SchemaProps: spec.SchemaProps{ |
||||
Description: "Value depends on type and describes the playlist item.\n\n - dashboard_by_id: The value is an internal numerical identifier set by Grafana. This\n is not portable as the numerical identifier is non-deterministic between different instances.\n Will be replaced by dashboard_by_uid in the future. (deprecated)\n - dashboard_by_tag: The value is a tag which is set on any number of dashboards. All\n dashboards behind the tag will be added to the playlist.\n - dashboard_by_uid: The value is the dashboard UID", |
||||
Default: "", |
||||
Type: []string{"string"}, |
||||
Format: "", |
||||
}, |
||||
}, |
||||
}, |
||||
Required: []string{"type", "value"}, |
||||
}, |
||||
}, |
||||
} |
||||
} |
||||
|
||||
func schema_pkg_apis_playlist_v0alpha1_Playlist(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{ |
||||
Description: "Standard object's metadata More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", |
||||
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/playlist/v0alpha1.Spec"), |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
Dependencies: []string{ |
||||
"github.com/grafana/grafana/pkg/apis/playlist/v0alpha1.Spec", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, |
||||
} |
||||
} |
||||
|
||||
func schema_pkg_apis_playlist_v0alpha1_PlaylistList(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/playlist/v0alpha1.Playlist"), |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
Dependencies: []string{ |
||||
"github.com/grafana/grafana/pkg/apis/playlist/v0alpha1.Playlist", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, |
||||
} |
||||
} |
||||
|
||||
func schema_pkg_apis_playlist_v0alpha1_Spec(ref common.ReferenceCallback) common.OpenAPIDefinition { |
||||
return common.OpenAPIDefinition{ |
||||
Schema: spec.Schema{ |
||||
SchemaProps: spec.SchemaProps{ |
||||
Description: "Spec defines model for Spec.", |
||||
Type: []string{"object"}, |
||||
Properties: map[string]spec.Schema{ |
||||
"title": { |
||||
SchemaProps: spec.SchemaProps{ |
||||
Description: "Name of the playlist.", |
||||
Default: "", |
||||
Type: []string{"string"}, |
||||
Format: "", |
||||
}, |
||||
}, |
||||
"interval": { |
||||
SchemaProps: spec.SchemaProps{ |
||||
Description: "Interval sets the time between switching views in a playlist.", |
||||
Default: "", |
||||
Type: []string{"string"}, |
||||
Format: "", |
||||
}, |
||||
}, |
||||
"items": { |
||||
SchemaProps: spec.SchemaProps{ |
||||
Description: "The ordered list of items that the playlist will iterate over.", |
||||
Type: []string{"array"}, |
||||
Items: &spec.SchemaOrArray{ |
||||
Schema: &spec.Schema{ |
||||
SchemaProps: spec.SchemaProps{ |
||||
Default: map[string]interface{}{}, |
||||
Ref: ref("github.com/grafana/grafana/pkg/apis/playlist/v0alpha1.Item"), |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
Required: []string{"title", "interval"}, |
||||
}, |
||||
}, |
||||
Dependencies: []string{ |
||||
"github.com/grafana/grafana/pkg/apis/playlist/v0alpha1.Item"}, |
||||
} |
||||
} |
@ -1,39 +0,0 @@ |
||||
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
|
||||
//
|
||||
// Generated by:
|
||||
// kinds/gen.go
|
||||
// Using jennies:
|
||||
// GoTypesJenny
|
||||
//
|
||||
// Run 'make gen-cue' from repository root to regenerate.
|
||||
|
||||
package playlist |
||||
|
||||
import ( |
||||
"github.com/grafana/grafana/pkg/kinds" |
||||
) |
||||
|
||||
// Resource is the kubernetes style representation of Playlist. (TODO be better)
|
||||
type K8sResource = kinds.GrafanaResource[Spec, Status] |
||||
|
||||
// NewResource creates a new instance of the resource with a given name (UID)
|
||||
func NewK8sResource(name string, s *Spec) K8sResource { |
||||
return K8sResource{ |
||||
Kind: "Playlist", |
||||
APIVersion: "v0-0-alpha", |
||||
Metadata: kinds.GrafanaResourceMetadata{ |
||||
Name: name, |
||||
Annotations: make(map[string]string), |
||||
Labels: make(map[string]string), |
||||
}, |
||||
Spec: s, |
||||
} |
||||
} |
||||
|
||||
// Resource is the wire representation of Playlist.
|
||||
// It currently will soon be merged into the k8s flavor (TODO be better)
|
||||
type Resource struct { |
||||
Metadata Metadata `json:"metadata"` |
||||
Spec Spec `json:"spec"` |
||||
Status Status `json:"status"` |
||||
} |
@ -1,79 +0,0 @@ |
||||
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
|
||||
//
|
||||
// Generated by:
|
||||
// kinds/gen.go
|
||||
// Using jennies:
|
||||
// CoreKindJenny
|
||||
//
|
||||
// Run 'make gen-cue' from repository root to regenerate.
|
||||
|
||||
package playlist |
||||
|
||||
import ( |
||||
"github.com/grafana/kindsys" |
||||
"github.com/grafana/thema" |
||||
"github.com/grafana/thema/vmux" |
||||
|
||||
"github.com/grafana/grafana/pkg/cuectx" |
||||
) |
||||
|
||||
// rootrel is the relative path from the grafana repository root to the
|
||||
// directory containing the .cue files in which this kind is defined. Necessary
|
||||
// for runtime errors related to the definition and/or lineage to provide
|
||||
// a real path to the correct .cue file.
|
||||
const rootrel string = "kinds/playlist" |
||||
|
||||
// TODO standard generated docs
|
||||
type Kind struct { |
||||
kindsys.Core |
||||
lin thema.ConvergentLineage[*Resource] |
||||
jcodec vmux.Codec |
||||
valmux vmux.ValueMux[*Resource] |
||||
} |
||||
|
||||
// type guard - ensure generated Kind type satisfies the kindsys.Core interface
|
||||
var _ kindsys.Core = &Kind{} |
||||
|
||||
// TODO standard generated docs
|
||||
func NewKind(rt *thema.Runtime, opts ...thema.BindOption) (*Kind, error) { |
||||
def, err := cuectx.LoadCoreKindDef(rootrel, rt.Context(), nil) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
k := &Kind{} |
||||
k.Core, err = kindsys.BindCore(rt, def, opts...) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
// Get the thema.Schema that the meta says is in the current version (which
|
||||
// codegen ensures is always the latest)
|
||||
cursch := thema.SchemaP(k.Core.Lineage(), def.Properties.CurrentVersion) |
||||
tsch, err := thema.BindType(cursch, &Resource{}) |
||||
if err != nil { |
||||
// Should be unreachable, modulo bugs in the Thema->Go code generator
|
||||
return nil, err |
||||
} |
||||
|
||||
k.jcodec = vmux.NewJSONCodec("playlist.json") |
||||
k.lin = tsch.ConvergentLineage() |
||||
k.valmux = vmux.NewValueMux(k.lin.TypedSchema(), k.jcodec) |
||||
return k, nil |
||||
} |
||||
|
||||
// ConvergentLineage returns the same [thema.Lineage] as Lineage, but bound (see [thema.BindType])
|
||||
// to the the Playlist [Resource] type generated from the current schema, v0.0.
|
||||
func (k *Kind) ConvergentLineage() thema.ConvergentLineage[*Resource] { |
||||
return k.lin |
||||
} |
||||
|
||||
// JSONValueMux is a version multiplexer that maps a []byte containing JSON data
|
||||
// at any schematized dashboard version to an instance of Playlist [Resource].
|
||||
//
|
||||
// Validation and translation errors emitted from this func will identify the
|
||||
// input bytes as "dashboard.json".
|
||||
//
|
||||
// This is a thin wrapper around Thema's [vmux.ValueMux].
|
||||
func (k *Kind) JSONValueMux(b []byte) (*Resource, thema.TranslationLacunas, error) { |
||||
return k.valmux(b) |
||||
} |
@ -1,42 +0,0 @@ |
||||
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
|
||||
//
|
||||
// Generated by:
|
||||
// kinds/gen.go
|
||||
// Using jennies:
|
||||
// GoResourceTypes
|
||||
//
|
||||
// Run 'make gen-cue' from repository root to regenerate.
|
||||
|
||||
package playlist |
||||
|
||||
import ( |
||||
"time" |
||||
) |
||||
|
||||
// Metadata defines model for Metadata.
|
||||
type Metadata struct { |
||||
CreatedBy string `json:"createdBy"` |
||||
CreationTimestamp time.Time `json:"creationTimestamp"` |
||||
DeletionTimestamp *time.Time `json:"deletionTimestamp,omitempty"` |
||||
|
||||
// extraFields is reserved for any fields that are pulled from the API server metadata but do not have concrete fields in the CUE metadata
|
||||
ExtraFields map[string]any `json:"extraFields"` |
||||
Finalizers []string `json:"finalizers"` |
||||
Labels map[string]string `json:"labels"` |
||||
ResourceVersion string `json:"resourceVersion"` |
||||
Uid string `json:"uid"` |
||||
UpdateTimestamp time.Time `json:"updateTimestamp"` |
||||
UpdatedBy string `json:"updatedBy"` |
||||
} |
||||
|
||||
// _kubeObjectMetadata is metadata found in a kubernetes object's metadata field.
|
||||
// It is not exhaustive and only includes fields which may be relevant to a kind's implementation,
|
||||
// As it is also intended to be generic enough to function with any API Server.
|
||||
type KubeObjectMetadata struct { |
||||
CreationTimestamp time.Time `json:"creationTimestamp"` |
||||
DeletionTimestamp *time.Time `json:"deletionTimestamp,omitempty"` |
||||
Finalizers []string `json:"finalizers"` |
||||
Labels map[string]string `json:"labels"` |
||||
ResourceVersion string `json:"resourceVersion"` |
||||
Uid string `json:"uid"` |
||||
} |
@ -1,57 +0,0 @@ |
||||
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
|
||||
//
|
||||
// Generated by:
|
||||
// kinds/gen.go
|
||||
// Using jennies:
|
||||
// GoResourceTypes
|
||||
//
|
||||
// Run 'make gen-cue' from repository root to regenerate.
|
||||
|
||||
package playlist |
||||
|
||||
// Defines values for ItemType.
|
||||
const ( |
||||
ItemTypeDashboardById ItemType = "dashboard_by_id" |
||||
ItemTypeDashboardByTag ItemType = "dashboard_by_tag" |
||||
ItemTypeDashboardByUid ItemType = "dashboard_by_uid" |
||||
) |
||||
|
||||
// Item defines model for Item.
|
||||
type Item struct { |
||||
// Title is an unused property -- it will be removed in the future
|
||||
Title *string `json:"title,omitempty"` |
||||
|
||||
// Type of the item.
|
||||
Type ItemType `json:"type"` |
||||
|
||||
// Value depends on type and describes the playlist item.
|
||||
//
|
||||
// - dashboard_by_id: The value is an internal numerical identifier set by Grafana. This
|
||||
// is not portable as the numerical identifier is non-deterministic between different instances.
|
||||
// Will be replaced by dashboard_by_uid in the future. (deprecated)
|
||||
// - dashboard_by_tag: The value is a tag which is set on any number of dashboards. All
|
||||
// dashboards behind the tag will be added to the playlist.
|
||||
// - dashboard_by_uid: The value is the dashboard UID
|
||||
Value string `json:"value"` |
||||
} |
||||
|
||||
// Type of the item.
|
||||
type ItemType string |
||||
|
||||
// Spec defines model for Spec.
|
||||
type Spec struct { |
||||
// Interval sets the time between switching views in a playlist.
|
||||
// FIXME: Is this based on a standardized format or what options are available? Can datemath be used?
|
||||
Interval string `json:"interval"` |
||||
|
||||
// The ordered list of items that the playlist will iterate over.
|
||||
// FIXME! This should not be optional, but changing it makes the godegen awkward
|
||||
Items []Item `json:"items,omitempty"` |
||||
|
||||
// Name of the playlist.
|
||||
Name string `json:"name"` |
||||
|
||||
// Unique playlist identifier. Generated on creation, either by the
|
||||
// creator of the playlist of by the application.
|
||||
Uid string `json:"uid"` |
||||
} |
@ -1,74 +0,0 @@ |
||||
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
|
||||
//
|
||||
// Generated by:
|
||||
// kinds/gen.go
|
||||
// Using jennies:
|
||||
// GoResourceTypes
|
||||
//
|
||||
// Run 'make gen-cue' from repository root to regenerate.
|
||||
|
||||
package playlist |
||||
|
||||
// Defines values for OperatorStateState.
|
||||
const ( |
||||
OperatorStateStateFailed OperatorStateState = "failed" |
||||
OperatorStateStateInProgress OperatorStateState = "in_progress" |
||||
OperatorStateStateSuccess OperatorStateState = "success" |
||||
) |
||||
|
||||
// Defines values for StatusOperatorStateState.
|
||||
const ( |
||||
StatusOperatorStateStateFailed StatusOperatorStateState = "failed" |
||||
StatusOperatorStateStateInProgress StatusOperatorStateState = "in_progress" |
||||
StatusOperatorStateStateSuccess StatusOperatorStateState = "success" |
||||
) |
||||
|
||||
// OperatorState defines model for OperatorState.
|
||||
type OperatorState struct { |
||||
// descriptiveState is an optional more descriptive state field which has no requirements on format
|
||||
DescriptiveState *string `json:"descriptiveState,omitempty"` |
||||
|
||||
// details contains any extra information that is operator-specific
|
||||
Details map[string]any `json:"details,omitempty"` |
||||
|
||||
// lastEvaluation is the ResourceVersion last evaluated
|
||||
LastEvaluation string `json:"lastEvaluation"` |
||||
|
||||
// state describes the state of the lastEvaluation.
|
||||
// It is limited to three possible states for machine evaluation.
|
||||
State OperatorStateState `json:"state"` |
||||
} |
||||
|
||||
// OperatorStateState state describes the state of the lastEvaluation.
|
||||
// It is limited to three possible states for machine evaluation.
|
||||
type OperatorStateState string |
||||
|
||||
// Status defines model for Status.
|
||||
type Status struct { |
||||
// additionalFields is reserved for future use
|
||||
AdditionalFields map[string]any `json:"additionalFields,omitempty"` |
||||
|
||||
// operatorStates is a map of operator ID to operator state evaluations.
|
||||
// Any operator which consumes this kind SHOULD add its state evaluation information to this field.
|
||||
OperatorStates map[string]StatusOperatorState `json:"operatorStates,omitempty"` |
||||
} |
||||
|
||||
// StatusOperatorState defines model for status.#OperatorState.
|
||||
type StatusOperatorState struct { |
||||
// descriptiveState is an optional more descriptive state field which has no requirements on format
|
||||
DescriptiveState *string `json:"descriptiveState,omitempty"` |
||||
|
||||
// details contains any extra information that is operator-specific
|
||||
Details map[string]any `json:"details,omitempty"` |
||||
|
||||
// lastEvaluation is the ResourceVersion last evaluated
|
||||
LastEvaluation string `json:"lastEvaluation"` |
||||
|
||||
// state describes the state of the lastEvaluation.
|
||||
// It is limited to three possible states for machine evaluation.
|
||||
State StatusOperatorStateState `json:"state"` |
||||
} |
||||
|
||||
// StatusOperatorStateState state describes the state of the lastEvaluation.
|
||||
// It is limited to three possible states for machine evaluation.
|
||||
type StatusOperatorStateState string |
@ -1,68 +0,0 @@ |
||||
package playlist |
||||
|
||||
import ( |
||||
"context" |
||||
"encoding/json" |
||||
"fmt" |
||||
|
||||
"github.com/grafana/grafana/pkg/kinds/playlist" |
||||
"github.com/grafana/grafana/pkg/services/store/entity" |
||||
) |
||||
|
||||
func GetEntityKindInfo() entity.EntityKindInfo { |
||||
return entity.EntityKindInfo{ |
||||
ID: entity.StandardKindPlaylist, |
||||
Name: "Playlist", |
||||
Description: "Cycle though a collection of dashboards automatically", |
||||
} |
||||
} |
||||
|
||||
func GetEntitySummaryBuilder() entity.EntitySummaryBuilder { |
||||
return summaryBuilder |
||||
} |
||||
|
||||
func summaryBuilder(ctx context.Context, uid string, body []byte) (*entity.EntitySummary, []byte, error) { |
||||
obj := &playlist.Spec{} |
||||
err := json.Unmarshal(body, obj) |
||||
if err != nil { |
||||
return nil, nil, err // unable to read object
|
||||
} |
||||
|
||||
// TODO: fix model so this is not possible
|
||||
if obj.Items == nil { |
||||
temp := make([]playlist.Item, 0) |
||||
obj.Items = temp |
||||
} |
||||
|
||||
obj.Uid = uid // make sure they are consistent
|
||||
summary := &entity.EntitySummary{ |
||||
UID: uid, |
||||
Name: obj.Name, |
||||
Description: fmt.Sprintf("%d items, refreshed every %s", len(obj.Items), obj.Interval), |
||||
} |
||||
|
||||
for _, item := range obj.Items { |
||||
switch item.Type { |
||||
case playlist.ItemTypeDashboardByUid: |
||||
summary.References = append(summary.References, &entity.EntityExternalReference{ |
||||
Family: entity.StandardKindDashboard, |
||||
Identifier: item.Value, |
||||
}) |
||||
|
||||
case playlist.ItemTypeDashboardByTag: |
||||
if summary.Labels == nil { |
||||
summary.Labels = make(map[string]string, 0) |
||||
} |
||||
summary.Labels[item.Value] = "" |
||||
|
||||
case playlist.ItemTypeDashboardById: |
||||
// obviously insufficient long term... but good to have an example :)
|
||||
summary.Error = &entity.EntityErrorInfo{ |
||||
Message: "Playlist uses deprecated internal id system", |
||||
} |
||||
} |
||||
} |
||||
|
||||
out, err := json.MarshalIndent(obj, "", " ") |
||||
return summary, out, err |
||||
} |
@ -1,40 +0,0 @@ |
||||
package playlist |
||||
|
||||
import ( |
||||
"context" |
||||
"encoding/json" |
||||
"testing" |
||||
|
||||
"github.com/stretchr/testify/require" |
||||
|
||||
"github.com/grafana/grafana/pkg/kinds/playlist" |
||||
) |
||||
|
||||
func TestPlaylistSummary(t *testing.T) { |
||||
builder := GetEntitySummaryBuilder() |
||||
|
||||
// Do not parse invalid input
|
||||
_, _, err := builder(context.Background(), "abc", []byte("{invalid json")) |
||||
require.Error(t, err) |
||||
|
||||
playlist := playlist.Spec{ |
||||
Interval: "30s", |
||||
Name: "test", |
||||
Items: []playlist.Item{ |
||||
{Type: playlist.ItemTypeDashboardByUid, Value: "D1"}, |
||||
{Type: playlist.ItemTypeDashboardByTag, Value: "tagA"}, |
||||
{Type: playlist.ItemTypeDashboardByUid, Value: "D3"}, |
||||
}, |
||||
} |
||||
out, err := json.Marshal(playlist) |
||||
require.NoError(t, err) |
||||
require.NotNil(t, out) |
||||
|
||||
// Do not parse invalid input
|
||||
summary, body, err := builder(context.Background(), "abc", out) |
||||
require.NoError(t, err) |
||||
require.Equal(t, "test", summary.Name) |
||||
require.Equal(t, 2, len(summary.References)) |
||||
require.Equal(t, map[string]string{"tagA": ""}, summary.Labels) |
||||
require.True(t, json.Valid(body)) |
||||
} |
Loading…
Reference in new issue