Compare commits
3 Commits
selectable
...
live-apise
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3679bfe924 | ||
|
|
b8234e2c39 | ||
|
|
870d881e63 |
@@ -8,12 +8,6 @@ foldersV1beta1: {
|
||||
spec: {
|
||||
title: string
|
||||
description?: string
|
||||
foo: bool
|
||||
bar: int
|
||||
}
|
||||
}
|
||||
|
||||
selectableFields: [
|
||||
"spec.title",
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -5,26 +5,13 @@
|
||||
package v1beta1
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/resource"
|
||||
)
|
||||
|
||||
// schema is unexported to prevent accidental overwrites
|
||||
var (
|
||||
schemaFolder = resource.NewSimpleSchema("folder.grafana.app", "v1beta1", NewFolder(), &FolderList{}, resource.WithKind("Folder"),
|
||||
resource.WithPlural("folders"), resource.WithScope(resource.NamespacedScope), resource.WithSelectableFields([]resource.SelectableField{resource.SelectableField{
|
||||
FieldSelector: "spec.title",
|
||||
FieldValueFunc: func(o resource.Object) (string, error) {
|
||||
cast, ok := o.(*Folder)
|
||||
if !ok {
|
||||
return "", errors.New("provided object must be of type *Folder")
|
||||
}
|
||||
|
||||
return cast.Spec.Title, nil
|
||||
},
|
||||
},
|
||||
}))
|
||||
resource.WithPlural("folders"), resource.WithScope(resource.NamespacedScope))
|
||||
kindFolder = resource.Kind{
|
||||
Schema: schemaFolder,
|
||||
Codecs: map[resource.KindEncoding]resource.Codec{
|
||||
|
||||
@@ -18,8 +18,6 @@ import (
|
||||
v1beta1 "github.com/grafana/grafana/apps/folder/pkg/apis/folder/v1beta1"
|
||||
)
|
||||
|
||||
var ()
|
||||
|
||||
var appManifestData = app.ManifestData{
|
||||
AppName: "folder",
|
||||
Group: "folder.grafana.app",
|
||||
@@ -34,9 +32,6 @@ var appManifestData = app.ManifestData{
|
||||
Plural: "Folders",
|
||||
Scope: "Namespaced",
|
||||
Conversion: false,
|
||||
SelectableFields: []string{
|
||||
"spec.title",
|
||||
},
|
||||
},
|
||||
},
|
||||
Routes: app.ManifestVersionRoutes{
|
||||
|
||||
@@ -14,7 +14,7 @@ userKind: {
|
||||
}
|
||||
|
||||
userv0alpha1: userKind & {
|
||||
// TODO: Uncomment this when User will be added to ManagedKinds
|
||||
// TODO: Uncomment this when User will be added to ManagedKinds
|
||||
// validation: {
|
||||
// operations: [
|
||||
// "CREATE",
|
||||
|
||||
4
apps/iam/pkg/apis/iam_manifest.go
generated
4
apps/iam/pkg/apis/iam_manifest.go
generated
@@ -74,10 +74,6 @@ var appManifestData = app.ManifestData{
|
||||
Plural: "Users",
|
||||
Scope: "Namespaced",
|
||||
Conversion: false,
|
||||
SelectableFields: []string{
|
||||
"spec.email",
|
||||
"spec.login",
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
13
apps/live/Makefile
Normal file
13
apps/live/Makefile
Normal file
@@ -0,0 +1,13 @@
|
||||
include ../sdk.mk
|
||||
|
||||
.PHONY: generate
|
||||
generate: install-app-sdk update-app-sdk ## Run Grafana App SDK code generation
|
||||
@$(APP_SDK_BIN) generate \
|
||||
--source=./kinds/ \
|
||||
--gogenpath=./pkg/apis \
|
||||
--grouping=group \
|
||||
--defencoding=none \
|
||||
--genoperatorstate=false \
|
||||
--noschemasinmanifest
|
||||
|
||||
|
||||
67
apps/live/go.mod
Normal file
67
apps/live/go.mod
Normal file
@@ -0,0 +1,67 @@
|
||||
module github.com/grafana/grafana/apps/live
|
||||
|
||||
go 1.25.5
|
||||
|
||||
require (
|
||||
github.com/grafana/grafana-app-sdk v0.48.5
|
||||
github.com/grafana/grafana-app-sdk/logging v0.48.3
|
||||
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20251017153501-8512b219c5fe
|
||||
k8s.io/apimachinery v0.34.2
|
||||
k8s.io/apiserver v0.34.2
|
||||
k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.13.0 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
|
||||
github.com/getkin/kin-openapi v0.133.0 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.22.1 // indirect
|
||||
github.com/go-openapi/jsonreference v0.21.2 // indirect
|
||||
github.com/go-openapi/swag v0.23.0 // indirect
|
||||
github.com/go-openapi/swag/jsonname v0.25.1 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/google/gnostic-models v0.7.0 // indirect
|
||||
github.com/grafana/grafana-app-sdk/logging v0.48.3 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 // indirect
|
||||
github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 // indirect
|
||||
github.com/perimeterx/marshmallow v1.1.5 // indirect
|
||||
github.com/prometheus/client_golang v1.23.2 // indirect
|
||||
github.com/prometheus/client_model v0.6.2 // indirect
|
||||
github.com/prometheus/common v0.67.2 // indirect
|
||||
github.com/prometheus/procfs v0.16.1 // indirect
|
||||
github.com/woodsbury/decimal128 v1.3.0 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
go.opentelemetry.io/otel v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.38.0 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.3 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/net v0.47.0 // indirect
|
||||
golang.org/x/oauth2 v0.33.0 // indirect
|
||||
golang.org/x/sys v0.38.0 // indirect
|
||||
golang.org/x/term v0.37.0 // indirect
|
||||
golang.org/x/text v0.31.0 // indirect
|
||||
golang.org/x/time v0.9.0 // indirect
|
||||
google.golang.org/protobuf v1.36.10 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
k8s.io/client-go v0.34.2 // indirect
|
||||
k8s.io/klog/v2 v2.130.1 // indirect
|
||||
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
|
||||
sigs.k8s.io/randfill v1.0.0 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v6 v6.3.1 // indirect
|
||||
sigs.k8s.io/yaml v1.6.0 // indirect
|
||||
)
|
||||
185
apps/live/go.sum
Normal file
185
apps/live/go.sum
Normal file
@@ -0,0 +1,185 @@
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/emicklei/go-restful/v3 v3.13.0 h1:C4Bl2xDndpU6nJ4bc1jXd+uTmYPVUwkD6bFY/oTyCes=
|
||||
github.com/emicklei/go-restful/v3 v3.13.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
|
||||
github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
|
||||
github.com/getkin/kin-openapi v0.133.0 h1:pJdmNohVIJ97r4AUFtEXRXwESr8b0bD721u/Tz6k8PQ=
|
||||
github.com/getkin/kin-openapi v0.133.0/go.mod h1:boAciF6cXk5FhPqe/NQeBTeenbjqU4LhWBf09ILVvWE=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-openapi/jsonpointer v0.22.1 h1:sHYI1He3b9NqJ4wXLoJDKmUmHkWy/L7rtEo92JUxBNk=
|
||||
github.com/go-openapi/jsonpointer v0.22.1/go.mod h1:pQT9OsLkfz1yWoMgYFy4x3U5GY5nUlsOn1qSBH5MkCM=
|
||||
github.com/go-openapi/jsonreference v0.21.2 h1:Wxjda4M/BBQllegefXrY/9aq1fxBA8sI5M/lFU6tSWU=
|
||||
github.com/go-openapi/jsonreference v0.21.2/go.mod h1:pp3PEjIsJ9CZDGCNOyXIQxsNuroxm8FAJ/+quA0yKzQ=
|
||||
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
|
||||
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
|
||||
github.com/go-openapi/swag/jsonname v0.25.1 h1:Sgx+qbwa4ej6AomWC6pEfXrA6uP2RkaNjA9BR8a1RJU=
|
||||
github.com/go-openapi/swag/jsonname v0.25.1/go.mod h1:71Tekow6UOLBD3wS7XhdT98g5J5GR13NOTQ9/6Q11Zo=
|
||||
github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
|
||||
github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo=
|
||||
github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/grafana/grafana-app-sdk v0.48.5 h1:MS8l9fTZz+VbTfgApn09jw27GxhQ6fNOWGhC4ydvZmM=
|
||||
github.com/grafana/grafana-app-sdk v0.48.5/go.mod h1:HJsMOSBmt/D/Ihs1SvagOwmXKi0coBMVHlfvdd+qe9Y=
|
||||
github.com/grafana/grafana-app-sdk/logging v0.48.3 h1:72NUpGNiJXCNQz/on++YSsl38xuVYYBKv5kKQaOClX4=
|
||||
github.com/grafana/grafana-app-sdk/logging v0.48.3/go.mod h1:Gh/nBWnspK3oDNWtiM5qUF/fardHzOIEez+SPI3JeHA=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8=
|
||||
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 h1:G7ERwszslrBzRxj//JalHPu/3yz+De2J+4aLtSRlHiY=
|
||||
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037/go.mod h1:2bpvgLBZEtENV5scfDFEtB/5+1M4hkQhDQrccEJ/qGw=
|
||||
github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 h1:bQx3WeLcUWy+RletIKwUIt4x3t8n2SxavmoclizMb8c=
|
||||
github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90/go.mod h1:y5+oSEHCPT/DGrS++Wc/479ERge0zTFxaF8PbGKcg2o=
|
||||
github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s=
|
||||
github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
|
||||
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
|
||||
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
||||
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||
github.com/prometheus/common v0.67.2 h1:PcBAckGFTIHt2+L3I33uNRTlKTplNzFctXcWhPyAEN8=
|
||||
github.com/prometheus/common v0.67.2/go.mod h1:63W3KZb1JOKgcjlIr64WW/LvFGAqKPj0atm+knVGEko=
|
||||
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
|
||||
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
|
||||
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
github.com/woodsbury/decimal128 v1.3.0 h1:8pffMNWIlC0O5vbyHWFZAt5yWvWcrHA+3ovIIjVWss0=
|
||||
github.com/woodsbury/decimal128 v1.3.0/go.mod h1:C5UTmyTjW3JftjUFzOVhC20BEQa2a4ZKOB5I6Zjb+ds=
|
||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
|
||||
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
|
||||
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
|
||||
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
|
||||
go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
||||
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
||||
golang.org/x/oauth2 v0.33.0 h1:4Q+qn+E5z8gPRJfmRy7C2gGG3T4jIprK6aSYgTXGRpo=
|
||||
golang.org/x/oauth2 v0.33.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
|
||||
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
||||
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
||||
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
|
||||
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
||||
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4=
|
||||
gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
|
||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
k8s.io/api v0.34.2 h1:fsSUNZhV+bnL6Aqrp6O7lMTy6o5x2C4XLjnh//8SLYY=
|
||||
k8s.io/api v0.34.2/go.mod h1:MMBPaWlED2a8w4RSeanD76f7opUoypY8TFYkSM+3XHw=
|
||||
k8s.io/apimachinery v0.34.2 h1:zQ12Uk3eMHPxrsbUJgNF8bTauTVR2WgqJsTmwTE/NW4=
|
||||
k8s.io/apimachinery v0.34.2/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw=
|
||||
k8s.io/client-go v0.34.2 h1:Co6XiknN+uUZqiddlfAjT68184/37PS4QAzYvQvDR8M=
|
||||
k8s.io/client-go v0.34.2/go.mod h1:2VYDl1XXJsdcAxw7BenFslRQX28Dxz91U9MWKjX97fE=
|
||||
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
|
||||
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
||||
k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 h1:Y3gxNAuB0OBLImH611+UDZcmKS3g6CthxToOb37KgwE=
|
||||
k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ=
|
||||
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y=
|
||||
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE=
|
||||
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
|
||||
sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
|
||||
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
|
||||
sigs.k8s.io/structured-merge-diff/v6 v6.3.1 h1:JrhdFMqOd/+3ByqlP2I45kTOZmTRLBUm5pvRjeheg7E=
|
||||
sigs.k8s.io/structured-merge-diff/v6 v6.3.1/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
|
||||
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
|
||||
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
|
||||
13
apps/live/kinds/channel.cue
Normal file
13
apps/live/kinds/channel.cue
Normal file
@@ -0,0 +1,13 @@
|
||||
package live
|
||||
|
||||
liveV1beta1: {
|
||||
kind: "Channel"
|
||||
pluralName: "Channels"
|
||||
|
||||
schema: {
|
||||
spec: {
|
||||
title: string
|
||||
description?: string
|
||||
}
|
||||
}
|
||||
}
|
||||
4
apps/live/kinds/cue.mod/module.cue
Normal file
4
apps/live/kinds/cue.mod/module.cue
Normal file
@@ -0,0 +1,4 @@
|
||||
module: "github.com/grafana/grafana/apps/live/kinds"
|
||||
language: {
|
||||
version: "v0.11.0"
|
||||
}
|
||||
17
apps/live/kinds/manifest.cue
Normal file
17
apps/live/kinds/manifest.cue
Normal file
@@ -0,0 +1,17 @@
|
||||
package live
|
||||
|
||||
manifest: {
|
||||
appName: "live"
|
||||
groupOverride: "live.grafana.app"
|
||||
versions: {
|
||||
"v1beta1": {
|
||||
codegen: {
|
||||
ts: {enabled: false}
|
||||
go: {enabled: true}
|
||||
}
|
||||
kinds: [
|
||||
channelV1beta1,
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
80
apps/live/pkg/apis/live/v1beta1/channel_client_gen.go
generated
Normal file
80
apps/live/pkg/apis/live/v1beta1/channel_client_gen.go
generated
Normal file
@@ -0,0 +1,80 @@
|
||||
package v1beta1
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/resource"
|
||||
)
|
||||
|
||||
type ChannelClient struct {
|
||||
client *resource.TypedClient[*Channel, *ChannelList]
|
||||
}
|
||||
|
||||
func NewChannelClient(client resource.Client) *ChannelClient {
|
||||
return &ChannelClient{
|
||||
client: resource.NewTypedClient[*Channel, *ChannelList](client, ChannelKind()),
|
||||
}
|
||||
}
|
||||
|
||||
func NewChannelClientFromGenerator(generator resource.ClientGenerator) (*ChannelClient, error) {
|
||||
c, err := generator.ClientFor(ChannelKind())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewChannelClient(c), nil
|
||||
}
|
||||
|
||||
func (c *ChannelClient) Get(ctx context.Context, identifier resource.Identifier) (*Channel, error) {
|
||||
return c.client.Get(ctx, identifier)
|
||||
}
|
||||
|
||||
func (c *ChannelClient) List(ctx context.Context, namespace string, opts resource.ListOptions) (*ChannelList, error) {
|
||||
return c.client.List(ctx, namespace, opts)
|
||||
}
|
||||
|
||||
func (c *ChannelClient) ListAll(ctx context.Context, namespace string, opts resource.ListOptions) (*ChannelList, error) {
|
||||
resp, err := c.client.List(ctx, namespace, resource.ListOptions{
|
||||
ResourceVersion: opts.ResourceVersion,
|
||||
Limit: opts.Limit,
|
||||
LabelFilters: opts.LabelFilters,
|
||||
FieldSelectors: opts.FieldSelectors,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for resp.GetContinue() != "" {
|
||||
page, err := c.client.List(ctx, namespace, resource.ListOptions{
|
||||
Continue: resp.GetContinue(),
|
||||
ResourceVersion: opts.ResourceVersion,
|
||||
Limit: opts.Limit,
|
||||
LabelFilters: opts.LabelFilters,
|
||||
FieldSelectors: opts.FieldSelectors,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp.SetContinue(page.GetContinue())
|
||||
resp.SetResourceVersion(page.GetResourceVersion())
|
||||
resp.SetItems(append(resp.GetItems(), page.GetItems()...))
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (c *ChannelClient) Create(ctx context.Context, obj *Channel, opts resource.CreateOptions) (*Channel, error) {
|
||||
// Make sure apiVersion and kind are set
|
||||
obj.APIVersion = GroupVersion.Identifier()
|
||||
obj.Kind = ChannelKind().Kind()
|
||||
return c.client.Create(ctx, obj, opts)
|
||||
}
|
||||
|
||||
func (c *ChannelClient) Update(ctx context.Context, obj *Channel, opts resource.UpdateOptions) (*Channel, error) {
|
||||
return c.client.Update(ctx, obj, opts)
|
||||
}
|
||||
|
||||
func (c *ChannelClient) Patch(ctx context.Context, identifier resource.Identifier, req resource.PatchRequest, opts resource.PatchOptions) (*Channel, error) {
|
||||
return c.client.Patch(ctx, identifier, req, opts)
|
||||
}
|
||||
|
||||
func (c *ChannelClient) Delete(ctx context.Context, identifier resource.Identifier, opts resource.DeleteOptions) error {
|
||||
return c.client.Delete(ctx, identifier, opts)
|
||||
}
|
||||
28
apps/live/pkg/apis/live/v1beta1/channel_codec_gen.go
generated
Normal file
28
apps/live/pkg/apis/live/v1beta1/channel_codec_gen.go
generated
Normal file
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// Code generated by grafana-app-sdk. DO NOT EDIT.
|
||||
//
|
||||
|
||||
package v1beta1
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/resource"
|
||||
)
|
||||
|
||||
// ChannelJSONCodec is an implementation of resource.Codec for kubernetes JSON encoding
|
||||
type ChannelJSONCodec struct{}
|
||||
|
||||
// Read reads JSON-encoded bytes from `reader` and unmarshals them into `into`
|
||||
func (*ChannelJSONCodec) Read(reader io.Reader, into resource.Object) error {
|
||||
return json.NewDecoder(reader).Decode(into)
|
||||
}
|
||||
|
||||
// Write writes JSON-encoded bytes into `writer` marshaled from `from`
|
||||
func (*ChannelJSONCodec) Write(writer io.Writer, from resource.Object) error {
|
||||
return json.NewEncoder(writer).Encode(from)
|
||||
}
|
||||
|
||||
// Interface compliance checks
|
||||
var _ resource.Codec = &ChannelJSONCodec{}
|
||||
31
apps/live/pkg/apis/live/v1beta1/channel_metadata_gen.go
generated
Normal file
31
apps/live/pkg/apis/live/v1beta1/channel_metadata_gen.go
generated
Normal file
@@ -0,0 +1,31 @@
|
||||
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
|
||||
|
||||
package v1beta1
|
||||
|
||||
import (
|
||||
time "time"
|
||||
)
|
||||
|
||||
// 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.
|
||||
type ChannelMetadata struct {
|
||||
UpdateTimestamp time.Time `json:"updateTimestamp"`
|
||||
CreatedBy string `json:"createdBy"`
|
||||
Uid string `json:"uid"`
|
||||
CreationTimestamp time.Time `json:"creationTimestamp"`
|
||||
DeletionTimestamp *time.Time `json:"deletionTimestamp,omitempty"`
|
||||
Finalizers []string `json:"finalizers"`
|
||||
ResourceVersion string `json:"resourceVersion"`
|
||||
Generation int64 `json:"generation"`
|
||||
UpdatedBy string `json:"updatedBy"`
|
||||
Labels map[string]string `json:"labels"`
|
||||
}
|
||||
|
||||
// NewChannelMetadata creates a new ChannelMetadata object.
|
||||
func NewChannelMetadata() *ChannelMetadata {
|
||||
return &ChannelMetadata{
|
||||
Finalizers: []string{},
|
||||
Labels: map[string]string{},
|
||||
}
|
||||
}
|
||||
299
apps/live/pkg/apis/live/v1beta1/channel_object_gen.go
generated
Normal file
299
apps/live/pkg/apis/live/v1beta1/channel_object_gen.go
generated
Normal file
@@ -0,0 +1,299 @@
|
||||
//
|
||||
// Code generated by grafana-app-sdk. DO NOT EDIT.
|
||||
//
|
||||
|
||||
package v1beta1
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/grafana/grafana-app-sdk/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"time"
|
||||
)
|
||||
|
||||
// +k8s:openapi-gen=true
|
||||
type Channel struct {
|
||||
metav1.TypeMeta `json:",inline" yaml:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata" yaml:"metadata"`
|
||||
|
||||
// Spec is the spec of the Channel
|
||||
Spec ChannelSpec `json:"spec" yaml:"spec"`
|
||||
}
|
||||
|
||||
func NewChannel() *Channel {
|
||||
return &Channel{
|
||||
Spec: *NewChannelSpec(),
|
||||
}
|
||||
}
|
||||
|
||||
func (o *Channel) GetSpec() any {
|
||||
return o.Spec
|
||||
}
|
||||
|
||||
func (o *Channel) SetSpec(spec any) error {
|
||||
cast, ok := spec.(ChannelSpec)
|
||||
if !ok {
|
||||
return fmt.Errorf("cannot set spec type %#v, not of type Spec", spec)
|
||||
}
|
||||
o.Spec = cast
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *Channel) GetSubresources() map[string]any {
|
||||
return map[string]any{}
|
||||
}
|
||||
|
||||
func (o *Channel) GetSubresource(name string) (any, bool) {
|
||||
switch name {
|
||||
default:
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
|
||||
func (o *Channel) SetSubresource(name string, value any) error {
|
||||
switch name {
|
||||
default:
|
||||
return fmt.Errorf("subresource '%s' does not exist", name)
|
||||
}
|
||||
}
|
||||
|
||||
func (o *Channel) GetStaticMetadata() resource.StaticMetadata {
|
||||
gvk := o.GroupVersionKind()
|
||||
return resource.StaticMetadata{
|
||||
Name: o.ObjectMeta.Name,
|
||||
Namespace: o.ObjectMeta.Namespace,
|
||||
Group: gvk.Group,
|
||||
Version: gvk.Version,
|
||||
Kind: gvk.Kind,
|
||||
}
|
||||
}
|
||||
|
||||
func (o *Channel) SetStaticMetadata(metadata resource.StaticMetadata) {
|
||||
o.Name = metadata.Name
|
||||
o.Namespace = metadata.Namespace
|
||||
o.SetGroupVersionKind(schema.GroupVersionKind{
|
||||
Group: metadata.Group,
|
||||
Version: metadata.Version,
|
||||
Kind: metadata.Kind,
|
||||
})
|
||||
}
|
||||
|
||||
func (o *Channel) GetCommonMetadata() resource.CommonMetadata {
|
||||
dt := o.DeletionTimestamp
|
||||
var deletionTimestamp *time.Time
|
||||
if dt != nil {
|
||||
deletionTimestamp = &dt.Time
|
||||
}
|
||||
// Legacy ExtraFields support
|
||||
extraFields := make(map[string]any)
|
||||
if o.Annotations != nil {
|
||||
extraFields["annotations"] = o.Annotations
|
||||
}
|
||||
if o.ManagedFields != nil {
|
||||
extraFields["managedFields"] = o.ManagedFields
|
||||
}
|
||||
if o.OwnerReferences != nil {
|
||||
extraFields["ownerReferences"] = o.OwnerReferences
|
||||
}
|
||||
return resource.CommonMetadata{
|
||||
UID: string(o.UID),
|
||||
ResourceVersion: o.ResourceVersion,
|
||||
Generation: o.Generation,
|
||||
Labels: o.Labels,
|
||||
CreationTimestamp: o.CreationTimestamp.Time,
|
||||
DeletionTimestamp: deletionTimestamp,
|
||||
Finalizers: o.Finalizers,
|
||||
UpdateTimestamp: o.GetUpdateTimestamp(),
|
||||
CreatedBy: o.GetCreatedBy(),
|
||||
UpdatedBy: o.GetUpdatedBy(),
|
||||
ExtraFields: extraFields,
|
||||
}
|
||||
}
|
||||
|
||||
func (o *Channel) SetCommonMetadata(metadata resource.CommonMetadata) {
|
||||
o.UID = types.UID(metadata.UID)
|
||||
o.ResourceVersion = metadata.ResourceVersion
|
||||
o.Generation = metadata.Generation
|
||||
o.Labels = metadata.Labels
|
||||
o.CreationTimestamp = metav1.NewTime(metadata.CreationTimestamp)
|
||||
if metadata.DeletionTimestamp != nil {
|
||||
dt := metav1.NewTime(*metadata.DeletionTimestamp)
|
||||
o.DeletionTimestamp = &dt
|
||||
} else {
|
||||
o.DeletionTimestamp = nil
|
||||
}
|
||||
o.Finalizers = metadata.Finalizers
|
||||
if o.Annotations == nil {
|
||||
o.Annotations = make(map[string]string)
|
||||
}
|
||||
if !metadata.UpdateTimestamp.IsZero() {
|
||||
o.SetUpdateTimestamp(metadata.UpdateTimestamp)
|
||||
}
|
||||
if metadata.CreatedBy != "" {
|
||||
o.SetCreatedBy(metadata.CreatedBy)
|
||||
}
|
||||
if metadata.UpdatedBy != "" {
|
||||
o.SetUpdatedBy(metadata.UpdatedBy)
|
||||
}
|
||||
// Legacy support for setting Annotations, ManagedFields, and OwnerReferences via ExtraFields
|
||||
if metadata.ExtraFields != nil {
|
||||
if annotations, ok := metadata.ExtraFields["annotations"]; ok {
|
||||
if cast, ok := annotations.(map[string]string); ok {
|
||||
o.Annotations = cast
|
||||
}
|
||||
}
|
||||
if managedFields, ok := metadata.ExtraFields["managedFields"]; ok {
|
||||
if cast, ok := managedFields.([]metav1.ManagedFieldsEntry); ok {
|
||||
o.ManagedFields = cast
|
||||
}
|
||||
}
|
||||
if ownerReferences, ok := metadata.ExtraFields["ownerReferences"]; ok {
|
||||
if cast, ok := ownerReferences.([]metav1.OwnerReference); ok {
|
||||
o.OwnerReferences = cast
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (o *Channel) GetCreatedBy() string {
|
||||
if o.ObjectMeta.Annotations == nil {
|
||||
o.ObjectMeta.Annotations = make(map[string]string)
|
||||
}
|
||||
|
||||
return o.ObjectMeta.Annotations["grafana.com/createdBy"]
|
||||
}
|
||||
|
||||
func (o *Channel) SetCreatedBy(createdBy string) {
|
||||
if o.ObjectMeta.Annotations == nil {
|
||||
o.ObjectMeta.Annotations = make(map[string]string)
|
||||
}
|
||||
|
||||
o.ObjectMeta.Annotations["grafana.com/createdBy"] = createdBy
|
||||
}
|
||||
|
||||
func (o *Channel) GetUpdateTimestamp() time.Time {
|
||||
if o.ObjectMeta.Annotations == nil {
|
||||
o.ObjectMeta.Annotations = make(map[string]string)
|
||||
}
|
||||
|
||||
parsed, _ := time.Parse(time.RFC3339, o.ObjectMeta.Annotations["grafana.com/updateTimestamp"])
|
||||
return parsed
|
||||
}
|
||||
|
||||
func (o *Channel) SetUpdateTimestamp(updateTimestamp time.Time) {
|
||||
if o.ObjectMeta.Annotations == nil {
|
||||
o.ObjectMeta.Annotations = make(map[string]string)
|
||||
}
|
||||
|
||||
o.ObjectMeta.Annotations["grafana.com/updateTimestamp"] = updateTimestamp.Format(time.RFC3339)
|
||||
}
|
||||
|
||||
func (o *Channel) GetUpdatedBy() string {
|
||||
if o.ObjectMeta.Annotations == nil {
|
||||
o.ObjectMeta.Annotations = make(map[string]string)
|
||||
}
|
||||
|
||||
return o.ObjectMeta.Annotations["grafana.com/updatedBy"]
|
||||
}
|
||||
|
||||
func (o *Channel) SetUpdatedBy(updatedBy string) {
|
||||
if o.ObjectMeta.Annotations == nil {
|
||||
o.ObjectMeta.Annotations = make(map[string]string)
|
||||
}
|
||||
|
||||
o.ObjectMeta.Annotations["grafana.com/updatedBy"] = updatedBy
|
||||
}
|
||||
|
||||
func (o *Channel) Copy() resource.Object {
|
||||
return resource.CopyObject(o)
|
||||
}
|
||||
|
||||
func (o *Channel) DeepCopyObject() runtime.Object {
|
||||
return o.Copy()
|
||||
}
|
||||
|
||||
func (o *Channel) DeepCopy() *Channel {
|
||||
cpy := &Channel{}
|
||||
o.DeepCopyInto(cpy)
|
||||
return cpy
|
||||
}
|
||||
|
||||
func (o *Channel) DeepCopyInto(dst *Channel) {
|
||||
dst.TypeMeta.APIVersion = o.TypeMeta.APIVersion
|
||||
dst.TypeMeta.Kind = o.TypeMeta.Kind
|
||||
o.ObjectMeta.DeepCopyInto(&dst.ObjectMeta)
|
||||
o.Spec.DeepCopyInto(&dst.Spec)
|
||||
}
|
||||
|
||||
// Interface compliance compile-time check
|
||||
var _ resource.Object = &Channel{}
|
||||
|
||||
// +k8s:openapi-gen=true
|
||||
type ChannelList struct {
|
||||
metav1.TypeMeta `json:",inline" yaml:",inline"`
|
||||
metav1.ListMeta `json:"metadata" yaml:"metadata"`
|
||||
Items []Channel `json:"items" yaml:"items"`
|
||||
}
|
||||
|
||||
func (o *ChannelList) DeepCopyObject() runtime.Object {
|
||||
return o.Copy()
|
||||
}
|
||||
|
||||
func (o *ChannelList) Copy() resource.ListObject {
|
||||
cpy := &ChannelList{
|
||||
TypeMeta: o.TypeMeta,
|
||||
Items: make([]Channel, len(o.Items)),
|
||||
}
|
||||
o.ListMeta.DeepCopyInto(&cpy.ListMeta)
|
||||
for i := 0; i < len(o.Items); i++ {
|
||||
if item, ok := o.Items[i].Copy().(*Channel); ok {
|
||||
cpy.Items[i] = *item
|
||||
}
|
||||
}
|
||||
return cpy
|
||||
}
|
||||
|
||||
func (o *ChannelList) GetItems() []resource.Object {
|
||||
items := make([]resource.Object, len(o.Items))
|
||||
for i := 0; i < len(o.Items); i++ {
|
||||
items[i] = &o.Items[i]
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
func (o *ChannelList) SetItems(items []resource.Object) {
|
||||
o.Items = make([]Channel, len(items))
|
||||
for i := 0; i < len(items); i++ {
|
||||
o.Items[i] = *items[i].(*Channel)
|
||||
}
|
||||
}
|
||||
|
||||
func (o *ChannelList) DeepCopy() *ChannelList {
|
||||
cpy := &ChannelList{}
|
||||
o.DeepCopyInto(cpy)
|
||||
return cpy
|
||||
}
|
||||
|
||||
func (o *ChannelList) DeepCopyInto(dst *ChannelList) {
|
||||
resource.CopyObjectInto(dst, o)
|
||||
}
|
||||
|
||||
// Interface compliance compile-time check
|
||||
var _ resource.ListObject = &ChannelList{}
|
||||
|
||||
// Copy methods for all subresource types
|
||||
|
||||
// DeepCopy creates a full deep copy of Spec
|
||||
func (s *ChannelSpec) DeepCopy() *ChannelSpec {
|
||||
cpy := &ChannelSpec{}
|
||||
s.DeepCopyInto(cpy)
|
||||
return cpy
|
||||
}
|
||||
|
||||
// DeepCopyInto deep copies Spec into another Spec object
|
||||
func (s *ChannelSpec) DeepCopyInto(dst *ChannelSpec) {
|
||||
resource.CopyObjectInto(dst, s)
|
||||
}
|
||||
34
apps/live/pkg/apis/live/v1beta1/channel_schema_gen.go
generated
Normal file
34
apps/live/pkg/apis/live/v1beta1/channel_schema_gen.go
generated
Normal file
@@ -0,0 +1,34 @@
|
||||
//
|
||||
// Code generated by grafana-app-sdk. DO NOT EDIT.
|
||||
//
|
||||
|
||||
package v1beta1
|
||||
|
||||
import (
|
||||
"github.com/grafana/grafana-app-sdk/resource"
|
||||
)
|
||||
|
||||
// schema is unexported to prevent accidental overwrites
|
||||
var (
|
||||
schemaChannel = resource.NewSimpleSchema("live.grafana.app", "v1beta1", NewChannel(), &ChannelList{}, resource.WithKind("Channel"),
|
||||
resource.WithPlural("channels"), resource.WithScope(resource.NamespacedScope))
|
||||
kindChannel = resource.Kind{
|
||||
Schema: schemaChannel,
|
||||
Codecs: map[resource.KindEncoding]resource.Codec{
|
||||
resource.KindEncodingJSON: &ChannelJSONCodec{},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
// Kind returns a resource.Kind for this Schema with a JSON codec
|
||||
func ChannelKind() resource.Kind {
|
||||
return kindChannel
|
||||
}
|
||||
|
||||
// Schema returns a resource.SimpleSchema representation of Channel
|
||||
func ChannelSchema() *resource.SimpleSchema {
|
||||
return schemaChannel
|
||||
}
|
||||
|
||||
// Interface compliance checks
|
||||
var _ resource.Schema = kindChannel
|
||||
14
apps/live/pkg/apis/live/v1beta1/channel_spec_gen.go
generated
Normal file
14
apps/live/pkg/apis/live/v1beta1/channel_spec_gen.go
generated
Normal file
@@ -0,0 +1,14 @@
|
||||
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
|
||||
|
||||
package v1beta1
|
||||
|
||||
// +k8s:openapi-gen=true
|
||||
type ChannelSpec struct {
|
||||
Title string `json:"title"`
|
||||
Description *string `json:"description,omitempty"`
|
||||
}
|
||||
|
||||
// NewChannelSpec creates a new ChannelSpec object.
|
||||
func NewChannelSpec() *ChannelSpec {
|
||||
return &ChannelSpec{}
|
||||
}
|
||||
18
apps/live/pkg/apis/live/v1beta1/constants.go
Normal file
18
apps/live/pkg/apis/live/v1beta1/constants.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package v1beta1
|
||||
|
||||
import "k8s.io/apimachinery/pkg/runtime/schema"
|
||||
|
||||
const (
|
||||
// APIGroup is the API group used by all kinds in this package
|
||||
APIGroup = "live.grafana.app"
|
||||
// APIVersion is the API version used by all kinds in this package
|
||||
APIVersion = "v1beta1"
|
||||
)
|
||||
|
||||
var (
|
||||
// GroupVersion is a schema.GroupVersion consisting of the Group and Version constants for this package
|
||||
GroupVersion = schema.GroupVersion{
|
||||
Group: APIGroup,
|
||||
Version: APIVersion,
|
||||
}
|
||||
)
|
||||
116
apps/live/pkg/apis/manifestdata/live_manifest.go
Normal file
116
apps/live/pkg/apis/manifestdata/live_manifest.go
Normal file
@@ -0,0 +1,116 @@
|
||||
//
|
||||
// This file is generated by grafana-app-sdk
|
||||
// DO NOT EDIT
|
||||
//
|
||||
|
||||
package manifestdata
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/app"
|
||||
"github.com/grafana/grafana-app-sdk/resource"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/kube-openapi/pkg/spec3"
|
||||
"k8s.io/kube-openapi/pkg/validation/spec"
|
||||
|
||||
v1beta1 "github.com/grafana/grafana/apps/live/pkg/apis/live/v1beta1"
|
||||
)
|
||||
|
||||
var appManifestData = app.ManifestData{
|
||||
AppName: "live",
|
||||
Group: "live.grafana.app",
|
||||
PreferredVersion: "v1beta1",
|
||||
Versions: []app.ManifestVersion{
|
||||
{
|
||||
Name: "v1beta1",
|
||||
Served: true,
|
||||
Kinds: []app.ManifestVersionKind{
|
||||
{
|
||||
Kind: "Channel",
|
||||
Plural: "Channels",
|
||||
Scope: "Namespaced",
|
||||
Conversion: false,
|
||||
},
|
||||
},
|
||||
Routes: app.ManifestVersionRoutes{
|
||||
Namespaced: map[string]spec3.PathProps{},
|
||||
Cluster: map[string]spec3.PathProps{},
|
||||
Schemas: map[string]spec.Schema{},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func LocalManifest() app.Manifest {
|
||||
return app.NewEmbeddedManifest(appManifestData)
|
||||
}
|
||||
|
||||
func RemoteManifest() app.Manifest {
|
||||
return app.NewAPIServerManifest("live")
|
||||
}
|
||||
|
||||
var kindVersionToGoType = map[string]resource.Kind{
|
||||
"Channel/v1beta1": v1beta1.ChannelKind(),
|
||||
}
|
||||
|
||||
// ManifestGoTypeAssociator returns the associated resource.Kind instance for a given Kind and Version, if one exists.
|
||||
// If there is no association for the provided Kind and Version, exists will return false.
|
||||
func ManifestGoTypeAssociator(kind, version string) (goType resource.Kind, exists bool) {
|
||||
goType, exists = kindVersionToGoType[fmt.Sprintf("%s/%s", kind, version)]
|
||||
return goType, exists
|
||||
}
|
||||
|
||||
var customRouteToGoResponseType = map[string]any{}
|
||||
|
||||
// ManifestCustomRouteResponsesAssociator returns the associated response go type for a given kind, version, custom route path, and method, if one exists.
|
||||
// kind may be empty for custom routes which are not kind subroutes. Leading slashes are removed from subroute paths.
|
||||
// If there is no association for the provided kind, version, custom route path, and method, exists will return false.
|
||||
// Resource routes (those without a kind) should prefix their route with "<namespace>/" if the route is namespaced (otherwise the route is assumed to be cluster-scope)
|
||||
func ManifestCustomRouteResponsesAssociator(kind, version, path, verb string) (goType any, exists bool) {
|
||||
if len(path) > 0 && path[0] == '/' {
|
||||
path = path[1:]
|
||||
}
|
||||
goType, exists = customRouteToGoResponseType[fmt.Sprintf("%s|%s|%s|%s", version, kind, path, strings.ToUpper(verb))]
|
||||
return goType, exists
|
||||
}
|
||||
|
||||
var customRouteToGoParamsType = map[string]runtime.Object{}
|
||||
|
||||
func ManifestCustomRouteQueryAssociator(kind, version, path, verb string) (goType runtime.Object, exists bool) {
|
||||
if len(path) > 0 && path[0] == '/' {
|
||||
path = path[1:]
|
||||
}
|
||||
goType, exists = customRouteToGoParamsType[fmt.Sprintf("%s|%s|%s|%s", version, kind, path, strings.ToUpper(verb))]
|
||||
return goType, exists
|
||||
}
|
||||
|
||||
var customRouteToGoRequestBodyType = map[string]any{}
|
||||
|
||||
func ManifestCustomRouteRequestBodyAssociator(kind, version, path, verb string) (goType any, exists bool) {
|
||||
if len(path) > 0 && path[0] == '/' {
|
||||
path = path[1:]
|
||||
}
|
||||
goType, exists = customRouteToGoRequestBodyType[fmt.Sprintf("%s|%s|%s|%s", version, kind, path, strings.ToUpper(verb))]
|
||||
return goType, exists
|
||||
}
|
||||
|
||||
type GoTypeAssociator struct{}
|
||||
|
||||
func NewGoTypeAssociator() *GoTypeAssociator {
|
||||
return &GoTypeAssociator{}
|
||||
}
|
||||
|
||||
func (g *GoTypeAssociator) KindToGoType(kind, version string) (goType resource.Kind, exists bool) {
|
||||
return ManifestGoTypeAssociator(kind, version)
|
||||
}
|
||||
func (g *GoTypeAssociator) CustomRouteReturnGoType(kind, version, path, verb string) (goType any, exists bool) {
|
||||
return ManifestCustomRouteResponsesAssociator(kind, version, path, verb)
|
||||
}
|
||||
func (g *GoTypeAssociator) CustomRouteQueryGoType(kind, version, path, verb string) (goType runtime.Object, exists bool) {
|
||||
return ManifestCustomRouteQueryAssociator(kind, version, path, verb)
|
||||
}
|
||||
func (g *GoTypeAssociator) CustomRouteRequestBodyGoType(kind, version, path, verb string) (goType any, exists bool) {
|
||||
return ManifestCustomRouteRequestBodyAssociator(kind, version, path, verb)
|
||||
}
|
||||
1
go.mod
1
go.mod
@@ -279,6 +279,7 @@ replace (
|
||||
github.com/grafana/grafana/apps/folder => ./apps/folder
|
||||
github.com/grafana/grafana/apps/iam => ./apps/iam
|
||||
github.com/grafana/grafana/apps/investigations => ./apps/investigations
|
||||
github.com/grafana/grafana/apps/live => ./apps/live
|
||||
github.com/grafana/grafana/apps/logsdrilldown => ./apps/logsdrilldown
|
||||
github.com/grafana/grafana/apps/playlist => ./apps/playlist
|
||||
github.com/grafana/grafana/apps/plugins => ./apps/plugins
|
||||
|
||||
@@ -4,24 +4,11 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apiserver/pkg/registry/generic"
|
||||
"k8s.io/apiserver/pkg/registry/generic/registry"
|
||||
"k8s.io/apiserver/pkg/storage"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
)
|
||||
|
||||
type registryStoreOptions struct {
|
||||
attrFunc storage.AttrFunc
|
||||
}
|
||||
|
||||
type OptionFn func(*registryStoreOptions)
|
||||
|
||||
func WithAttrFunc(attrFunc storage.AttrFunc) OptionFn {
|
||||
return func(opts *registryStoreOptions) {
|
||||
opts.attrFunc = attrFunc
|
||||
}
|
||||
}
|
||||
|
||||
func NewRegistryStore(scheme *runtime.Scheme, resourceInfo utils.ResourceInfo, optsGetter generic.RESTOptionsGetter, options ...OptionFn) (*registry.Store, error) {
|
||||
func NewRegistryStore(scheme *runtime.Scheme, resourceInfo utils.ResourceInfo, optsGetter generic.RESTOptionsGetter) (*registry.Store, error) {
|
||||
gv := resourceInfo.GroupVersion()
|
||||
gv.Version = runtime.APIVersionInternal
|
||||
strategy := NewStrategy(scheme, gv)
|
||||
@@ -33,7 +20,7 @@ func NewRegistryStore(scheme *runtime.Scheme, resourceInfo utils.ResourceInfo, o
|
||||
NewListFunc: resourceInfo.NewListFunc,
|
||||
KeyRootFunc: KeyRootFunc(resourceInfo.GroupResource()),
|
||||
KeyFunc: NamespaceKeyFunc(resourceInfo.GroupResource()),
|
||||
//PredicateFunc: Matcher,
|
||||
PredicateFunc: Matcher,
|
||||
DefaultQualifiedResource: resourceInfo.GroupResource(),
|
||||
SingularQualifiedResource: resourceInfo.SingularGroupResource(),
|
||||
TableConvertor: resourceInfo.TableConverter(),
|
||||
@@ -41,16 +28,8 @@ func NewRegistryStore(scheme *runtime.Scheme, resourceInfo utils.ResourceInfo, o
|
||||
UpdateStrategy: strategy,
|
||||
DeleteStrategy: strategy,
|
||||
}
|
||||
|
||||
opts := ®istryStoreOptions{
|
||||
attrFunc: GetAttrs,
|
||||
}
|
||||
for _, opt := range options {
|
||||
opt(opts)
|
||||
}
|
||||
|
||||
o := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: opts.attrFunc}
|
||||
if err := store.CompleteWithOptions(o); err != nil {
|
||||
options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: GetAttrs}
|
||||
if err := store.CompleteWithOptions(options); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return store, nil
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/registry/apis/datasource"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/folders"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/iam"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/live"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/ofrep"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/preferences"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/provisioning"
|
||||
@@ -29,6 +30,7 @@ func ProvideRegistryServiceSink(
|
||||
_ *collections.APIBuilder,
|
||||
_ *provisioning.APIBuilder,
|
||||
_ *ofrep.APIBuilder,
|
||||
_ *live.APIBuilder,
|
||||
_ *secret.DependencyRegisterer,
|
||||
_ *provisioning.DependencyRegisterer,
|
||||
) *Service {
|
||||
|
||||
@@ -8,8 +8,6 @@ import (
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/fields"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apiserver/pkg/admission"
|
||||
@@ -24,8 +22,6 @@ import (
|
||||
authlib "github.com/grafana/authlib/types"
|
||||
"github.com/grafana/grafana-app-sdk/logging"
|
||||
|
||||
sdkres "github.com/grafana/grafana-app-sdk/resource"
|
||||
|
||||
folders "github.com/grafana/grafana/apps/folder/pkg/apis/folder/v1beta1"
|
||||
"github.com/grafana/grafana/apps/iam/pkg/reconcilers"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
@@ -133,29 +129,6 @@ func (b *FolderAPIBuilder) InstallSchema(scheme *runtime.Scheme) error {
|
||||
Version: runtime.APIVersionInternal,
|
||||
})
|
||||
|
||||
kinds := []sdkres.Kind{folders.FolderKind()}
|
||||
for _, kind := range kinds {
|
||||
gvk := gv.WithKind(kind.Kind())
|
||||
err := scheme.AddFieldLabelConversionFunc(
|
||||
gvk,
|
||||
func(label, value string) (string, string, error) {
|
||||
if label == "metadata.name" || label == "metadata.namespace" {
|
||||
return label, value, nil
|
||||
}
|
||||
fields := kind.SelectableFields()
|
||||
for _, field := range fields {
|
||||
if field.FieldSelector == label {
|
||||
return label, value, nil
|
||||
}
|
||||
}
|
||||
return "", "", fmt.Errorf("field label not supported for %s: %s", gvk, label)
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// If multiple versions exist, then register conversions from zz_generated.conversion.go
|
||||
// if err := playlist.RegisterConversions(scheme); err != nil {
|
||||
// return err
|
||||
@@ -164,26 +137,6 @@ func (b *FolderAPIBuilder) InstallSchema(scheme *runtime.Scheme) error {
|
||||
return scheme.SetVersionPriority(gv)
|
||||
}
|
||||
|
||||
// TODO: work with all kinds from schema, not just one.
|
||||
func (b *FolderAPIBuilder) BuildGetAttrsFn(k sdkres.Kind) func(obj runtime.Object) (labels.Set, fields.Set, error) {
|
||||
return func(obj runtime.Object) (labels.Set, fields.Set, error) {
|
||||
if robj, ok := obj.(sdkres.Object); !ok {
|
||||
return nil, nil, fmt.Errorf("not a resource.Object")
|
||||
} else {
|
||||
fieldsSet := fields.Set{}
|
||||
|
||||
for _, f := range k.SelectableFields() {
|
||||
v, err := f.FieldValueFunc(robj)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
fieldsSet[f.FieldSelector] = v
|
||||
}
|
||||
return robj.GetLabels(), fieldsSet, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b *FolderAPIBuilder) AllowedV0Alpha1Resources() []string {
|
||||
return nil
|
||||
}
|
||||
@@ -195,11 +148,10 @@ func (b *FolderAPIBuilder) UpdateAPIGroupInfo(apiGroupInfo *genericapiserver.API
|
||||
Permissions: b.setDefaultFolderPermissions,
|
||||
})
|
||||
|
||||
unified, err := grafanaregistry.NewRegistryStore(opts.Scheme, resourceInfo, opts.OptsGetter, grafanaregistry.WithAttrFunc(b.BuildGetAttrsFn(folders.FolderKind())))
|
||||
unified, err := grafanaregistry.NewRegistryStore(opts.Scheme, resourceInfo, opts.OptsGetter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
b.registerPermissionHooks(unified)
|
||||
b.storage = unified
|
||||
|
||||
|
||||
86
pkg/registry/apis/live/register.go
Normal file
86
pkg/registry/apis/live/register.go
Normal file
@@ -0,0 +1,86 @@
|
||||
package live
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
genericapiserver "k8s.io/apiserver/pkg/server"
|
||||
"k8s.io/kube-openapi/pkg/common"
|
||||
|
||||
authlib "github.com/grafana/authlib/types"
|
||||
preferences "github.com/grafana/grafana/apps/preferences/pkg/apis/preferences/v1alpha1"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/builder"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/live"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
var (
|
||||
_ builder.APIGroupBuilder = (*APIBuilder)(nil)
|
||||
)
|
||||
|
||||
type APIBuilder struct {
|
||||
namespacer authlib.NamespaceFormatter
|
||||
live *live.GrafanaLive
|
||||
}
|
||||
|
||||
func RegisterAPIService(
|
||||
cfg *setting.Cfg,
|
||||
features featuremgmt.FeatureToggles,
|
||||
live *live.GrafanaLive,
|
||||
apiregistration builder.APIRegistrar,
|
||||
) *APIBuilder {
|
||||
// Requires development settings and clearly experimental
|
||||
//nolint:staticcheck // not yet migrated to OpenFeature
|
||||
if !features.IsEnabledGlobally(featuremgmt.FlagGrafanaAPIServerWithExperimentalAPIs) {
|
||||
return nil
|
||||
}
|
||||
|
||||
builder := &APIBuilder{
|
||||
live: live,
|
||||
namespacer: request.GetNamespaceMapper(cfg),
|
||||
}
|
||||
apiregistration.RegisterAPI(builder)
|
||||
return builder
|
||||
}
|
||||
|
||||
// AllowedV0Alpha1Resources implements builder.APIGroupBuilder.
|
||||
func (b *APIBuilder) AllowedV0Alpha1Resources() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *APIBuilder) GetGroupVersion() schema.GroupVersion {
|
||||
return preferences.GroupVersion
|
||||
}
|
||||
|
||||
func (b *APIBuilder) InstallSchema(scheme *runtime.Scheme) error {
|
||||
gv := preferences.GroupVersion
|
||||
err := preferences.AddToScheme(scheme)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
metav1.AddToGroupVersion(scheme, gv)
|
||||
return scheme.SetVersionPriority(gv)
|
||||
}
|
||||
|
||||
func (b *APIBuilder) UpdateAPIGroupInfo(apiGroupInfo *genericapiserver.APIGroupInfo, opts builder.APIGroupOptions) error {
|
||||
storage := map[string]rest.Storage{}
|
||||
|
||||
// prefs := preferences.PreferencesResourceInfo
|
||||
// storage[prefs.StoragePath()] = b.legacyPrefs
|
||||
|
||||
apiGroupInfo.VersionedResourcesStorageMap[preferences.APIVersion] = storage
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *APIBuilder) GetAuthorizer() authorizer.Authorizer {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *APIBuilder) GetOpenAPIDefinitions() common.GetOpenAPIDefinitions {
|
||||
return preferences.GetOpenAPIDefinitions
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/registry/apis/iam"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/iam/externalgroupmapping"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/iam/noopstorage"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/live"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/ofrep"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/preferences"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/provisioning"
|
||||
@@ -68,5 +69,6 @@ var WireSet = wire.NewSet(
|
||||
preferences.RegisterAPIService,
|
||||
collections.RegisterAPIService,
|
||||
userstorage.RegisterAPIService,
|
||||
live.RegisterAPIService,
|
||||
ofrep.RegisterAPIService,
|
||||
)
|
||||
|
||||
@@ -13,9 +13,6 @@ import (
|
||||
"github.com/grafana/dskit/kv"
|
||||
"github.com/grafana/dskit/ring"
|
||||
ringclient "github.com/grafana/dskit/ring/client"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/options"
|
||||
"github.com/grafana/grafana/pkg/storage/unified"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
@@ -79,22 +76,6 @@ func newModuleServer(opts Options,
|
||||
) (*ModuleServer, error) {
|
||||
rootCtx, shutdownFn := context.WithCancel(context.Background())
|
||||
|
||||
// TODO should inject this with Wire
|
||||
apiserverCfg := cfg.SectionWithEnvOverrides("grafana-apiserver")
|
||||
searchServerAddress := apiserverCfg.Key("search_server_address").MustString("")
|
||||
var searchClient resourcepb.ResourceIndexClient
|
||||
var err error
|
||||
if searchServerAddress != "" {
|
||||
storageOptions := options.StorageOptions{
|
||||
SearchServerAddress: searchServerAddress,
|
||||
}
|
||||
searchClient, err = unified.NewSearchClient(storageOptions, features)
|
||||
if err != nil {
|
||||
shutdownFn()
|
||||
return nil, fmt.Errorf("failed to create search client: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
s := &ModuleServer{
|
||||
opts: opts,
|
||||
apiOpts: apiOpts,
|
||||
@@ -115,7 +96,6 @@ func newModuleServer(opts Options,
|
||||
license: license,
|
||||
moduleRegisterer: moduleRegisterer,
|
||||
storageBackend: storageBackend,
|
||||
searchClient: searchClient,
|
||||
hooksService: hooksService,
|
||||
}
|
||||
|
||||
@@ -139,7 +119,6 @@ type ModuleServer struct {
|
||||
isInitialized bool
|
||||
mtx sync.Mutex
|
||||
storageBackend resource.StorageBackend
|
||||
searchClient resourcepb.ResourceIndexClient
|
||||
storageMetrics *resource.StorageMetrics
|
||||
indexMetrics *resource.BleveIndexMetrics
|
||||
license licensing.Licensing
|
||||
@@ -223,7 +202,7 @@ func (s *ModuleServer) Run() error {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return sql.ProvideUnifiedStorageGrpcService(s.cfg, s.features, nil, s.log, s.registerer, docBuilders, s.storageMetrics, s.indexMetrics, s.searchServerRing, s.MemberlistKVConfig, s.httpServerRouter, s.storageBackend, s.searchClient)
|
||||
return sql.ProvideUnifiedStorageGrpcService(s.cfg, s.features, nil, s.log, s.registerer, docBuilders, s.storageMetrics, s.indexMetrics, s.searchServerRing, s.MemberlistKVConfig, s.httpServerRouter, s.storageBackend)
|
||||
})
|
||||
|
||||
m.RegisterModule(modules.ZanzanaServer, func() (services.Service, error) {
|
||||
|
||||
7
pkg/server/wire_gen.go
generated
7
pkg/server/wire_gen.go
generated
@@ -56,6 +56,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/registry/apis/iam"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/iam/externalgroupmapping"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/iam/noopstorage"
|
||||
live2 "github.com/grafana/grafana/pkg/registry/apis/live"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/ofrep"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/preferences"
|
||||
provisioning2 "github.com/grafana/grafana/pkg/registry/apis/provisioning"
|
||||
@@ -919,11 +920,12 @@ func Initialize(ctx context.Context, cfg *setting.Cfg, opts Options, apiOpts api
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
liveAPIBuilder := live2.RegisterAPIService(cfg, featureToggles, grafanaLive, apiserverService)
|
||||
provisioningDependencyRegisterer, err := provisioning2.RegisterDependencies(cfg, acimplService, featureToggles)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
apiregistryService := apiregistry.ProvideRegistryServiceSink(dashboardsAPIBuilder, dataSourceAPIBuilder, folderAPIBuilder, identityAccessManagementAPIBuilder, queryAPIBuilder, userStorageAPIBuilder, apiBuilder, collectionsAPIBuilder, provisioningAPIBuilder, ofrepAPIBuilder, dependencyRegisterer, provisioningDependencyRegisterer)
|
||||
apiregistryService := apiregistry.ProvideRegistryServiceSink(dashboardsAPIBuilder, dataSourceAPIBuilder, folderAPIBuilder, identityAccessManagementAPIBuilder, queryAPIBuilder, userStorageAPIBuilder, apiBuilder, collectionsAPIBuilder, provisioningAPIBuilder, ofrepAPIBuilder, liveAPIBuilder, dependencyRegisterer, provisioningDependencyRegisterer)
|
||||
teamPermissionsService, err := ossaccesscontrol.ProvideTeamPermissions(cfg, featureToggles, routeRegisterImpl, sqlStore, accessControl, ossLicensingService, acimplService, teamService, userService, actionSetService)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -1577,11 +1579,12 @@ func InitializeForTest(ctx context.Context, t sqlutil.ITestDB, testingT interfac
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
liveAPIBuilder := live2.RegisterAPIService(cfg, featureToggles, grafanaLive, apiserverService)
|
||||
provisioningDependencyRegisterer, err := provisioning2.RegisterDependencies(cfg, acimplService, featureToggles)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
apiregistryService := apiregistry.ProvideRegistryServiceSink(dashboardsAPIBuilder, dataSourceAPIBuilder, folderAPIBuilder, identityAccessManagementAPIBuilder, queryAPIBuilder, userStorageAPIBuilder, apiBuilder, collectionsAPIBuilder, provisioningAPIBuilder, ofrepAPIBuilder, dependencyRegisterer, provisioningDependencyRegisterer)
|
||||
apiregistryService := apiregistry.ProvideRegistryServiceSink(dashboardsAPIBuilder, dataSourceAPIBuilder, folderAPIBuilder, identityAccessManagementAPIBuilder, queryAPIBuilder, userStorageAPIBuilder, apiBuilder, collectionsAPIBuilder, provisioningAPIBuilder, ofrepAPIBuilder, liveAPIBuilder, dependencyRegisterer, provisioningDependencyRegisterer)
|
||||
teamPermissionsService, err := ossaccesscontrol.ProvideTeamPermissions(cfg, featureToggles, routeRegisterImpl, sqlStore, accessControl, ossLicensingService, acimplService, teamService, userService, actionSetService)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -130,7 +130,7 @@ func NewSchedulerMetrics(r prometheus.Registerer) *Scheduler {
|
||||
Name: "rule_group_rules",
|
||||
Help: "The number of alert rules that are scheduled, by type and state.",
|
||||
},
|
||||
[]string{"org", "type", "state", "rule_group", "folder_uid"},
|
||||
[]string{"org", "type", "state", "rule_group"},
|
||||
),
|
||||
Groups: promauto.With(r).NewGaugeVec(
|
||||
prometheus.GaugeOpts{
|
||||
|
||||
@@ -117,7 +117,7 @@ func (sch *schedule) updateRulesMetrics(alertRules []*models.AlertRule) {
|
||||
|
||||
// Set metrics
|
||||
for key, count := range buckets {
|
||||
sch.metrics.GroupRules.WithLabelValues(fmt.Sprint(key.orgID), key.ruleType.String(), key.state, makeRuleGroupLabelValue(key.ruleGroup), key.ruleGroup.NamespaceUID).Set(float64(count))
|
||||
sch.metrics.GroupRules.WithLabelValues(fmt.Sprint(key.orgID), key.ruleType.String(), key.state, makeRuleGroupLabelValue(key.ruleGroup)).Set(float64(count))
|
||||
}
|
||||
for orgID, numRulesNfSettings := range orgsNfSettings {
|
||||
sch.metrics.SimpleNotificationRules.WithLabelValues(fmt.Sprint(orgID)).Set(float64(numRulesNfSettings))
|
||||
|
||||
@@ -157,8 +157,8 @@ func TestProcessTicks(t *testing.T) {
|
||||
expectedMetric := fmt.Sprintf(
|
||||
`# HELP grafana_alerting_rule_group_rules The number of alert rules that are scheduled, by type and state.
|
||||
# TYPE grafana_alerting_rule_group_rules gauge
|
||||
grafana_alerting_rule_group_rules{folder_uid="%[3]s",org="%[1]d",rule_group="%[2]s",state="active",type="alerting"} 1
|
||||
`, alertRule1.OrgID, folderWithRuleGroup1, alertRule1.NamespaceUID)
|
||||
grafana_alerting_rule_group_rules{org="%[1]d",rule_group="%[2]s",state="active",type="alerting"} 1
|
||||
`, alertRule1.OrgID, folderWithRuleGroup1)
|
||||
|
||||
err := testutil.GatherAndCompare(reg, bytes.NewBufferString(expectedMetric), "grafana_alerting_rule_group_rules")
|
||||
require.NoError(t, err)
|
||||
@@ -199,9 +199,9 @@ func TestProcessTicks(t *testing.T) {
|
||||
expectedMetric := fmt.Sprintf(
|
||||
`# HELP grafana_alerting_rule_group_rules The number of alert rules that are scheduled, by type and state.
|
||||
# TYPE grafana_alerting_rule_group_rules gauge
|
||||
grafana_alerting_rule_group_rules{folder_uid="%[4]s",org="%[1]d",rule_group="%[2]s",state="active",type="alerting"} 1
|
||||
grafana_alerting_rule_group_rules{folder_uid="%[5]s",org="%[1]d",rule_group="%[3]s",state="active",type="alerting"} 1
|
||||
`, alertRule1.OrgID, folderWithRuleGroup1, folderWithRuleGroup2, alertRule1.NamespaceUID, alertRule2.NamespaceUID)
|
||||
grafana_alerting_rule_group_rules{org="%[1]d",rule_group="%[2]s",state="active",type="alerting"} 1
|
||||
grafana_alerting_rule_group_rules{org="%[1]d",rule_group="%[3]s",state="active",type="alerting"} 1
|
||||
`, alertRule1.OrgID, folderWithRuleGroup1, folderWithRuleGroup2)
|
||||
|
||||
err := testutil.GatherAndCompare(reg, bytes.NewBufferString(expectedMetric), "grafana_alerting_rule_group_rules")
|
||||
require.NoError(t, err)
|
||||
@@ -265,9 +265,9 @@ func TestProcessTicks(t *testing.T) {
|
||||
expectedMetric := fmt.Sprintf(
|
||||
`# HELP grafana_alerting_rule_group_rules The number of alert rules that are scheduled, by type and state.
|
||||
# TYPE grafana_alerting_rule_group_rules gauge
|
||||
grafana_alerting_rule_group_rules{folder_uid="%[4]s",org="%[1]d",rule_group="%[2]s",state="paused",type="alerting"} 1
|
||||
grafana_alerting_rule_group_rules{folder_uid="%[5]s",org="%[1]d",rule_group="%[3]s",state="active",type="alerting"} 1
|
||||
`, alertRule1.OrgID, folderWithRuleGroup1, folderWithRuleGroup2, alertRule1.NamespaceUID, alertRule2.NamespaceUID)
|
||||
grafana_alerting_rule_group_rules{org="%[1]d",rule_group="%[2]s",state="paused",type="alerting"} 1
|
||||
grafana_alerting_rule_group_rules{org="%[1]d",rule_group="%[3]s",state="active",type="alerting"} 1
|
||||
`, alertRule1.OrgID, folderWithRuleGroup1, folderWithRuleGroup2)
|
||||
|
||||
err := testutil.GatherAndCompare(reg, bytes.NewBufferString(expectedMetric), "grafana_alerting_rule_group_rules")
|
||||
require.NoError(t, err)
|
||||
@@ -308,10 +308,10 @@ func TestProcessTicks(t *testing.T) {
|
||||
expectedMetric := fmt.Sprintf(
|
||||
`# HELP grafana_alerting_rule_group_rules The number of alert rules that are scheduled, by type and state.
|
||||
# TYPE grafana_alerting_rule_group_rules gauge
|
||||
grafana_alerting_rule_group_rules{folder_uid="%[4]s",org="%[1]d",rule_group="%[2]s",state="paused",type="alerting"} 1
|
||||
grafana_alerting_rule_group_rules{folder_uid="%[5]s",org="%[1]d",rule_group="%[3]s",state="paused",type="alerting"} 1
|
||||
grafana_alerting_rule_group_rules{org="%[1]d",rule_group="%[2]s",state="paused",type="alerting"} 1
|
||||
grafana_alerting_rule_group_rules{org="%[1]d",rule_group="%[3]s",state="paused",type="alerting"} 1
|
||||
|
||||
`, alertRule1.OrgID, folderWithRuleGroup1, folderWithRuleGroup2, alertRule1.NamespaceUID, alertRule2.NamespaceUID)
|
||||
`, alertRule1.OrgID, folderWithRuleGroup1, folderWithRuleGroup2)
|
||||
err := testutil.GatherAndCompare(reg, bytes.NewBufferString(expectedMetric), "grafana_alerting_rule_group_rules")
|
||||
require.NoError(t, err)
|
||||
})
|
||||
@@ -336,9 +336,9 @@ func TestProcessTicks(t *testing.T) {
|
||||
expectedMetric := fmt.Sprintf(
|
||||
`# HELP grafana_alerting_rule_group_rules The number of alert rules that are scheduled, by type and state.
|
||||
# TYPE grafana_alerting_rule_group_rules gauge
|
||||
grafana_alerting_rule_group_rules{folder_uid="%[4]s",org="%[1]d",rule_group="%[2]s",state="active",type="alerting"} 1
|
||||
grafana_alerting_rule_group_rules{folder_uid="%[5]s",org="%[1]d",rule_group="%[3]s",state="active",type="alerting"} 1
|
||||
`, alertRule1.OrgID, folderWithRuleGroup1, folderWithRuleGroup2, alertRule1.NamespaceUID, alertRule2.NamespaceUID)
|
||||
grafana_alerting_rule_group_rules{org="%[1]d",rule_group="%[2]s",state="active",type="alerting"} 1
|
||||
grafana_alerting_rule_group_rules{org="%[1]d",rule_group="%[3]s",state="active",type="alerting"} 1
|
||||
`, alertRule1.OrgID, folderWithRuleGroup1, folderWithRuleGroup2)
|
||||
|
||||
err := testutil.GatherAndCompare(reg, bytes.NewBufferString(expectedMetric), "grafana_alerting_rule_group_rules")
|
||||
require.NoError(t, err)
|
||||
@@ -363,8 +363,8 @@ func TestProcessTicks(t *testing.T) {
|
||||
expectedMetric := fmt.Sprintf(
|
||||
`# HELP grafana_alerting_rule_group_rules The number of alert rules that are scheduled, by type and state.
|
||||
# TYPE grafana_alerting_rule_group_rules gauge
|
||||
grafana_alerting_rule_group_rules{folder_uid="%[3]s",org="%[1]d",rule_group="%[2]s",state="active",type="alerting"} 1
|
||||
`, alertRule1.OrgID, folderWithRuleGroup2, alertRule2.NamespaceUID)
|
||||
grafana_alerting_rule_group_rules{org="%[1]d",rule_group="%[2]s",state="active",type="alerting"} 1
|
||||
`, alertRule1.OrgID, folderWithRuleGroup2)
|
||||
err := testutil.GatherAndCompare(reg, bytes.NewBufferString(expectedMetric), "grafana_alerting_rule_group_rules")
|
||||
require.NoError(t, err)
|
||||
})
|
||||
@@ -684,8 +684,8 @@ func TestSchedule_updateRulesMetrics(t *testing.T) {
|
||||
expectedMetric := fmt.Sprintf(
|
||||
`# HELP grafana_alerting_rule_group_rules The number of alert rules that are scheduled, by type and state.
|
||||
# TYPE grafana_alerting_rule_group_rules gauge
|
||||
grafana_alerting_rule_group_rules{folder_uid="%[3]s",org="%[1]d",rule_group="%[2]s",state="active",type="alerting"} 1
|
||||
`, alertRule1.OrgID, folderWithRuleGroup1, alertRule1.NamespaceUID)
|
||||
grafana_alerting_rule_group_rules{org="%[1]d",rule_group="%[2]s",state="active", type="alerting"} 1
|
||||
`, alertRule1.OrgID, folderWithRuleGroup1)
|
||||
|
||||
err := testutil.GatherAndCompare(reg, bytes.NewBufferString(expectedMetric), "grafana_alerting_rule_group_rules")
|
||||
require.NoError(t, err)
|
||||
@@ -705,9 +705,9 @@ func TestSchedule_updateRulesMetrics(t *testing.T) {
|
||||
expectedMetric := fmt.Sprintf(
|
||||
`# HELP grafana_alerting_rule_group_rules The number of alert rules that are scheduled, by type and state.
|
||||
# TYPE grafana_alerting_rule_group_rules gauge
|
||||
grafana_alerting_rule_group_rules{folder_uid="%[4]s",org="%[1]d",rule_group="%[2]s",state="active",type="alerting"} 1
|
||||
grafana_alerting_rule_group_rules{folder_uid="%[5]s",org="%[1]d",rule_group="%[3]s",state="active",type="alerting"} 1
|
||||
`, alertRule1.OrgID, folderWithRuleGroup1, folderWithRuleGroup2, alertRule1.NamespaceUID, alertRule2.NamespaceUID)
|
||||
grafana_alerting_rule_group_rules{org="%[1]d",rule_group="%[2]s",state="active",type="alerting"} 1
|
||||
grafana_alerting_rule_group_rules{org="%[1]d",rule_group="%[3]s",state="active",type="alerting"} 1
|
||||
`, alertRule1.OrgID, folderWithRuleGroup1, folderWithRuleGroup2)
|
||||
|
||||
err := testutil.GatherAndCompare(reg, bytes.NewBufferString(expectedMetric), "grafana_alerting_rule_group_rules")
|
||||
require.NoError(t, err)
|
||||
@@ -720,9 +720,9 @@ func TestSchedule_updateRulesMetrics(t *testing.T) {
|
||||
expectedMetric := fmt.Sprintf(
|
||||
`# HELP grafana_alerting_rule_group_rules The number of alert rules that are scheduled, by type and state.
|
||||
# TYPE grafana_alerting_rule_group_rules gauge
|
||||
grafana_alerting_rule_group_rules{folder_uid="%[4]s",org="%[1]d",rule_group="%[2]s",state="active",type="alerting"} 1
|
||||
grafana_alerting_rule_group_rules{folder_uid="%[5]s",org="%[1]d",rule_group="%[3]s",state="active",type="alerting"} 1
|
||||
`, alertRule1.OrgID, folderWithRuleGroup1, folderWithRuleGroup2, alertRule1.NamespaceUID, alertRule2.NamespaceUID)
|
||||
grafana_alerting_rule_group_rules{org="%[1]d",rule_group="%[2]s",state="active",type="alerting"} 1
|
||||
grafana_alerting_rule_group_rules{org="%[1]d",rule_group="%[3]s",state="active",type="alerting"} 1
|
||||
`, alertRule1.OrgID, folderWithRuleGroup1, folderWithRuleGroup2)
|
||||
|
||||
err := testutil.GatherAndCompare(reg, bytes.NewBufferString(expectedMetric), "grafana_alerting_rule_group_rules")
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -7,8 +7,6 @@ import (
|
||||
"time"
|
||||
|
||||
badger "github.com/dgraph-io/badger/v4"
|
||||
"github.com/fullstorydev/grpchan"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
otgrpc "github.com/opentracing-contrib/go-grpc"
|
||||
"github.com/opentracing/opentracing-go"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
@@ -33,7 +31,6 @@ import (
|
||||
"github.com/grafana/grafana/pkg/storage/legacysql"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/federated"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
grpcUtils "github.com/grafana/grafana/pkg/storage/unified/resource/grpc"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/search"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/sql"
|
||||
"github.com/grafana/grafana/pkg/util/scheduler"
|
||||
@@ -94,27 +91,6 @@ func ProvideUnifiedStorageClient(opts *Options,
|
||||
return client, err
|
||||
}
|
||||
|
||||
// TODO use wire to provide to module server
|
||||
func NewSearchClient(opts options.StorageOptions, features featuremgmt.FeatureToggles) (resourcepb.ResourceIndexClient, error) {
|
||||
if opts.SearchServerAddress == "" {
|
||||
return nil, fmt.Errorf("expecting address for search server")
|
||||
}
|
||||
|
||||
var (
|
||||
conn grpc.ClientConnInterface
|
||||
err error
|
||||
metrics = newClientMetrics(prometheus.NewRegistry())
|
||||
)
|
||||
|
||||
conn, err = newGrpcConn(opts.SearchServerAddress, metrics, features, opts.GrpcClientKeepaliveTime)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cc := grpchan.InterceptClientConn(conn, grpcUtils.UnaryClientInterceptor, grpcUtils.StreamClientInterceptor)
|
||||
return resourcepb.NewResourceIndexClient(cc), nil
|
||||
}
|
||||
|
||||
func newClient(opts options.StorageOptions,
|
||||
cfg *setting.Cfg,
|
||||
features featuremgmt.FeatureToggles,
|
||||
|
||||
@@ -3,11 +3,9 @@ package resource
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/app"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
|
||||
@@ -103,9 +101,6 @@ type IndexableDocument struct {
|
||||
// metadata, annotations, or external data linked at index time
|
||||
Fields map[string]any `json:"fields,omitempty"`
|
||||
|
||||
// Automatically indexed selectable fields, used for field-based filtering when listing.
|
||||
SelectableFields map[string]string `json:"selectable_fields,omitempty"`
|
||||
|
||||
// Maintain a list of resource references.
|
||||
// Someday this will likely be part of https://github.com/grafana/gamma
|
||||
References ResourceReferences `json:"references,omitempty"`
|
||||
@@ -180,7 +175,7 @@ func (m ResourceReferences) Less(i, j int) bool {
|
||||
}
|
||||
|
||||
// Create a new indexable document based on a generic k8s resource
|
||||
func NewIndexableDocument(key *resourcepb.ResourceKey, rv int64, obj utils.GrafanaMetaAccessor, selectableFields map[string]string) *IndexableDocument {
|
||||
func NewIndexableDocument(key *resourcepb.ResourceKey, rv int64, obj utils.GrafanaMetaAccessor) *IndexableDocument {
|
||||
title := obj.FindTitle(key.Name)
|
||||
if title == key.Name {
|
||||
// TODO: something wrong with FindTitle
|
||||
@@ -196,15 +191,14 @@ func NewIndexableDocument(key *resourcepb.ResourceKey, rv int64, obj utils.Grafa
|
||||
}
|
||||
}
|
||||
doc := &IndexableDocument{
|
||||
Key: key,
|
||||
RV: rv,
|
||||
Name: key.Name,
|
||||
Title: title, // We always want *something* to display
|
||||
Labels: obj.GetLabels(),
|
||||
Folder: obj.GetFolder(),
|
||||
CreatedBy: obj.GetCreatedBy(),
|
||||
UpdatedBy: obj.GetUpdatedBy(),
|
||||
SelectableFields: selectableFields,
|
||||
Key: key,
|
||||
RV: rv,
|
||||
Name: key.Name,
|
||||
Title: title, // We always want *something* to display
|
||||
Labels: obj.GetLabels(),
|
||||
Folder: obj.GetFolder(),
|
||||
CreatedBy: obj.GetCreatedBy(),
|
||||
UpdatedBy: obj.GetUpdatedBy(),
|
||||
}
|
||||
m, ok := obj.GetManagerProperties()
|
||||
if ok {
|
||||
@@ -226,14 +220,11 @@ func NewIndexableDocument(key *resourcepb.ResourceKey, rv int64, obj utils.Grafa
|
||||
return doc.UpdateCopyFields()
|
||||
}
|
||||
|
||||
func StandardDocumentBuilder(manifests []app.Manifest) DocumentBuilder {
|
||||
return &standardDocumentBuilder{selectableFields: SelectableFieldsForManifests(manifests)}
|
||||
func StandardDocumentBuilder() DocumentBuilder {
|
||||
return &standardDocumentBuilder{}
|
||||
}
|
||||
|
||||
type standardDocumentBuilder struct {
|
||||
// Maps "group/resource" (in lowercase) to list of selectable fields.
|
||||
selectableFields map[string][]string
|
||||
}
|
||||
type standardDocumentBuilder struct{}
|
||||
|
||||
func (s *standardDocumentBuilder) BuildDocument(ctx context.Context, key *resourcepb.ResourceKey, rv int64, value []byte) (*IndexableDocument, error) {
|
||||
tmp := &unstructured.Unstructured{}
|
||||
@@ -247,36 +238,10 @@ func (s *standardDocumentBuilder) BuildDocument(ctx context.Context, key *resour
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sfKey := strings.ToLower(key.GetGroup() + "/" + key.GetResource())
|
||||
selectableFields := buildSelectableFields(tmp, s.selectableFields[sfKey])
|
||||
|
||||
doc := NewIndexableDocument(key, rv, obj, selectableFields)
|
||||
doc := NewIndexableDocument(key, rv, obj)
|
||||
return doc, nil
|
||||
}
|
||||
|
||||
func buildSelectableFields(tmp *unstructured.Unstructured, fields []string) map[string]string {
|
||||
result := map[string]string{}
|
||||
|
||||
for _, field := range fields {
|
||||
path := strings.Split(field, ".")
|
||||
val, ok, err := unstructured.NestedFieldNoCopy(tmp.Object, path...)
|
||||
if err != nil || !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
switch v := val.(type) {
|
||||
case string:
|
||||
result[field] = v
|
||||
case bool:
|
||||
result[field] = strconv.FormatBool(v)
|
||||
case int, float64:
|
||||
result[field] = fmt.Sprintf("%v", v)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
type searchableDocumentFields struct {
|
||||
names []string
|
||||
fields map[string]*resourceTableColumn
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
|
||||
func TestStandardDocumentBuilder(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
builder := StandardDocumentBuilder(nil)
|
||||
builder := StandardDocumentBuilder()
|
||||
|
||||
body, err := os.ReadFile("testdata/playlist-resource.json")
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
package resource
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/app"
|
||||
|
||||
folder "github.com/grafana/grafana/apps/folder/pkg/apis/manifestdata"
|
||||
iam "github.com/grafana/grafana/apps/iam/pkg/apis"
|
||||
)
|
||||
|
||||
func AppManifests() []app.Manifest {
|
||||
return []app.Manifest{
|
||||
iam.LocalManifest(),
|
||||
folder.LocalManifest(),
|
||||
}
|
||||
}
|
||||
|
||||
func SelectableFields() map[string][]string {
|
||||
return SelectableFieldsForManifests(AppManifests())
|
||||
}
|
||||
|
||||
// SelectableFieldsForManifests returns map of <group/kind> to list of selectable fields.
|
||||
// Also <group/plural> is included as a key, pointing to the same fields.
|
||||
func SelectableFieldsForManifests(manifests []app.Manifest) map[string][]string {
|
||||
fields := map[string][]string{}
|
||||
|
||||
for _, m := range manifests {
|
||||
group := m.ManifestData.Group
|
||||
|
||||
for _, version := range m.ManifestData.Versions {
|
||||
for _, kind := range version.Kinds {
|
||||
key := strings.ToLower(group + "/" + kind.Kind)
|
||||
keyPlural := strings.ToLower(group + "/" + kind.Plural)
|
||||
|
||||
if len(kind.SelectableFields) > 0 {
|
||||
fields[key] = kind.SelectableFields
|
||||
fields[keyPlural] = kind.SelectableFields
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return fields
|
||||
}
|
||||
@@ -12,8 +12,6 @@ import (
|
||||
|
||||
"github.com/Masterminds/semver"
|
||||
"github.com/google/uuid"
|
||||
claims "github.com/grafana/authlib/types"
|
||||
"github.com/grafana/dskit/backoff"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
@@ -22,6 +20,9 @@ import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
||||
claims "github.com/grafana/authlib/types"
|
||||
"github.com/grafana/dskit/backoff"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/validation"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
@@ -222,9 +223,6 @@ type ResourceServerOptions struct {
|
||||
// Search options
|
||||
Search SearchOptions
|
||||
|
||||
// to be used by storage
|
||||
SearchClient resourcepb.ResourceIndexClient
|
||||
|
||||
// Quota service
|
||||
OverridesService *OverridesService
|
||||
|
||||
@@ -350,7 +348,6 @@ func NewResourceServer(opts ResourceServerOptions) (*server, error) {
|
||||
queue: opts.QOSQueue,
|
||||
queueConfig: opts.QOSConfig,
|
||||
overridesService: opts.OverridesService,
|
||||
searchClient: opts.SearchClient,
|
||||
|
||||
artificialSuccessfulWriteDelay: opts.Search.IndexMinUpdateInterval,
|
||||
}
|
||||
@@ -390,9 +387,6 @@ type server struct {
|
||||
indexMetrics *BleveIndexMetrics
|
||||
overridesService *OverridesService
|
||||
|
||||
// only to be used with storage server for field selector search
|
||||
searchClient resourcepb.ResourceIndexClient
|
||||
|
||||
// Background watch task -- this has permissions for everything
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
@@ -1049,75 +1043,6 @@ func (s *server) List(ctx context.Context, req *resourcepb.ListRequest) (*resour
|
||||
return rsp, nil
|
||||
}
|
||||
|
||||
// Remove metadata.namespace filter from requirement fields, if it's present.
|
||||
for ix := 0; ix < len(req.Options.Fields); {
|
||||
v := req.Options.Fields[ix]
|
||||
if v.Key == "metadata.namespace" && v.Operator == "=" {
|
||||
if len(v.Values) == 1 && v.Values[0] == req.Options.Key.Namespace {
|
||||
// Remove this requirement from fields, as it's implied by the key.namespace.
|
||||
req.Options.Fields = append(req.Options.Fields[:ix], req.Options.Fields[ix+1:]...)
|
||||
// Don't increment ix, as we're removing an element from the slice.
|
||||
continue
|
||||
}
|
||||
}
|
||||
ix++
|
||||
}
|
||||
|
||||
// TODO: What to do about RV and version_match fields?
|
||||
// If we get here, we're doing list with selectable fields. Let's do search instead, since
|
||||
// we index all selectable fields, and fetch resulting documents one by one.
|
||||
|
||||
if (s.search != nil || s.searchClient != nil) && req.Source == resourcepb.ListRequest_STORE && (len(req.Options.Fields) > 0) {
|
||||
if req.Options.Key.Namespace == "" {
|
||||
return &resourcepb.ListResponse{
|
||||
Error: NewBadRequestError("namespace must be specified for list with filter"),
|
||||
}, nil
|
||||
}
|
||||
|
||||
srq := &resourcepb.ResourceSearchRequest{
|
||||
Options: req.Options,
|
||||
//Federated: nil,
|
||||
Limit: req.Limit,
|
||||
// Offset: req.NextPageToken, // TODO
|
||||
// Page: 0,
|
||||
// Permission: 0, // Not needed, default is List
|
||||
}
|
||||
|
||||
var searchResp *resourcepb.ResourceSearchResponse
|
||||
var err error
|
||||
if s.searchClient != nil {
|
||||
searchResp, err = s.searchClient.Search(ctx, srq)
|
||||
} else {
|
||||
searchResp, err = s.search.Search(ctx, srq)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rsp := &resourcepb.ListResponse{}
|
||||
// Using searchResp.GetResults().GetRows() will not panic if anything is nil on the path.
|
||||
for _, row := range searchResp.GetResults().GetRows() {
|
||||
// TODO: use batch reading
|
||||
val, err := s.Read(ctx, &resourcepb.ReadRequest{
|
||||
Key: row.Key,
|
||||
ResourceVersion: row.ResourceVersion,
|
||||
})
|
||||
if err != nil {
|
||||
return &resourcepb.ListResponse{Error: AsErrorResult(err)}, nil
|
||||
}
|
||||
if len(val.Value) > 0 {
|
||||
rsp.Items = append(rsp.Items, &resourcepb.ResourceWrapper{
|
||||
Value: val.Value,
|
||||
ResourceVersion: val.ResourceVersion,
|
||||
})
|
||||
if val.ResourceVersion > rsp.ResourceVersion {
|
||||
rsp.ResourceVersion = val.ResourceVersion
|
||||
}
|
||||
}
|
||||
}
|
||||
return rsp, nil
|
||||
}
|
||||
|
||||
if req.Limit < 1 {
|
||||
req.Limit = 500 // default max 500 items in a page
|
||||
}
|
||||
|
||||
@@ -59,6 +59,7 @@ type kvStorageBackend struct {
|
||||
dataStore *dataStore
|
||||
eventStore *eventStore
|
||||
notifier *notifier
|
||||
builder DocumentBuilder
|
||||
log logging.Logger
|
||||
withPruner bool
|
||||
eventRetentionPeriod time.Duration
|
||||
@@ -108,7 +109,8 @@ func NewKVStorageBackend(opts KVBackendOptions) (StorageBackend, error) {
|
||||
eventStore: eventStore,
|
||||
notifier: newNotifier(eventStore, notifierOptions{}),
|
||||
snowflake: s,
|
||||
log: &logging.NoOpLogger{}, // Make this configurable
|
||||
builder: StandardDocumentBuilder(), // For now we use the standard document builder.
|
||||
log: &logging.NoOpLogger{}, // Make this configurable
|
||||
eventRetentionPeriod: eventRetentionPeriod,
|
||||
eventPruningInterval: eventPruningInterval,
|
||||
withExperimentalClusterScope: opts.WithExperimentalClusterScope,
|
||||
|
||||
@@ -93,8 +93,6 @@ type bleveBackend struct {
|
||||
|
||||
indexMetrics *resource.BleveIndexMetrics
|
||||
|
||||
selectableFields map[string][]string
|
||||
|
||||
bgTasksCancel func()
|
||||
bgTasksWg sync.WaitGroup
|
||||
}
|
||||
@@ -137,12 +135,11 @@ func NewBleveBackend(opts BleveOptions, indexMetrics *resource.BleveIndexMetrics
|
||||
}
|
||||
|
||||
be := &bleveBackend{
|
||||
log: l,
|
||||
cache: map[resource.NamespacedResource]*bleveIndex{},
|
||||
opts: opts,
|
||||
ownsIndexFn: ownFn,
|
||||
indexMetrics: indexMetrics,
|
||||
selectableFields: resource.SelectableFields(),
|
||||
log: l,
|
||||
cache: map[resource.NamespacedResource]*bleveIndex{},
|
||||
opts: opts,
|
||||
ownsIndexFn: ownFn,
|
||||
indexMetrics: indexMetrics,
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
@@ -369,9 +366,7 @@ func (b *bleveBackend) BuildIndex(
|
||||
attribute.String("reason", indexBuildReason),
|
||||
)
|
||||
|
||||
selectableFields := b.selectableFields[fmt.Sprintf("%s/%s", key.Group, key.Resource)]
|
||||
|
||||
mapper, err := GetBleveMappings(fields, selectableFields)
|
||||
mapper, err := GetBleveMappings(fields)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -464,7 +459,7 @@ func (b *bleveBackend) BuildIndex(
|
||||
}
|
||||
|
||||
// Batch all the changes
|
||||
idx := b.newBleveIndex(key, index, newIndexType, fields, allFields, standardSearchFields, selectableFields, updater, b.log.New("namespace", key.Namespace, "group", key.Group, "resource", key.Resource))
|
||||
idx := b.newBleveIndex(key, index, newIndexType, fields, allFields, standardSearchFields, updater, b.log.New("namespace", key.Namespace, "group", key.Group, "resource", key.Resource))
|
||||
|
||||
if build {
|
||||
if b.indexMetrics != nil {
|
||||
@@ -704,9 +699,8 @@ type bleveIndex struct {
|
||||
// Subsequent update requests only trigger new update if minUpdateInterval has elapsed.
|
||||
nextUpdateTime time.Time
|
||||
|
||||
standard resource.SearchableDocumentFields
|
||||
fields resource.SearchableDocumentFields
|
||||
selectableFields []string
|
||||
standard resource.SearchableDocumentFields
|
||||
fields resource.SearchableDocumentFields
|
||||
|
||||
indexStorage string // memory or file, used when updating metrics
|
||||
|
||||
@@ -742,7 +736,6 @@ func (b *bleveBackend) newBleveIndex(
|
||||
fields resource.SearchableDocumentFields,
|
||||
allFields []*resourcepb.ResourceTableColumnDefinition,
|
||||
standardSearchFields resource.SearchableDocumentFields,
|
||||
selectableFields []string,
|
||||
updaterFn resource.UpdateFn,
|
||||
logger log.Logger,
|
||||
) *bleveIndex {
|
||||
@@ -752,7 +745,6 @@ func (b *bleveBackend) newBleveIndex(
|
||||
indexStorage: newIndexType,
|
||||
fields: fields,
|
||||
allFields: allFields,
|
||||
selectableFields: selectableFields,
|
||||
standard: standardSearchFields,
|
||||
logger: logger,
|
||||
updaterFn: updaterFn,
|
||||
@@ -1223,11 +1215,7 @@ func (b *bleveIndex) toBleveSearchRequest(ctx context.Context, req *resourcepb.R
|
||||
// filters
|
||||
if len(req.Options.Fields) > 0 {
|
||||
for _, v := range req.Options.Fields {
|
||||
prefix := ""
|
||||
if b.isSelectableField(v.Key) {
|
||||
prefix = "selectable_fields."
|
||||
}
|
||||
q, err := requirementQuery(v, prefix)
|
||||
q, err := requirementQuery(v, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1799,15 +1787,6 @@ func (b *bleveIndex) hitsToTable(ctx context.Context, selectFields []string, hit
|
||||
return table, nil
|
||||
}
|
||||
|
||||
func (b *bleveIndex) isSelectableField(key string) bool {
|
||||
for _, f := range b.selectableFields {
|
||||
if key == f {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func getAllFields(standard resource.SearchableDocumentFields, custom resource.SearchableDocumentFields) ([]*resourcepb.ResourceTableColumnDefinition, error) {
|
||||
fields := []*resourcepb.ResourceTableColumnDefinition{
|
||||
standard.Field(resource.SEARCH_FIELD_ID),
|
||||
|
||||
@@ -10,19 +10,19 @@ import (
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
)
|
||||
|
||||
func GetBleveMappings(fields resource.SearchableDocumentFields, selectableFields []string) (mapping.IndexMapping, error) {
|
||||
func GetBleveMappings(fields resource.SearchableDocumentFields) (mapping.IndexMapping, error) {
|
||||
mapper := bleve.NewIndexMapping()
|
||||
|
||||
err := RegisterCustomAnalyzers(mapper)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mapper.DefaultMapping = getBleveDocMappings(fields, selectableFields)
|
||||
mapper.DefaultMapping = getBleveDocMappings(fields)
|
||||
|
||||
return mapper, nil
|
||||
}
|
||||
|
||||
func getBleveDocMappings(fields resource.SearchableDocumentFields, selectableFields []string) *mapping.DocumentMapping {
|
||||
func getBleveDocMappings(fields resource.SearchableDocumentFields) *mapping.DocumentMapping {
|
||||
mapper := bleve.NewDocumentStaticMapping()
|
||||
|
||||
nameMapping := &mapping.FieldMapping{
|
||||
@@ -165,73 +165,5 @@ func getBleveDocMappings(fields resource.SearchableDocumentFields, selectableFie
|
||||
|
||||
mapper.AddSubDocumentMapping("fields", fieldMapper)
|
||||
|
||||
selectableFieldsMapper := bleve.NewDocumentStaticMapping()
|
||||
for _, field := range selectableFields {
|
||||
selectableFieldsMapper.AddFieldMappingsAt(field, &mapping.FieldMapping{
|
||||
Name: field,
|
||||
Type: "text",
|
||||
Analyzer: keyword.Name,
|
||||
Store: false,
|
||||
Index: true,
|
||||
})
|
||||
}
|
||||
mapper.AddSubDocumentMapping("selectable_fields", selectableFieldsMapper)
|
||||
|
||||
return mapper
|
||||
}
|
||||
|
||||
/*
|
||||
Here's a tree representation of the field mappings in pkg/storage/unified/search/bleve_mappings.go:
|
||||
|
||||
Document Root (DefaultMapping)
|
||||
│
|
||||
├── name [text, keyword analyzer]
|
||||
│
|
||||
├── title_phrase [keyword, not stored]
|
||||
│
|
||||
├── title [3 mappings]
|
||||
│ ├── [1] standard analyzer, stored
|
||||
│ ├── [2] TITLE_ANALYZER (edge ngram), not stored
|
||||
│ └── [3] keyword, not stored
|
||||
│
|
||||
├── description [text, stored]
|
||||
│
|
||||
├── tags [text, keyword analyzer, stored, includeInAll]
|
||||
│
|
||||
├── folder [text, keyword analyzer, stored, includeInAll, docValues]
|
||||
│
|
||||
├── managedBy [text, keyword analyzer, not stored]
|
||||
│
|
||||
├── source/ [sub-document]
|
||||
│ ├── path [text, keyword analyzer, stored]
|
||||
│ ├── checksum [text, keyword analyzer, stored]
|
||||
│ └── timestampMillis [numeric]
|
||||
│
|
||||
├── manager/ [sub-document]
|
||||
│ ├── kind [text, keyword analyzer, stored, includeInAll]
|
||||
│ └── id [text, keyword analyzer, stored, includeInAll]
|
||||
│
|
||||
├── reference/ [sub-document, default analyzer: keyword]
|
||||
│ └── (dynamic fields inherit keyword analyzer)
|
||||
│
|
||||
├── labels/ [sub-document]
|
||||
│ └── (dynamic fields)
|
||||
│
|
||||
└── fields/ [sub-document]
|
||||
└── (conditional mappings)
|
||||
├── {filterable string fields} [keyword, stored]
|
||||
└── {other fields} [dynamically mapped by Bleve]
|
||||
|
||||
Key observations:
|
||||
|
||||
- Root level has standard searchable fields (name, title, description, tags, folder)
|
||||
- title has 3 analyzers applied: standard (for word search), edge ngram (for prefix search), and keyword (for phrase sorting)
|
||||
- source/, manager/: Static sub-documents with explicitly mapped fields
|
||||
- reference/: Dynamic sub-document with keyword default analyzer (line 142)
|
||||
- labels/, fields/: Dynamic sub-documents where Bleve auto-detects field types at index time
|
||||
|
||||
References:
|
||||
- Main mapping function: pkg/storage/unified/search/bleve_mappings.go:25-169
|
||||
- Sub-document mappings: lines 88-143
|
||||
- Dynamic fields handling: lines 148-166
|
||||
*/
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
)
|
||||
|
||||
func TestDocumentMapping(t *testing.T) {
|
||||
mappings, err := search.GetBleveMappings(nil, nil)
|
||||
mappings, err := search.GetBleveMappings(nil)
|
||||
require.NoError(t, err)
|
||||
data := resource.IndexableDocument{
|
||||
Title: "title",
|
||||
|
||||
@@ -264,7 +264,7 @@ func (s *DashboardDocumentBuilder) BuildDocument(ctx context.Context, key *resou
|
||||
summary.UID = obj.GetName()
|
||||
summary.ID = obj.GetDeprecatedInternalID() // nolint:staticcheck
|
||||
|
||||
doc := resource.NewIndexableDocument(key, rv, obj, nil)
|
||||
doc := resource.NewIndexableDocument(key, rv, obj)
|
||||
doc.Title = summary.Title
|
||||
doc.Description = summary.Description
|
||||
doc.Tags = summary.Tags
|
||||
|
||||
@@ -115,7 +115,7 @@ func TestDashboardDocumentBuilder(t *testing.T) {
|
||||
"aaa",
|
||||
})
|
||||
|
||||
builder = resource.StandardDocumentBuilder(nil)
|
||||
builder = resource.StandardDocumentBuilder()
|
||||
doSnapshotTests(t, builder, "folder", &resourcepb.ResourceKey{
|
||||
Namespace: "default",
|
||||
Group: "folder.grafana.app",
|
||||
|
||||
@@ -66,7 +66,7 @@ func (u *extGroupMappingDocumentBuilder) BuildDocument(ctx context.Context, key
|
||||
return nil, err
|
||||
}
|
||||
|
||||
doc := resource.NewIndexableDocument(key, rv, obj, nil)
|
||||
doc := resource.NewIndexableDocument(key, rv, obj)
|
||||
|
||||
doc.Fields = make(map[string]any)
|
||||
if extGroupMapping.Spec.TeamRef.Name != "" {
|
||||
|
||||
@@ -70,7 +70,7 @@ func (t *teamSearchBuilder) BuildDocument(ctx context.Context, key *resourcepb.R
|
||||
return nil, err
|
||||
}
|
||||
|
||||
doc := resource.NewIndexableDocument(key, rv, obj, nil)
|
||||
doc := resource.NewIndexableDocument(key, rv, obj)
|
||||
|
||||
doc.Fields = make(map[string]any)
|
||||
if team.Spec.Email != "" {
|
||||
|
||||
@@ -66,7 +66,7 @@ func (u *userDocumentBuilder) BuildDocument(ctx context.Context, key *resourcepb
|
||||
return nil, err
|
||||
}
|
||||
|
||||
doc := resource.NewIndexableDocument(key, rv, obj, nil)
|
||||
doc := resource.NewIndexableDocument(key, rv, obj)
|
||||
|
||||
doc.Fields = make(map[string]any)
|
||||
if user.Spec.Email != "" {
|
||||
|
||||
@@ -25,7 +25,7 @@ func (s *StandardDocumentBuilders) GetDocumentBuilders() ([]resource.DocumentBui
|
||||
|
||||
result := []resource.DocumentBuilderInfo{
|
||||
{
|
||||
Builder: resource.StandardDocumentBuilder(resource.AppManifests()),
|
||||
Builder: resource.StandardDocumentBuilder(),
|
||||
},
|
||||
}
|
||||
return append(result, all...), nil
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
|
||||
@@ -38,7 +37,6 @@ type ServerOptions struct {
|
||||
Tracer trace.Tracer
|
||||
Reg prometheus.Registerer
|
||||
AccessClient types.AccessClient
|
||||
SearchClient resourcepb.ResourceIndexClient
|
||||
SearchOptions resource.SearchOptions
|
||||
StorageMetrics *resource.StorageMetrics
|
||||
IndexMetrics *resource.BleveIndexMetrics
|
||||
@@ -117,10 +115,6 @@ func NewResourceServer(opts ServerOptions) (resource.ResourceServer, error) {
|
||||
serverOptions.Lifecycle = backend
|
||||
}
|
||||
|
||||
// use the search client when search isnt initialized. Dont need both.
|
||||
if opts.SearchOptions.Resources == nil {
|
||||
serverOptions.SearchClient = opts.SearchClient
|
||||
}
|
||||
serverOptions.Search = opts.SearchOptions
|
||||
serverOptions.IndexMetrics = opts.IndexMetrics
|
||||
serverOptions.QOSQueue = opts.QOSQueue
|
||||
|
||||
@@ -59,13 +59,12 @@ type service struct {
|
||||
subservicesWatcher *services.FailureWatcher
|
||||
hasSubservices bool
|
||||
|
||||
backend resource.StorageBackend
|
||||
searchClient resourcepb.ResourceIndexClient
|
||||
cfg *setting.Cfg
|
||||
features featuremgmt.FeatureToggles
|
||||
db infraDB.DB
|
||||
stopCh chan struct{}
|
||||
stoppedCh chan error
|
||||
backend resource.StorageBackend
|
||||
cfg *setting.Cfg
|
||||
features featuremgmt.FeatureToggles
|
||||
db infraDB.DB
|
||||
stopCh chan struct{}
|
||||
stoppedCh chan error
|
||||
|
||||
handler grpcserver.Provider
|
||||
|
||||
@@ -100,7 +99,6 @@ func ProvideUnifiedStorageGrpcService(
|
||||
memberlistKVConfig kv.Config,
|
||||
httpServerRouter *mux.Router,
|
||||
backend resource.StorageBackend,
|
||||
searchClient resourcepb.ResourceIndexClient,
|
||||
) (UnifiedStorageGrpcService, error) {
|
||||
var err error
|
||||
tracer := otel.Tracer("unified-storage")
|
||||
@@ -114,7 +112,6 @@ func ProvideUnifiedStorageGrpcService(
|
||||
|
||||
s := &service{
|
||||
backend: backend,
|
||||
searchClient: searchClient,
|
||||
cfg: cfg,
|
||||
features: features,
|
||||
stopCh: make(chan struct{}),
|
||||
@@ -275,7 +272,6 @@ func (s *service) starting(ctx context.Context) error {
|
||||
Tracer: s.tracing,
|
||||
Reg: s.reg,
|
||||
AccessClient: authzClient,
|
||||
SearchClient: s.searchClient,
|
||||
SearchOptions: searchOptions,
|
||||
StorageMetrics: s.storageMetrics,
|
||||
IndexMetrics: s.indexMetrics,
|
||||
|
||||
@@ -79,8 +79,16 @@ export function PanelNonApplicableDrilldownsSubHeader({ filtersVar, groupByVar,
|
||||
};
|
||||
|
||||
fetchApplicability();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [filterKey, groupByKey, filtersVar, groupByVar, queryRunner]);
|
||||
}, [
|
||||
filterKey,
|
||||
groupByKey,
|
||||
filtersVar,
|
||||
groupByVar,
|
||||
queryRunner,
|
||||
filtersState?.filters,
|
||||
groupByState?.value,
|
||||
filtersState?.originFilters,
|
||||
]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (!nonApplicable.length) {
|
||||
|
||||
Reference in New Issue
Block a user