mirror of https://github.com/grafana/grafana.git
Merge branch 'main' into ash/modal-button-row
CodeQL checks / Detect whether code changed (push) Waiting to run
Details
CodeQL checks / Analyze (actions) (push) Blocked by required conditions
Details
CodeQL checks / Analyze (go) (push) Blocked by required conditions
Details
CodeQL checks / Analyze (javascript) (push) Blocked by required conditions
Details
CodeQL checks / Detect whether code changed (push) Waiting to run
Details
CodeQL checks / Analyze (actions) (push) Blocked by required conditions
Details
CodeQL checks / Analyze (go) (push) Blocked by required conditions
Details
CodeQL checks / Analyze (javascript) (push) Blocked by required conditions
Details
This commit is contained in:
commit
c678fd9086
|
@ -88,6 +88,7 @@
|
|||
/apps/preferences/ @grafana/grafana-app-platform-squad @grafana/grafana-frontend-platform
|
||||
/apps/shorturl/ @grafana/sharing-squad
|
||||
/apps/secret/ @grafana/grafana-operator-experience-squad
|
||||
/apps/scope/ @grafana/grafana-operator-experience-squad
|
||||
/apps/investigations/ @fcjack @matryer @svennergr
|
||||
/apps/advisor/ @grafana/plugins-platform-backend
|
||||
/apps/iam/ @grafana/access-squad
|
||||
|
@ -629,6 +630,7 @@
|
|||
/packages/grafana-runtime/rollup.config.ts @grafana/grafana-frontend-platform
|
||||
/packages/grafana-runtime/src/index.ts @grafana/grafana-frontend-platform @grafana/plugins-platform-frontend
|
||||
/packages/grafana-runtime/src/internal/index.ts @grafana/grafana-frontend-platform @grafana/plugins-platform-frontend
|
||||
/packages/grafana-runtime/src/internal/openFeature @grafana/grafana-frontend-platform
|
||||
/packages/grafana-runtime/src/unstable.ts @grafana/grafana-frontend-platform @grafana/plugins-platform-frontend
|
||||
/packages/grafana-runtime/tsconfig.build.json @grafana/grafana-frontend-platform
|
||||
/packages/grafana-runtime/tsconfig.json @grafana/grafana-frontend-platform
|
||||
|
@ -1281,6 +1283,7 @@ embed.go @grafana/grafana-as-code
|
|||
/.github/license_finder.yaml @bergquist
|
||||
/.github/actionlint.yaml @grafana/grafana-developer-enablement-squad
|
||||
/.github/workflows/pr-test-docker.yml @grafana/grafana-developer-enablement-squad
|
||||
/.github/workflows/update-schema-types.yml @grafana/plugins-platform-frontend
|
||||
|
||||
# Generated files not requiring owner approval
|
||||
/packages/grafana-data/src/types/featureToggles.gen.ts @grafanabot
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
name: Update Schema Types
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- docs/sources/developers/plugins/plugin.schema.json
|
||||
workflow_dispatch:
|
||||
|
||||
# These permissions are needed to assume roles from Github's OIDC.
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
|
||||
jobs:
|
||||
bundle-schema-types:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: grafana/plugin-actions/bundle-schema-types@main
|
|
@ -99,6 +99,7 @@ COPY apps/correlations apps/correlations
|
|||
COPY apps/preferences apps/preferences
|
||||
COPY apps/provisioning apps/provisioning
|
||||
COPY apps/secret apps/secret
|
||||
COPY apps/scope apps/scope
|
||||
COPY apps/investigations apps/investigations
|
||||
COPY apps/advisor apps/advisor
|
||||
COPY apps/dashboard apps/dashboard
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"github.com/grafana/grafana-app-sdk/app"
|
||||
"github.com/grafana/grafana-app-sdk/k8s"
|
||||
"github.com/grafana/grafana-app-sdk/logging"
|
||||
"github.com/grafana/grafana-app-sdk/operator"
|
||||
"github.com/grafana/grafana-app-sdk/resource"
|
||||
"github.com/grafana/grafana-app-sdk/simple"
|
||||
advisorv0alpha1 "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1"
|
||||
|
@ -48,10 +49,12 @@ func New(cfg app.Config) (app.App, error) {
|
|||
Name: "advisor",
|
||||
KubeConfig: cfg.KubeConfig,
|
||||
InformerConfig: simple.AppInformerConfig{
|
||||
InformerOptions: operator.InformerOptions{
|
||||
ErrorHandler: func(ctx context.Context, err error) {
|
||||
log.WithContext(ctx).Error("Informer processing error", "error", err)
|
||||
},
|
||||
},
|
||||
},
|
||||
ManagedKinds: []simple.AppManagedKind{
|
||||
{
|
||||
Kind: advisorv0alpha1.CheckKind(),
|
||||
|
|
|
@ -3,14 +3,14 @@ module github.com/grafana/grafana/apps/alerting/alertenrichment
|
|||
go 1.24.6
|
||||
|
||||
require (
|
||||
github.com/grafana/grafana-app-sdk v0.45.0
|
||||
github.com/grafana/grafana-app-sdk v0.46.0
|
||||
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250901080157-a0280d701b28
|
||||
k8s.io/apimachinery v0.34.1
|
||||
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/emicklei/go-restful/v3 v3.12.2 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.13.0 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
||||
|
|
|
@ -2,8 +2,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
|||
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.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU=
|
||||
github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
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/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
|
@ -21,8 +21,8 @@ github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7O
|
|||
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/grafana/grafana-app-sdk v0.45.0 h1:niFqYovxuw9vnUB9qoxEgmupqriG7Gns9ZGwB2uuOyE=
|
||||
github.com/grafana/grafana-app-sdk v0.45.0/go.mod h1:1pYGEBrgG8i6pKmmsNXvtAr15jZ4iLtyHU4yj7T6XaI=
|
||||
github.com/grafana/grafana-app-sdk v0.46.0 h1:gvzQvCQgZJ/73BfAcbDt/6TAMhnVikVPxZt/UwDl+oc=
|
||||
github.com/grafana/grafana-app-sdk v0.46.0/go.mod h1:LCTrqR1SwBS13XGVYveBmM7giJDDjzuXK+M9VzPuPWc=
|
||||
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250901080157-a0280d701b28 h1:PgMfX4OPENz/iXmtDDIW9+poZY4UD0hhmXm7flVclDo=
|
||||
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250901080157-a0280d701b28/go.mod h1:av5N0Naq+8VV9MLF7zAkihy/mVq5UbS2EvRSJukDHlY=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
|
|
|
@ -5,6 +5,7 @@ metadata:
|
|||
spec:
|
||||
appName: alerting-notifications
|
||||
group: notifications.alerting.grafana.app
|
||||
preferredVersion: v0alpha1
|
||||
versions:
|
||||
- kinds:
|
||||
- conversion: false
|
||||
|
|
|
@ -3,7 +3,7 @@ module github.com/grafana/grafana/apps/alerting/notifications
|
|||
go 1.24.6
|
||||
|
||||
require (
|
||||
github.com/grafana/grafana-app-sdk v0.45.0
|
||||
github.com/grafana/grafana-app-sdk v0.46.0
|
||||
github.com/grafana/grafana-app-sdk/logging v0.45.0
|
||||
k8s.io/apimachinery v0.34.1
|
||||
k8s.io/apiserver v0.34.1
|
||||
|
@ -19,7 +19,7 @@ require (
|
|||
github.com/coreos/go-semver v0.3.1 // indirect
|
||||
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.12.2 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.13.0 // indirect
|
||||
github.com/evanphx/json-patch v5.9.11+incompatible // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
|
||||
|
|
|
@ -21,8 +21,8 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1
|
|||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU=
|
||||
github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
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/evanphx/json-patch v5.9.11+incompatible h1:ixHHqfcGvxhWkniF1tWxBHA0yb4Z+d1UQi45df52xW8=
|
||||
github.com/evanphx/json-patch v5.9.11+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
|
@ -69,8 +69,8 @@ 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/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo=
|
||||
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
|
||||
github.com/grafana/grafana-app-sdk v0.45.0 h1:niFqYovxuw9vnUB9qoxEgmupqriG7Gns9ZGwB2uuOyE=
|
||||
github.com/grafana/grafana-app-sdk v0.45.0/go.mod h1:1pYGEBrgG8i6pKmmsNXvtAr15jZ4iLtyHU4yj7T6XaI=
|
||||
github.com/grafana/grafana-app-sdk v0.46.0 h1:gvzQvCQgZJ/73BfAcbDt/6TAMhnVikVPxZt/UwDl+oc=
|
||||
github.com/grafana/grafana-app-sdk v0.46.0/go.mod h1:LCTrqR1SwBS13XGVYveBmM7giJDDjzuXK+M9VzPuPWc=
|
||||
github.com/grafana/grafana-app-sdk/logging v0.45.0 h1:0SH6nYZpiLBZRwUq4J6+1vo8xuHKJjnO95/2pGOoA8w=
|
||||
github.com/grafana/grafana-app-sdk/logging v0.45.0/go.mod h1:Gh/nBWnspK3oDNWtiM5qUF/fardHzOIEez+SPI3JeHA=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0 h1:QGLs/O40yoNK9vmy4rhUGBVyMf1lISBGtXRpsu/Qu/o=
|
||||
|
|
|
@ -36,6 +36,7 @@ var (
|
|||
var appManifestData = app.ManifestData{
|
||||
AppName: "alerting-notifications",
|
||||
Group: "notifications.alerting.grafana.app",
|
||||
PreferredVersion: "v0alpha1",
|
||||
Versions: []app.ManifestVersion{
|
||||
{
|
||||
Name: "v0alpha1",
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
|
||||
"github.com/grafana/grafana-app-sdk/app"
|
||||
"github.com/grafana/grafana-app-sdk/logging"
|
||||
"github.com/grafana/grafana-app-sdk/operator"
|
||||
"github.com/grafana/grafana-app-sdk/simple"
|
||||
|
||||
"github.com/grafana/grafana/apps/alerting/notifications/pkg/apis"
|
||||
|
@ -22,10 +23,12 @@ func New(cfg app.Config) (app.App, error) {
|
|||
Name: "alerting.notification",
|
||||
KubeConfig: cfg.KubeConfig,
|
||||
InformerConfig: simple.AppInformerConfig{
|
||||
InformerOptions: operator.InformerOptions{
|
||||
ErrorHandler: func(ctx context.Context, err error) {
|
||||
logging.DefaultLogger.With("error", err).Error("Informer processing error")
|
||||
},
|
||||
},
|
||||
},
|
||||
ManagedKinds: managedKinds,
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ metadata:
|
|||
spec:
|
||||
appName: alerting
|
||||
group: rules.alerting.grafana.app
|
||||
preferredVersion: v0alpha1
|
||||
versions:
|
||||
- kinds:
|
||||
- conversion: false
|
||||
|
|
|
@ -3,7 +3,7 @@ module github.com/grafana/grafana/apps/alerting/rules
|
|||
go 1.24.4
|
||||
|
||||
require (
|
||||
github.com/grafana/grafana-app-sdk v0.45.0
|
||||
github.com/grafana/grafana-app-sdk v0.46.0
|
||||
github.com/grafana/grafana-app-sdk/logging v0.45.0
|
||||
k8s.io/apimachinery v0.34.1
|
||||
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b
|
||||
|
@ -15,7 +15,7 @@ require (
|
|||
github.com/cenkalti/backoff/v5 v5.0.3 // 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.12.2 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.13.0 // indirect
|
||||
github.com/evanphx/json-patch v5.9.11+incompatible // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
|
||||
github.com/getkin/kin-openapi v0.133.0 // indirect
|
||||
|
|
|
@ -10,8 +10,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
|||
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.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU=
|
||||
github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
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/evanphx/json-patch v5.9.11+incompatible h1:ixHHqfcGvxhWkniF1tWxBHA0yb4Z+d1UQi45df52xW8=
|
||||
github.com/evanphx/json-patch v5.9.11+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
|
||||
|
@ -46,8 +46,8 @@ github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J
|
|||
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
|
||||
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.45.0 h1:niFqYovxuw9vnUB9qoxEgmupqriG7Gns9ZGwB2uuOyE=
|
||||
github.com/grafana/grafana-app-sdk v0.45.0/go.mod h1:1pYGEBrgG8i6pKmmsNXvtAr15jZ4iLtyHU4yj7T6XaI=
|
||||
github.com/grafana/grafana-app-sdk v0.46.0 h1:gvzQvCQgZJ/73BfAcbDt/6TAMhnVikVPxZt/UwDl+oc=
|
||||
github.com/grafana/grafana-app-sdk v0.46.0/go.mod h1:LCTrqR1SwBS13XGVYveBmM7giJDDjzuXK+M9VzPuPWc=
|
||||
github.com/grafana/grafana-app-sdk/logging v0.45.0 h1:0SH6nYZpiLBZRwUq4J6+1vo8xuHKJjnO95/2pGOoA8w=
|
||||
github.com/grafana/grafana-app-sdk/logging v0.45.0/go.mod h1:Gh/nBWnspK3oDNWtiM5qUF/fardHzOIEez+SPI3JeHA=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU=
|
||||
|
|
|
@ -30,6 +30,7 @@ var (
|
|||
var appManifestData = app.ManifestData{
|
||||
AppName: "alerting",
|
||||
Group: "rules.alerting.grafana.app",
|
||||
PreferredVersion: "v0alpha1",
|
||||
Versions: []app.ManifestVersion{
|
||||
{
|
||||
Name: "v0alpha1",
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
|
||||
"github.com/grafana/grafana-app-sdk/app"
|
||||
"github.com/grafana/grafana-app-sdk/logging"
|
||||
"github.com/grafana/grafana-app-sdk/operator"
|
||||
"github.com/grafana/grafana-app-sdk/simple"
|
||||
|
||||
"github.com/grafana/grafana/apps/alerting/rules/pkg/apis"
|
||||
|
@ -22,10 +23,12 @@ func New(cfg app.Config) (app.App, error) {
|
|||
Name: "alerting.rules",
|
||||
KubeConfig: cfg.KubeConfig,
|
||||
InformerConfig: simple.AppInformerConfig{
|
||||
InformerOptions: operator.InformerOptions{
|
||||
ErrorHandler: func(ctx context.Context, err error) {
|
||||
logging.DefaultLogger.With("error", err).Error("Informer processing error")
|
||||
},
|
||||
},
|
||||
},
|
||||
ManagedKinds: managedKinds,
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ go 1.24.0
|
|||
toolchain go1.24.6
|
||||
|
||||
require (
|
||||
github.com/grafana/grafana-app-sdk v0.45.0
|
||||
github.com/grafana/grafana-app-sdk v0.46.0
|
||||
github.com/grafana/grafana-app-sdk/logging v0.45.0
|
||||
k8s.io/apimachinery v0.34.1
|
||||
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b
|
||||
|
@ -17,7 +17,7 @@ require (
|
|||
github.com/cenkalti/backoff/v5 v5.0.3 // 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.12.2 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.13.0 // indirect
|
||||
github.com/evanphx/json-patch v5.9.11+incompatible // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
|
||||
github.com/getkin/kin-openapi v0.133.0 // indirect
|
||||
|
|
|
@ -10,8 +10,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
|||
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.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU=
|
||||
github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
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/evanphx/json-patch v5.9.11+incompatible h1:ixHHqfcGvxhWkniF1tWxBHA0yb4Z+d1UQi45df52xW8=
|
||||
github.com/evanphx/json-patch v5.9.11+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
|
||||
|
@ -46,8 +46,8 @@ github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J
|
|||
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
|
||||
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.45.0 h1:niFqYovxuw9vnUB9qoxEgmupqriG7Gns9ZGwB2uuOyE=
|
||||
github.com/grafana/grafana-app-sdk v0.45.0/go.mod h1:1pYGEBrgG8i6pKmmsNXvtAr15jZ4iLtyHU4yj7T6XaI=
|
||||
github.com/grafana/grafana-app-sdk v0.46.0 h1:gvzQvCQgZJ/73BfAcbDt/6TAMhnVikVPxZt/UwDl+oc=
|
||||
github.com/grafana/grafana-app-sdk v0.46.0/go.mod h1:LCTrqR1SwBS13XGVYveBmM7giJDDjzuXK+M9VzPuPWc=
|
||||
github.com/grafana/grafana-app-sdk/logging v0.45.0 h1:0SH6nYZpiLBZRwUq4J6+1vo8xuHKJjnO95/2pGOoA8w=
|
||||
github.com/grafana/grafana-app-sdk/logging v0.45.0/go.mod h1:Gh/nBWnspK3oDNWtiM5qUF/fardHzOIEez+SPI3JeHA=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU=
|
||||
|
|
|
@ -27,6 +27,7 @@ var (
|
|||
var appManifestData = app.ManifestData{
|
||||
AppName: "correlation",
|
||||
Group: "correlations.grafana.app",
|
||||
PreferredVersion: "v0alpha1",
|
||||
Versions: []app.ManifestVersion{
|
||||
{
|
||||
Name: "v0alpha1",
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
|
||||
"github.com/grafana/grafana-app-sdk/app"
|
||||
"github.com/grafana/grafana-app-sdk/logging"
|
||||
"github.com/grafana/grafana-app-sdk/operator"
|
||||
"github.com/grafana/grafana-app-sdk/resource"
|
||||
"github.com/grafana/grafana-app-sdk/simple"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
|
@ -17,10 +18,12 @@ func New(cfg app.Config) (app.App, error) {
|
|||
Name: "correlation",
|
||||
KubeConfig: cfg.KubeConfig,
|
||||
InformerConfig: simple.AppInformerConfig{
|
||||
InformerOptions: operator.InformerOptions{
|
||||
ErrorHandler: func(ctx context.Context, err error) {
|
||||
logging.FromContext(ctx).Error("Informer processing error", "error", err)
|
||||
},
|
||||
},
|
||||
},
|
||||
ManagedKinds: []simple.AppManagedKind{
|
||||
{
|
||||
Kind: correlationsv0alpha1.CorrelationKind(),
|
||||
|
|
|
@ -5,7 +5,7 @@ go 1.24.6
|
|||
require (
|
||||
cuelang.org/go v0.11.1
|
||||
github.com/grafana/authlib/types v0.0.0-20250926065801-df98203cff37
|
||||
github.com/grafana/grafana-app-sdk v0.45.0
|
||||
github.com/grafana/grafana-app-sdk v0.46.0
|
||||
github.com/grafana/grafana-app-sdk/logging v0.45.0
|
||||
github.com/grafana/grafana-plugin-sdk-go v0.279.0
|
||||
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250514132646-acbc7b54ed9e
|
||||
|
@ -31,7 +31,7 @@ require (
|
|||
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/elazarl/goproxy v1.7.2 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.12.2 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.13.0 // indirect
|
||||
github.com/fatih/color v1.18.0 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
|
||||
github.com/getkin/kin-openapi v0.133.0 // indirect
|
||||
|
|
|
@ -36,8 +36,8 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1
|
|||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o=
|
||||
github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE=
|
||||
github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU=
|
||||
github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
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/emicklei/proto v1.13.2 h1:z/etSFO3uyXeuEsVPzfl56WNgzcvIr42aQazXaQmFZY=
|
||||
github.com/emicklei/proto v1.13.2/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A=
|
||||
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
||||
|
@ -101,8 +101,8 @@ github.com/grafana/authlib/types v0.0.0-20250926065801-df98203cff37 h1:qEwZ+7MbP
|
|||
github.com/grafana/authlib/types v0.0.0-20250926065801-df98203cff37/go.mod h1:qeWYbnWzaYGl88JlL9+DsP1GT2Cudm58rLtx13fKZdw=
|
||||
github.com/grafana/dskit v0.0.0-20250908063411-6b6da59b5cc4 h1:jSojuc7njleS3UOz223WDlXOinmuLAIPI0z2vtq8EgI=
|
||||
github.com/grafana/dskit v0.0.0-20250908063411-6b6da59b5cc4/go.mod h1:VahT+GtfQIM+o8ht2StR6J9g+Ef+C2Vokh5uuSmOD/4=
|
||||
github.com/grafana/grafana-app-sdk v0.45.0 h1:niFqYovxuw9vnUB9qoxEgmupqriG7Gns9ZGwB2uuOyE=
|
||||
github.com/grafana/grafana-app-sdk v0.45.0/go.mod h1:1pYGEBrgG8i6pKmmsNXvtAr15jZ4iLtyHU4yj7T6XaI=
|
||||
github.com/grafana/grafana-app-sdk v0.46.0 h1:gvzQvCQgZJ/73BfAcbDt/6TAMhnVikVPxZt/UwDl+oc=
|
||||
github.com/grafana/grafana-app-sdk v0.46.0/go.mod h1:LCTrqR1SwBS13XGVYveBmM7giJDDjzuXK+M9VzPuPWc=
|
||||
github.com/grafana/grafana-app-sdk/logging v0.45.0 h1:0SH6nYZpiLBZRwUq4J6+1vo8xuHKJjnO95/2pGOoA8w=
|
||||
github.com/grafana/grafana-app-sdk/logging v0.45.0/go.mod h1:Gh/nBWnspK3oDNWtiM5qUF/fardHzOIEez+SPI3JeHA=
|
||||
github.com/grafana/grafana-plugin-sdk-go v0.279.0 h1:/KCrsZkj9pEGwIGovqAz1A8rjI2A2YT+ZpvgfZN0LAA=
|
||||
|
|
|
@ -23,6 +23,7 @@ import (
|
|||
var appManifestData = app.ManifestData{
|
||||
AppName: "dashboard",
|
||||
Group: "dashboard.grafana.app",
|
||||
PreferredVersion: "v1beta1",
|
||||
Versions: []app.ManifestVersion{
|
||||
{
|
||||
Name: "v0alpha1",
|
||||
|
|
|
@ -198,7 +198,7 @@ func sortPanelsByGridPos(dashboard map[string]interface{}) {
|
|||
return
|
||||
}
|
||||
|
||||
sort.Slice(panels, func(i, j int) bool {
|
||||
sort.SliceStable(panels, func(i, j int) bool {
|
||||
panelA := panels[i]
|
||||
panelB := panels[j]
|
||||
|
||||
|
@ -831,7 +831,7 @@ func cleanupPanelList(panels []interface{}) {
|
|||
|
||||
// sortPanelsByGridPosition sorts panels by grid position (matches frontend sortPanelsByGridPos behavior)
|
||||
func sortPanelsByGridPosition(panels []interface{}) {
|
||||
sort.Slice(panels, func(i, j int) bool {
|
||||
sort.SliceStable(panels, func(i, j int) bool {
|
||||
panelA, okA := panels[i].(map[string]interface{})
|
||||
panelB, okB := panels[j].(map[string]interface{})
|
||||
if !okA || !okB {
|
||||
|
|
|
@ -49,10 +49,15 @@ func upgradeToGridLayout(dashboard map[string]interface{}) {
|
|||
maxPanelID := getMaxPanelID(rows)
|
||||
nextRowID := maxPanelID + 1
|
||||
|
||||
// Get existing panels
|
||||
var finalPanels []interface{}
|
||||
if existingPanels, ok := dashboard["panels"].([]interface{}); ok {
|
||||
finalPanels = existingPanels
|
||||
// Match frontend: dashboard.panels already exists with top-level panels
|
||||
// The frontend's this.dashboard.panels is initialized in the constructor with existing panels
|
||||
// Then upgradeToGridLayout adds more panels to it
|
||||
|
||||
// Initialize panels array - make a copy to avoid modifying the original
|
||||
panels := []interface{}{}
|
||||
if existingPanels, ok := dashboard["panels"].([]interface{}); ok && len(existingPanels) > 0 {
|
||||
// Copy existing panels to preserve order
|
||||
panels = append(panels, existingPanels...)
|
||||
}
|
||||
|
||||
// Add special "row" panels if even one row is collapsed, repeated or has visible title (line 1028 in TS)
|
||||
|
@ -72,7 +77,14 @@ func upgradeToGridLayout(dashboard map[string]interface{}) {
|
|||
|
||||
height := getRowHeight(row)
|
||||
rowGridHeight := getGridHeight(height)
|
||||
isCollapsed := GetBoolValue(row, "collapse")
|
||||
// Check if collapse property exists and get its value
|
||||
collapseValue, hasCollapseProperty := row["collapse"]
|
||||
isCollapsed := false
|
||||
if hasCollapseProperty {
|
||||
if b, ok := collapseValue.(bool); ok {
|
||||
isCollapsed = b
|
||||
}
|
||||
}
|
||||
|
||||
var rowPanel map[string]interface{}
|
||||
|
||||
|
@ -110,9 +122,9 @@ func upgradeToGridLayout(dashboard map[string]interface{}) {
|
|||
},
|
||||
}
|
||||
|
||||
// Set collapsed property only if the original row had a collapse property
|
||||
// This matches the frontend behavior: rowPanel.collapsed = row.collapse
|
||||
if _, hasCollapse := row["collapse"]; hasCollapse {
|
||||
// Match frontend behavior: rowPanel.collapsed = row.collapse (line 1065 in TS)
|
||||
// Only set collapsed property if the original row had a collapse property
|
||||
if hasCollapseProperty {
|
||||
rowPanel["collapsed"] = isCollapsed
|
||||
}
|
||||
nextRowID++
|
||||
|
@ -128,23 +140,13 @@ func upgradeToGridLayout(dashboard map[string]interface{}) {
|
|||
continue
|
||||
}
|
||||
|
||||
// Set default span (line 1063 in TS)
|
||||
span := GetFloatValue(panel, "span", defaultPanelSpan)
|
||||
|
||||
// Handle minSpan conversion (lines 1064-1066 in TS)
|
||||
if minSpan, hasMinSpan := panel["minSpan"]; hasMinSpan {
|
||||
if minSpanFloat, ok := ConvertToFloat(minSpan); ok && minSpanFloat > 0 {
|
||||
panel["minSpan"] = int(math.Min(float64(gridColumnCount), (float64(gridColumnCount)/12.0)*minSpanFloat))
|
||||
}
|
||||
// Match frontend logic: panel.span = panel.span || DEFAULT_PANEL_SPAN (line 1082 in TS)
|
||||
span := GetFloatValue(panel, "span", 0)
|
||||
if span == 0 {
|
||||
span = defaultPanelSpan
|
||||
}
|
||||
|
||||
panelWidth := int(math.Floor(span * widthFactor))
|
||||
panelHeight := rowGridHeight
|
||||
if panelHeightValue, hasHeight := panel["height"]; hasHeight {
|
||||
if h, ok := ConvertToFloat(panelHeightValue); ok {
|
||||
panelHeight = getGridHeight(h)
|
||||
}
|
||||
}
|
||||
panelWidth, panelHeight := calculatePanelDimensionsFromSpan(span, panel, widthFactor, rowGridHeight)
|
||||
|
||||
panelPos := rowArea.getPanelPosition(panelHeight, panelWidth)
|
||||
yPos = rowArea.yPos
|
||||
|
@ -161,21 +163,21 @@ func upgradeToGridLayout(dashboard map[string]interface{}) {
|
|||
// Remove span (line 1080 in TS)
|
||||
delete(panel, "span")
|
||||
|
||||
// Exact logic from lines 1082-1086 in TS
|
||||
// Match frontend logic: lines 1101-1105 in TS
|
||||
if rowPanel != nil && isCollapsed {
|
||||
// Add to collapsed row's nested panels
|
||||
// Add to collapsed row's nested panels (line 1102)
|
||||
if rowPanelPanels, ok := rowPanel["panels"].([]interface{}); ok {
|
||||
rowPanel["panels"] = append(rowPanelPanels, panel)
|
||||
}
|
||||
} else {
|
||||
// Add directly to dashboard panels
|
||||
finalPanels = append(finalPanels, panel)
|
||||
// Add directly to panels array like frontend (line 1104)
|
||||
panels = append(panels, panel)
|
||||
}
|
||||
}
|
||||
|
||||
// Add row panel after processing all panels (lines 1089-1091 in TS)
|
||||
// Add row panel after regular panels from this row (lines 1108-1110 in TS)
|
||||
if rowPanel != nil {
|
||||
finalPanels = append(finalPanels, rowPanel)
|
||||
panels = append(panels, rowPanel)
|
||||
}
|
||||
|
||||
// Update yPos (lines 1093-1095 in TS)
|
||||
|
@ -185,7 +187,7 @@ func upgradeToGridLayout(dashboard map[string]interface{}) {
|
|||
}
|
||||
|
||||
// Update the dashboard
|
||||
dashboard["panels"] = finalPanels
|
||||
dashboard["panels"] = panels
|
||||
delete(dashboard, "rows")
|
||||
}
|
||||
|
||||
|
@ -315,3 +317,24 @@ func getGridHeight(height float64) int {
|
|||
}
|
||||
return int(math.Ceil(height / panelHeightStep))
|
||||
}
|
||||
|
||||
func calculatePanelDimensionsFromSpan(span float64, panel map[string]interface{}, widthFactor float64, defaultHeight int) (int, int) {
|
||||
// span should already be normalized by caller (line 1082 in DashboardMigrator.ts)
|
||||
|
||||
if minSpan, hasMinSpan := panel["minSpan"]; hasMinSpan {
|
||||
if minSpanFloat, ok := ConvertToFloat(minSpan); ok && minSpanFloat > 0 {
|
||||
panel["minSpan"] = int(math.Min(float64(gridColumnCount), (float64(gridColumnCount)/12.0)*minSpanFloat))
|
||||
}
|
||||
}
|
||||
|
||||
panelWidth := int(math.Floor(span * widthFactor))
|
||||
panelHeight := defaultHeight
|
||||
|
||||
if panelHeightValue, hasHeight := panel["height"]; hasHeight {
|
||||
if h, ok := ConvertToFloat(panelHeightValue); ok {
|
||||
panelHeight = getGridHeight(h)
|
||||
}
|
||||
}
|
||||
|
||||
return panelWidth, panelHeight
|
||||
}
|
||||
|
|
|
@ -1532,6 +1532,123 @@ func TestV16(t *testing.T) {
|
|||
// rows field should be removed
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "should handle span zero by defaulting to DEFAULT_PANEL_SPAN",
|
||||
input: map[string]interface{}{
|
||||
"schemaVersion": 15,
|
||||
"rows": []interface{}{
|
||||
map[string]interface{}{
|
||||
"collapse": false,
|
||||
"showTitle": true, // Need this to create row panel
|
||||
"title": "Test Row",
|
||||
"height": 250,
|
||||
"panels": []interface{}{
|
||||
map[string]interface{}{
|
||||
"id": 1,
|
||||
"type": "graph",
|
||||
"span": 0, // This should be defaulted to 4 (DEFAULT_PANEL_SPAN)
|
||||
},
|
||||
map[string]interface{}{
|
||||
"id": 2,
|
||||
"type": "stat",
|
||||
"span": 6, // Normal span value
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: map[string]interface{}{
|
||||
"schemaVersion": 16,
|
||||
"panels": []interface{}{
|
||||
map[string]interface{}{
|
||||
"id": 1,
|
||||
"type": "graph",
|
||||
"gridPos": map[string]interface{}{
|
||||
"x": 0,
|
||||
"y": 1,
|
||||
"w": 8, // span 0 -> DEFAULT_PANEL_SPAN (4) -> 4 * 2 = 8 width
|
||||
"h": 7, // default height
|
||||
},
|
||||
},
|
||||
map[string]interface{}{
|
||||
"id": 2,
|
||||
"type": "stat",
|
||||
"gridPos": map[string]interface{}{
|
||||
"x": 8, // After first panel
|
||||
"y": 1,
|
||||
"w": 12, // span 6 -> 6 * 2 = 12 width
|
||||
"h": 7, // default height
|
||||
},
|
||||
},
|
||||
// Row panel should be created because showTitle is true
|
||||
map[string]interface{}{
|
||||
"id": 3,
|
||||
"type": "row",
|
||||
"title": "Test Row",
|
||||
"collapsed": false, // Set because input has "collapse": false
|
||||
"repeat": "",
|
||||
"panels": []interface{}{},
|
||||
"gridPos": map[string]interface{}{
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 24,
|
||||
"h": 7,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "should not set collapsed property when input row has no collapse property",
|
||||
input: map[string]interface{}{
|
||||
"schemaVersion": 15,
|
||||
"rows": []interface{}{
|
||||
map[string]interface{}{
|
||||
// No "collapse" property in input
|
||||
"showTitle": true,
|
||||
"title": "Test Row",
|
||||
"height": 250,
|
||||
"panels": []interface{}{
|
||||
map[string]interface{}{
|
||||
"id": 1,
|
||||
"type": "graph",
|
||||
"span": 12,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: map[string]interface{}{
|
||||
"schemaVersion": 16,
|
||||
"panels": []interface{}{
|
||||
map[string]interface{}{
|
||||
"id": 1,
|
||||
"type": "graph",
|
||||
"gridPos": map[string]interface{}{
|
||||
"x": 0,
|
||||
"y": 1,
|
||||
"w": 24, // span 12 -> 12 * 2 = 24 width
|
||||
"h": 7, // default height
|
||||
},
|
||||
},
|
||||
// Row panel should be created because showTitle is true
|
||||
map[string]interface{}{
|
||||
"id": 2,
|
||||
"type": "row",
|
||||
"title": "Test Row",
|
||||
// No "collapsed" property because input had no "collapse" property
|
||||
"repeat": "",
|
||||
"panels": []interface{}{},
|
||||
"gridPos": map[string]interface{}{
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 24,
|
||||
"h": 7,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
runMigrationTests(t, tests, schemaversion.V16)
|
||||
|
|
|
@ -0,0 +1,687 @@
|
|||
{
|
||||
"__requires": [
|
||||
{
|
||||
"id": "grafana",
|
||||
"name": "Grafana",
|
||||
"type": "grafana",
|
||||
"version": "8.0.0"
|
||||
}
|
||||
],
|
||||
"annotations": {
|
||||
"list": []
|
||||
},
|
||||
"editable": false,
|
||||
"gnetId": null,
|
||||
"graphTooltip": 0,
|
||||
"hideControls": false,
|
||||
"links": [
|
||||
{
|
||||
"icon": "external link",
|
||||
"targetBlank": true,
|
||||
"title": "External Documentation",
|
||||
"type": "link",
|
||||
"url": "https://example.com/docs"
|
||||
}
|
||||
],
|
||||
"panels": [
|
||||
{
|
||||
"gridPos": {
|
||||
"h": 3,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"options": {
|
||||
"content": "This dashboard demonstrates various monitoring components for application observability and performance metrics.\n",
|
||||
"mode": "markdown"
|
||||
},
|
||||
"title": "Application Monitoring",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"refresh": "10s",
|
||||
"rows": [
|
||||
{
|
||||
"collapse": false,
|
||||
"collapsed": false,
|
||||
"height": "250px",
|
||||
"panels": [
|
||||
{
|
||||
"gridPos": {
|
||||
"h": 11,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 5
|
||||
},
|
||||
"id": 6,
|
||||
"options": {
|
||||
"content": "This service handles background processing tasks for the application system. It manages various types of operations including data synchronization, resource management, and batch processing.\n\nSupported operation types:\n1. Sync: Synchronizes data between different systems\n2. Process: Handles batch data processing tasks\n3. Cleanup: Removes outdated or temporary resources\n4. Update: Applies configuration changes across services\n\nService dependencies:\n- Data API: For reading and writing application data\n- Configuration Service: For managing system settings\n- Queue Service: For handling task scheduling\n- Storage Service: For persistent data management\n- Auth Service: For authentication and authorization\n- Metrics Service: For collecting operational statistics\n",
|
||||
"mode": "markdown"
|
||||
},
|
||||
"span": 0,
|
||||
"title": "Service Overview",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"gridPos": {
|
||||
"h": 3,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 16
|
||||
},
|
||||
"id": 7,
|
||||
"options": {
|
||||
"content": "Error monitoring helps identify issues in the system. This section displays error logs and success rates for operations.",
|
||||
"mode": "markdown"
|
||||
},
|
||||
"span": 0,
|
||||
"title": "Error Monitoring",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${prom}"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "red",
|
||||
"value": 0
|
||||
},
|
||||
{
|
||||
"color": "yellow",
|
||||
"value": 0.95
|
||||
},
|
||||
{
|
||||
"color": "green",
|
||||
"value": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
"unit": "percentunit"
|
||||
}
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 9,
|
||||
"w": 3,
|
||||
"x": 0,
|
||||
"y": 19
|
||||
},
|
||||
"id": 8,
|
||||
"span": 0,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "sum by (action) (app_jobs_processed_total{outcome=\"success\", cluster=\"$cluster\", namespace=\"default\"})\n/\nsum by (action) (app_jobs_processed_total{cluster=\"$cluster\", namespace=\"default\"})\n",
|
||||
"legendFormat": "{{action}}"
|
||||
}
|
||||
],
|
||||
"title": "Job Success Rate",
|
||||
"type": "stat"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "loki",
|
||||
"uid": "${loki}"
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 9,
|
||||
"w": 10,
|
||||
"x": 3,
|
||||
"y": 19
|
||||
},
|
||||
"id": 9,
|
||||
"options": {
|
||||
"enableLogDetails": true,
|
||||
"showTime": false,
|
||||
"sortOrder": "Descending",
|
||||
"wrapLogMessage": true
|
||||
},
|
||||
"span": 0,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "{namespace=\"default\", cluster=\"$cluster\", job=\"app-service\"} | logfmt | level=\"error\""
|
||||
}
|
||||
],
|
||||
"title": "Errors",
|
||||
"type": "logs"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "loki",
|
||||
"uid": "${loki}"
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 9,
|
||||
"w": 11,
|
||||
"x": 13,
|
||||
"y": 19
|
||||
},
|
||||
"id": 10,
|
||||
"options": {
|
||||
"enableLogDetails": true,
|
||||
"showTime": false,
|
||||
"sortOrder": "Descending",
|
||||
"wrapLogMessage": true
|
||||
},
|
||||
"span": 0,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "{namespace=\"default\", cluster=\"$cluster\", job=\"app-service\"} | logfmt"
|
||||
}
|
||||
],
|
||||
"title": "All",
|
||||
"type": "logs"
|
||||
},
|
||||
{
|
||||
"gridPos": {
|
||||
"h": 3,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 28
|
||||
},
|
||||
"id": 11,
|
||||
"options": {
|
||||
"content": "Performance monitoring examines factors that affect system response times, including operation duration, queue lengths, and processing delays. This section provides metrics and traces for performance analysis.\n",
|
||||
"mode": "markdown"
|
||||
},
|
||||
"span": 0,
|
||||
"title": "Performance Analysis",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${prom}"
|
||||
},
|
||||
"description": "Number of concurrent processing threads available for handling operations",
|
||||
"gridPos": {
|
||||
"h": 6,
|
||||
"w": 5,
|
||||
"x": 0,
|
||||
"y": 31
|
||||
},
|
||||
"id": 12,
|
||||
"span": 0,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "max(app_worker_threads_active{cluster=\"$cluster\", namespace=\"default\"})",
|
||||
"instant": true
|
||||
}
|
||||
],
|
||||
"title": "Concurrent Job Drivers",
|
||||
"type": "stat"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "tempo",
|
||||
"uid": "${tempo}"
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 6,
|
||||
"w": 19,
|
||||
"x": 5,
|
||||
"y": 31
|
||||
},
|
||||
"id": 13,
|
||||
"span": 0,
|
||||
"targets": [
|
||||
{
|
||||
"filters": [
|
||||
{
|
||||
"id": "span-name",
|
||||
"operator": "=",
|
||||
"scope": "span",
|
||||
"tag": "name",
|
||||
"value": [
|
||||
"provisioning.sync.process"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "k8s-cluster-name",
|
||||
"operator": "=",
|
||||
"scope": "resource",
|
||||
"tag": "k8s.cluster.name",
|
||||
"value": [
|
||||
"$cluster"
|
||||
]
|
||||
}
|
||||
],
|
||||
"query": "{name=\"app.operation.process\"}",
|
||||
"queryType": "traceqlSearch"
|
||||
}
|
||||
],
|
||||
"title": "Recent Operation Traces",
|
||||
"type": "table"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${prom}"
|
||||
},
|
||||
"description": "Histogram showing p99, p95, p50, and p10 percentiles for job processing duration based on number of resources changed",
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"value": 0
|
||||
},
|
||||
{
|
||||
"color": "yellow",
|
||||
"value": 2
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 5
|
||||
}
|
||||
]
|
||||
},
|
||||
"unit": "s"
|
||||
}
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 10,
|
||||
"w": 8,
|
||||
"x": 0,
|
||||
"y": 55
|
||||
},
|
||||
"id": 14,
|
||||
"span": 0,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "histogram_quantile(0.99, sum(rate(app_operation_duration_seconds_bucket{cluster=\"$cluster\", namespace=\"default\"}[7d])) by (le, resources_changed_bucket, action)) and on(resources_changed_bucket, action) sum(rate(app_operation_duration_seconds_bucket{cluster=\"$cluster\", namespace=\"default\"}[7d])) by (resources_changed_bucket, action) > 0",
|
||||
"legendFormat": "{{action}} q0.99 - size {{resources_changed_bucket}}",
|
||||
"refId": "B"
|
||||
},
|
||||
{
|
||||
"expr": "histogram_quantile(0.9, sum(rate(app_operation_duration_seconds_bucket{cluster=\"$cluster\", namespace=\"default\"}[7d])) by (le, resources_changed_bucket, action)) and on(resources_changed_bucket, action) sum(rate(app_operation_duration_seconds_bucket{cluster=\"$cluster\", namespace=\"default\"}[7d])) by (resources_changed_bucket, action) > 0",
|
||||
"legendFormat": "{{action}} q0.95 - size {{resources_changed_bucket}}",
|
||||
"refId": "C"
|
||||
},
|
||||
{
|
||||
"expr": "histogram_quantile(0.5, sum(rate(app_operation_duration_seconds_bucket{cluster=\"$cluster\", namespace=\"default\"}[7d])) by (le, resources_changed_bucket, action)) and on(resources_changed_bucket, action) sum(rate(app_operation_duration_seconds_bucket{cluster=\"$cluster\", namespace=\"default\"}[7d])) by (resources_changed_bucket, action) > 0",
|
||||
"legendFormat": "{{action}} q0.5 - size {{resources_changed_bucket}}",
|
||||
"refId": "D"
|
||||
},
|
||||
{
|
||||
"expr": "histogram_quantile(0.1, sum(rate(app_operation_duration_seconds_bucket{cluster=\"$cluster\", namespace=\"default\"}[7d])) by (le, resources_changed_bucket, action)) and on(resources_changed_bucket, action) sum(rate(app_operation_duration_seconds_bucket{cluster=\"$cluster\", namespace=\"default\"}[7d])) by (resources_changed_bucket, action) > 0",
|
||||
"legendFormat": "{{action}} q0.1 - size {{resources_changed_bucket}}",
|
||||
"refId": "E"
|
||||
}
|
||||
],
|
||||
"timeFrom": "7d",
|
||||
"title": "7d avg of job durations",
|
||||
"transformations": [
|
||||
{
|
||||
"id": "reduce",
|
||||
"options": {
|
||||
"mode": "seriesToRows",
|
||||
"reducers": [
|
||||
"mean"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "seriesToRows"
|
||||
},
|
||||
{
|
||||
"id": "organize",
|
||||
"options": {
|
||||
"renameByName": {
|
||||
"Field": "Type",
|
||||
"Mean": "Avg Duration",
|
||||
"Metric": "Legend",
|
||||
"Value": "Duration"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"type": "table"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${prom}"
|
||||
},
|
||||
"description": "Histogram showing p99, p95, p50, and p10 percentiles for job processing duration based on number of resources changed",
|
||||
"gridPos": {
|
||||
"h": 10,
|
||||
"w": 16,
|
||||
"x": 8,
|
||||
"y": 55
|
||||
},
|
||||
"id": 15,
|
||||
"span": 0,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "histogram_quantile(0.99, sum(rate(app_operation_duration_seconds_bucket{cluster=\"$cluster\", namespace=\"default\"}[5m])) by (le, resources_changed_bucket, action))",
|
||||
"legendFormat": "{{action}} q0.99 - size {{resources_changed_bucket}}",
|
||||
"refId": "B"
|
||||
},
|
||||
{
|
||||
"expr": "histogram_quantile(0.95, sum(rate(app_operation_duration_seconds_bucket{cluster=\"$cluster\", namespace=\"default\"}[5m])) by (le, resources_changed_bucket, action))",
|
||||
"legendFormat": "{{action}} q0.95 - size {{resources_changed_bucket}}",
|
||||
"refId": "C"
|
||||
},
|
||||
{
|
||||
"expr": "histogram_quantile(0.5, sum(rate(app_operation_duration_seconds_bucket{cluster=\"$cluster\", namespace=\"default\"}[5m])) by (le, resources_changed_bucket, action))",
|
||||
"legendFormat": "{{action}} q0.5 - size {{resources_changed_bucket}}",
|
||||
"refId": "D"
|
||||
},
|
||||
{
|
||||
"expr": "histogram_quantile(0.1, sum(rate(app_operation_duration_seconds_bucket{cluster=\"$cluster\", namespace=\"default\"}[5m])) by (le, resources_changed_bucket, action))",
|
||||
"legendFormat": "{{action}} q0.1 - size {{resources_changed_bucket}}",
|
||||
"refId": "E"
|
||||
}
|
||||
],
|
||||
"title": "Job Duration",
|
||||
"type": "timeseries"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${prom}"
|
||||
},
|
||||
"description": "Total number of jobs waiting to be processed",
|
||||
"gridPos": {
|
||||
"h": 5,
|
||||
"w": 4,
|
||||
"x": 0,
|
||||
"y": 65
|
||||
},
|
||||
"id": 16,
|
||||
"span": 0,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "clamp_min(sum(app_operation_queue_size{cluster=\"$cluster\", namespace=\"default\"}), 0)",
|
||||
"legendFormat": "Queue size"
|
||||
}
|
||||
],
|
||||
"title": "Queue Size",
|
||||
"type": "stat"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${prom}"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"unit": "s"
|
||||
}
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 5,
|
||||
"w": 4,
|
||||
"x": 4,
|
||||
"y": 65
|
||||
},
|
||||
"id": 17,
|
||||
"span": 0,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "avg(histogram_quantile(0.5, sum(rate(app_operation_queue_wait_seconds_bucket{cluster=\"$cluster\", namespace=\"default\"}[7d])) by (le)))",
|
||||
"legendFormat": "Queue size"
|
||||
}
|
||||
],
|
||||
"timeFrom": "7d",
|
||||
"title": "7d avg Queue Wait Time",
|
||||
"type": "stat"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${prom}"
|
||||
},
|
||||
"description": "How long a job is in the queue before being picked up",
|
||||
"gridPos": {
|
||||
"h": 5,
|
||||
"w": 16,
|
||||
"x": 8,
|
||||
"y": 65
|
||||
},
|
||||
"id": 18,
|
||||
"span": 0,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "histogram_quantile(0.99, sum(rate(app_operation_queue_wait_seconds_bucket{cluster=\"$cluster\", namespace=\"default\"}[$__rate_interval])) by (le))",
|
||||
"legendFormat": "q0.99",
|
||||
"refId": "B"
|
||||
},
|
||||
{
|
||||
"expr": "histogram_quantile(0.95, sum(rate(app_operation_queue_wait_seconds_bucket{cluster=\"$cluster\", namespace=\"default\"}[$__rate_interval])) by (le))",
|
||||
"legendFormat": "q0.95",
|
||||
"refId": "C"
|
||||
},
|
||||
{
|
||||
"expr": "histogram_quantile(0.5, sum(rate(app_operation_queue_wait_seconds_bucket{cluster=\"$cluster\", namespace=\"default\"}[$__rate_interval])) by (le))",
|
||||
"legendFormat": "q0.5",
|
||||
"refId": "D"
|
||||
},
|
||||
{
|
||||
"expr": "histogram_quantile(0.1, sum(rate(app_operation_queue_wait_seconds_bucket{cluster=\"$cluster\", namespace=\"default\"}[$__rate_interval])) by (le))",
|
||||
"legendFormat": "q0.1",
|
||||
"refId": "E"
|
||||
}
|
||||
],
|
||||
"title": "Queue Wait Time",
|
||||
"type": "timeseries"
|
||||
},
|
||||
{
|
||||
"gridPos": {
|
||||
"h": 3,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 52
|
||||
},
|
||||
"id": 19,
|
||||
"options": {
|
||||
"content": "Resource utilization monitoring for application containers",
|
||||
"mode": "markdown"
|
||||
},
|
||||
"span": 0,
|
||||
"title": "Resource Monitoring",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${prom}"
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 9,
|
||||
"w": 7,
|
||||
"x": 0,
|
||||
"y": 55
|
||||
},
|
||||
"id": 20,
|
||||
"span": 0,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "count by (cluster, channel)(label_replace(label_replace(kube_pod_container_info{namespace=\"default\", container=\"app-worker\", pod=~\"app-worker.*\", cluster=~\"$cluster\"}, \"version\", \"$1\", \"image\", \".+:(.+)\"), \"channel\", \"$1\", \"container\", \".+-(.+)\"))",
|
||||
"legendFormat": "{{cluster}}"
|
||||
}
|
||||
],
|
||||
"title": "Running Pod(s)",
|
||||
"type": "timeseries"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${prom}"
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 9,
|
||||
"w": 8,
|
||||
"x": 7,
|
||||
"y": 55
|
||||
},
|
||||
"id": 21,
|
||||
"span": 0,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "max(kube_pod_container_resource_requests{namespace=\"default\", resource=\"memory\", cluster=~\"$cluster\", container=\"app-worker\", pod=~\"app-worker.*\"})",
|
||||
"legendFormat": "Memory Request"
|
||||
},
|
||||
{
|
||||
"expr": "max(kube_pod_container_resource_limits{namespace=\"default\", resource=\"memory\", cluster=~\"$cluster\", container=\"app-worker\", pod=~\"app-worker.*\"})",
|
||||
"legendFormat": "Memory Limit"
|
||||
},
|
||||
{
|
||||
"expr": "max(container_memory_usage_bytes{namespace=\"default\",cluster=~\"$cluster\", container=\"app-worker\", pod=~\"app-worker.*\"}) by (pod)",
|
||||
"legendFormat": "Container usage {{pod}}"
|
||||
}
|
||||
],
|
||||
"title": "Memory Utilization",
|
||||
"type": "timeseries"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${prom}"
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 9,
|
||||
"w": 9,
|
||||
"x": 15,
|
||||
"y": 55
|
||||
},
|
||||
"id": 22,
|
||||
"span": 0,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "sum(irate(container_cpu_usage_seconds_total{namespace=\"default\", cluster=~\"$cluster\", container=\"app-worker\", pod=~\"app-worker-.*\"}[$__rate_interval])) by (pod, container, cpu)",
|
||||
"legendFormat": "Usage {{pod}}"
|
||||
},
|
||||
{
|
||||
"expr": "sum(irate(container_cpu_cfs_throttled_seconds_total{namespace=\"default\", cluster=~\"$cluster\", container=\"app-worker\", pod=~\"app-worker-.*\"}[$__rate_interval])) by (pod, container)",
|
||||
"legendFormat": "Throttling {{pod}}"
|
||||
},
|
||||
{
|
||||
"expr": "max(kube_pod_container_resource_limits{namespace=\"default\", cluster=~\"$cluster\", container=\"app-worker\", pod=~\"app-worker-.*\", resource=\"cpu\"})",
|
||||
"legendFormat": "CPU limit"
|
||||
},
|
||||
{
|
||||
"expr": "max(kube_pod_container_resource_requests{namespace=\"default\", cluster=~\"$cluster\", container=\"app-worker\", pod=~\"app-worker-.*\", resource=\"cpu\"})",
|
||||
"legendFormat": "CPU request"
|
||||
}
|
||||
],
|
||||
"title": "CPU Utilization",
|
||||
"type": "timeseries"
|
||||
}
|
||||
],
|
||||
"repeat": null,
|
||||
"repeatIteration": null,
|
||||
"repeatRowId": null,
|
||||
"showTitle": true,
|
||||
"title": "Application Service",
|
||||
"titleSize": "h6"
|
||||
}
|
||||
],
|
||||
"schemaVersion": 15,
|
||||
"style": "dark",
|
||||
"tags": [
|
||||
"as-code"
|
||||
],
|
||||
"templating": {
|
||||
"list": [
|
||||
{
|
||||
"current": {
|
||||
"value": "prometheus-datasource"
|
||||
},
|
||||
"hide": 0,
|
||||
"label": "Data source",
|
||||
"name": "datasource",
|
||||
"options": [],
|
||||
"query": "prometheus",
|
||||
"refresh": 1,
|
||||
"regex": "",
|
||||
"type": "datasource"
|
||||
},
|
||||
{
|
||||
"current": {
|
||||
"value": "prometheus-datasource"
|
||||
},
|
||||
"name": "prom",
|
||||
"query": "prometheus",
|
||||
"refresh": 1,
|
||||
"regex": "",
|
||||
"type": "datasource"
|
||||
},
|
||||
{
|
||||
"current": {
|
||||
"value": "loki-datasource"
|
||||
},
|
||||
"name": "loki",
|
||||
"query": "loki",
|
||||
"refresh": 1,
|
||||
"regex": "",
|
||||
"type": "datasource"
|
||||
},
|
||||
{
|
||||
"current": {
|
||||
"text": "tempo-datasource",
|
||||
"value": "tempo-datasource"
|
||||
},
|
||||
"name": "tempo",
|
||||
"query": "tempo",
|
||||
"refresh": 1,
|
||||
"regex": ".*tempo.*",
|
||||
"type": "datasource"
|
||||
},
|
||||
{
|
||||
"current": {
|
||||
"text": "demo-cluster",
|
||||
"value": "demo-cluster"
|
||||
},
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${prom}"
|
||||
},
|
||||
"name": "cluster",
|
||||
"query": "label_values(app_worker_threads_active,cluster)",
|
||||
"refresh": 1,
|
||||
"type": "query"
|
||||
}
|
||||
]
|
||||
},
|
||||
"time": {
|
||||
"from": "now-6h",
|
||||
"to": "now"
|
||||
},
|
||||
"timepicker": {
|
||||
"refresh_intervals": [
|
||||
"5s",
|
||||
"10s",
|
||||
"30s",
|
||||
"1m",
|
||||
"5m",
|
||||
"15m",
|
||||
"30m",
|
||||
"1h",
|
||||
"2h",
|
||||
"1d"
|
||||
],
|
||||
"time_options": [
|
||||
"5m",
|
||||
"15m",
|
||||
"1h",
|
||||
"6h",
|
||||
"12h",
|
||||
"24h",
|
||||
"2d",
|
||||
"7d",
|
||||
"30d"
|
||||
]
|
||||
},
|
||||
"timezone": "utc",
|
||||
"title": "Span Zero Demo Dashboard",
|
||||
"uid": "span-zero-demo-dashboard",
|
||||
"version": 0
|
||||
}
|
881
apps/dashboard/pkg/migration/testdata/output/latest_version/v16.span_zero_demo.v42.json
vendored
Normal file
881
apps/dashboard/pkg/migration/testdata/output/latest_version/v16.span_zero_demo.v42.json
vendored
Normal file
|
@ -0,0 +1,881 @@
|
|||
{
|
||||
"annotations": {
|
||||
"list": [
|
||||
{
|
||||
"builtIn": 1,
|
||||
"datasource": {
|
||||
"type": "grafana",
|
||||
"uid": "-- Grafana --"
|
||||
},
|
||||
"enable": true,
|
||||
"hide": true,
|
||||
"iconColor": "rgba(0, 211, 255, 1)",
|
||||
"name": "Annotations \u0026 Alerts",
|
||||
"type": "dashboard"
|
||||
}
|
||||
]
|
||||
},
|
||||
"editable": false,
|
||||
"fiscalYearStartMonth": 0,
|
||||
"graphTooltip": 0,
|
||||
"links": [
|
||||
{
|
||||
"icon": "external link",
|
||||
"targetBlank": true,
|
||||
"title": "External Documentation",
|
||||
"type": "link",
|
||||
"url": "https://example.com/docs"
|
||||
}
|
||||
],
|
||||
"panels": [
|
||||
{
|
||||
"datasource": {
|
||||
"apiVersion": "v1",
|
||||
"type": "prometheus",
|
||||
"uid": "default-ds-uid"
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 3,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"id": 1,
|
||||
"options": {
|
||||
"content": "This dashboard demonstrates various monitoring components for application observability and performance metrics.\n",
|
||||
"mode": "markdown"
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"apiVersion": "v1",
|
||||
"type": "prometheus",
|
||||
"uid": "default-ds-uid"
|
||||
},
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Application Monitoring",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"collapsed": false,
|
||||
"datasource": {
|
||||
"apiVersion": "v1",
|
||||
"type": "prometheus",
|
||||
"uid": "default-ds-uid"
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"id": 23,
|
||||
"panels": [],
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"apiVersion": "v1",
|
||||
"type": "prometheus",
|
||||
"uid": "default-ds-uid"
|
||||
},
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Application Service",
|
||||
"type": "row"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"apiVersion": "v1",
|
||||
"type": "prometheus",
|
||||
"uid": "default-ds-uid"
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 8,
|
||||
"x": 0,
|
||||
"y": 1
|
||||
},
|
||||
"id": 6,
|
||||
"options": {
|
||||
"content": "This service handles background processing tasks for the application system. It manages various types of operations including data synchronization, resource management, and batch processing.\n\nSupported operation types:\n1. Sync: Synchronizes data between different systems\n2. Process: Handles batch data processing tasks\n3. Cleanup: Removes outdated or temporary resources\n4. Update: Applies configuration changes across services\n\nService dependencies:\n- Data API: For reading and writing application data\n- Configuration Service: For managing system settings\n- Queue Service: For handling task scheduling\n- Storage Service: For persistent data management\n- Auth Service: For authentication and authorization\n- Metrics Service: For collecting operational statistics\n",
|
||||
"mode": "markdown"
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"apiVersion": "v1",
|
||||
"type": "prometheus",
|
||||
"uid": "default-ds-uid"
|
||||
},
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Service Overview",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"apiVersion": "v1",
|
||||
"type": "prometheus",
|
||||
"uid": "default-ds-uid"
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 8,
|
||||
"x": 8,
|
||||
"y": 1
|
||||
},
|
||||
"id": 7,
|
||||
"options": {
|
||||
"content": "Error monitoring helps identify issues in the system. This section displays error logs and success rates for operations.",
|
||||
"mode": "markdown"
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"apiVersion": "v1",
|
||||
"type": "prometheus",
|
||||
"uid": "default-ds-uid"
|
||||
},
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Error Monitoring",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${prom}"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "red",
|
||||
"value": 0
|
||||
},
|
||||
{
|
||||
"color": "yellow",
|
||||
"value": 0.95
|
||||
},
|
||||
{
|
||||
"color": "green",
|
||||
"value": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
"unit": "percentunit"
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 8,
|
||||
"x": 16,
|
||||
"y": 1
|
||||
},
|
||||
"id": 8,
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${prom}"
|
||||
},
|
||||
"expr": "sum by (action) (app_jobs_processed_total{outcome=\"success\", cluster=\"$cluster\", namespace=\"default\"})\n/\nsum by (action) (app_jobs_processed_total{cluster=\"$cluster\", namespace=\"default\"})\n",
|
||||
"legendFormat": "{{action}}",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Job Success Rate",
|
||||
"type": "stat"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "loki",
|
||||
"uid": "${loki}"
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 8,
|
||||
"x": 0,
|
||||
"y": 8
|
||||
},
|
||||
"id": 9,
|
||||
"options": {
|
||||
"enableLogDetails": true,
|
||||
"showTime": false,
|
||||
"sortOrder": "Descending",
|
||||
"wrapLogMessage": true
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "loki",
|
||||
"uid": "${loki}"
|
||||
},
|
||||
"expr": "{namespace=\"default\", cluster=\"$cluster\", job=\"app-service\"} | logfmt | level=\"error\"",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Errors",
|
||||
"type": "logs"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "loki",
|
||||
"uid": "${loki}"
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 8,
|
||||
"x": 8,
|
||||
"y": 8
|
||||
},
|
||||
"id": 10,
|
||||
"options": {
|
||||
"enableLogDetails": true,
|
||||
"showTime": false,
|
||||
"sortOrder": "Descending",
|
||||
"wrapLogMessage": true
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "loki",
|
||||
"uid": "${loki}"
|
||||
},
|
||||
"expr": "{namespace=\"default\", cluster=\"$cluster\", job=\"app-service\"} | logfmt",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "All",
|
||||
"type": "logs"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"apiVersion": "v1",
|
||||
"type": "prometheus",
|
||||
"uid": "default-ds-uid"
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 8,
|
||||
"x": 16,
|
||||
"y": 8
|
||||
},
|
||||
"id": 11,
|
||||
"options": {
|
||||
"content": "Performance monitoring examines factors that affect system response times, including operation duration, queue lengths, and processing delays. This section provides metrics and traces for performance analysis.\n",
|
||||
"mode": "markdown"
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"apiVersion": "v1",
|
||||
"type": "prometheus",
|
||||
"uid": "default-ds-uid"
|
||||
},
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Performance Analysis",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${prom}"
|
||||
},
|
||||
"description": "Number of concurrent processing threads available for handling operations",
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 8,
|
||||
"x": 0,
|
||||
"y": 15
|
||||
},
|
||||
"id": 12,
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${prom}"
|
||||
},
|
||||
"expr": "max(app_worker_threads_active{cluster=\"$cluster\", namespace=\"default\"})",
|
||||
"instant": true,
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Concurrent Job Drivers",
|
||||
"type": "stat"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "tempo",
|
||||
"uid": "${tempo}"
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 8,
|
||||
"x": 8,
|
||||
"y": 15
|
||||
},
|
||||
"id": 13,
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "tempo",
|
||||
"uid": "${tempo}"
|
||||
},
|
||||
"filters": [
|
||||
{
|
||||
"id": "span-name",
|
||||
"operator": "=",
|
||||
"scope": "span",
|
||||
"tag": "name",
|
||||
"value": [
|
||||
"provisioning.sync.process"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "k8s-cluster-name",
|
||||
"operator": "=",
|
||||
"scope": "resource",
|
||||
"tag": "k8s.cluster.name",
|
||||
"value": [
|
||||
"$cluster"
|
||||
]
|
||||
}
|
||||
],
|
||||
"query": "{name=\"app.operation.process\"}",
|
||||
"queryType": "traceqlSearch",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Recent Operation Traces",
|
||||
"type": "table"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${prom}"
|
||||
},
|
||||
"description": "Histogram showing p99, p95, p50, and p10 percentiles for job processing duration based on number of resources changed",
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"value": 0
|
||||
},
|
||||
{
|
||||
"color": "yellow",
|
||||
"value": 2
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 5
|
||||
}
|
||||
]
|
||||
},
|
||||
"unit": "s"
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 8,
|
||||
"x": 16,
|
||||
"y": 15
|
||||
},
|
||||
"id": 14,
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${prom}"
|
||||
},
|
||||
"expr": "histogram_quantile(0.99, sum(rate(app_operation_duration_seconds_bucket{cluster=\"$cluster\", namespace=\"default\"}[7d])) by (le, resources_changed_bucket, action)) and on(resources_changed_bucket, action) sum(rate(app_operation_duration_seconds_bucket{cluster=\"$cluster\", namespace=\"default\"}[7d])) by (resources_changed_bucket, action) \u003e 0",
|
||||
"legendFormat": "{{action}} q0.99 - size {{resources_changed_bucket}}",
|
||||
"refId": "B"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${prom}"
|
||||
},
|
||||
"expr": "histogram_quantile(0.9, sum(rate(app_operation_duration_seconds_bucket{cluster=\"$cluster\", namespace=\"default\"}[7d])) by (le, resources_changed_bucket, action)) and on(resources_changed_bucket, action) sum(rate(app_operation_duration_seconds_bucket{cluster=\"$cluster\", namespace=\"default\"}[7d])) by (resources_changed_bucket, action) \u003e 0",
|
||||
"legendFormat": "{{action}} q0.95 - size {{resources_changed_bucket}}",
|
||||
"refId": "C"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${prom}"
|
||||
},
|
||||
"expr": "histogram_quantile(0.5, sum(rate(app_operation_duration_seconds_bucket{cluster=\"$cluster\", namespace=\"default\"}[7d])) by (le, resources_changed_bucket, action)) and on(resources_changed_bucket, action) sum(rate(app_operation_duration_seconds_bucket{cluster=\"$cluster\", namespace=\"default\"}[7d])) by (resources_changed_bucket, action) \u003e 0",
|
||||
"legendFormat": "{{action}} q0.5 - size {{resources_changed_bucket}}",
|
||||
"refId": "D"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${prom}"
|
||||
},
|
||||
"expr": "histogram_quantile(0.1, sum(rate(app_operation_duration_seconds_bucket{cluster=\"$cluster\", namespace=\"default\"}[7d])) by (le, resources_changed_bucket, action)) and on(resources_changed_bucket, action) sum(rate(app_operation_duration_seconds_bucket{cluster=\"$cluster\", namespace=\"default\"}[7d])) by (resources_changed_bucket, action) \u003e 0",
|
||||
"legendFormat": "{{action}} q0.1 - size {{resources_changed_bucket}}",
|
||||
"refId": "E"
|
||||
}
|
||||
],
|
||||
"timeFrom": "7d",
|
||||
"title": "7d avg of job durations",
|
||||
"transformations": [
|
||||
{
|
||||
"id": "reduce",
|
||||
"options": {
|
||||
"mode": "seriesToRows",
|
||||
"reducers": [
|
||||
"mean"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "seriesToRows"
|
||||
},
|
||||
{
|
||||
"id": "organize",
|
||||
"options": {
|
||||
"renameByName": {
|
||||
"Field": "Type",
|
||||
"Mean": "Avg Duration",
|
||||
"Metric": "Legend",
|
||||
"Value": "Duration"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"type": "table"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${prom}"
|
||||
},
|
||||
"description": "Histogram showing p99, p95, p50, and p10 percentiles for job processing duration based on number of resources changed",
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 8,
|
||||
"x": 0,
|
||||
"y": 22
|
||||
},
|
||||
"id": 15,
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${prom}"
|
||||
},
|
||||
"expr": "histogram_quantile(0.99, sum(rate(app_operation_duration_seconds_bucket{cluster=\"$cluster\", namespace=\"default\"}[5m])) by (le, resources_changed_bucket, action))",
|
||||
"legendFormat": "{{action}} q0.99 - size {{resources_changed_bucket}}",
|
||||
"refId": "B"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${prom}"
|
||||
},
|
||||
"expr": "histogram_quantile(0.95, sum(rate(app_operation_duration_seconds_bucket{cluster=\"$cluster\", namespace=\"default\"}[5m])) by (le, resources_changed_bucket, action))",
|
||||
"legendFormat": "{{action}} q0.95 - size {{resources_changed_bucket}}",
|
||||
"refId": "C"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${prom}"
|
||||
},
|
||||
"expr": "histogram_quantile(0.5, sum(rate(app_operation_duration_seconds_bucket{cluster=\"$cluster\", namespace=\"default\"}[5m])) by (le, resources_changed_bucket, action))",
|
||||
"legendFormat": "{{action}} q0.5 - size {{resources_changed_bucket}}",
|
||||
"refId": "D"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${prom}"
|
||||
},
|
||||
"expr": "histogram_quantile(0.1, sum(rate(app_operation_duration_seconds_bucket{cluster=\"$cluster\", namespace=\"default\"}[5m])) by (le, resources_changed_bucket, action))",
|
||||
"legendFormat": "{{action}} q0.1 - size {{resources_changed_bucket}}",
|
||||
"refId": "E"
|
||||
}
|
||||
],
|
||||
"title": "Job Duration",
|
||||
"type": "timeseries"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${prom}"
|
||||
},
|
||||
"description": "Total number of jobs waiting to be processed",
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 8,
|
||||
"x": 8,
|
||||
"y": 22
|
||||
},
|
||||
"id": 16,
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${prom}"
|
||||
},
|
||||
"expr": "clamp_min(sum(app_operation_queue_size{cluster=\"$cluster\", namespace=\"default\"}), 0)",
|
||||
"legendFormat": "Queue size",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Queue Size",
|
||||
"type": "stat"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${prom}"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"unit": "s"
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 8,
|
||||
"x": 16,
|
||||
"y": 22
|
||||
},
|
||||
"id": 17,
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${prom}"
|
||||
},
|
||||
"expr": "avg(histogram_quantile(0.5, sum(rate(app_operation_queue_wait_seconds_bucket{cluster=\"$cluster\", namespace=\"default\"}[7d])) by (le)))",
|
||||
"legendFormat": "Queue size",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"timeFrom": "7d",
|
||||
"title": "7d avg Queue Wait Time",
|
||||
"type": "stat"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${prom}"
|
||||
},
|
||||
"description": "How long a job is in the queue before being picked up",
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 8,
|
||||
"x": 0,
|
||||
"y": 29
|
||||
},
|
||||
"id": 18,
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${prom}"
|
||||
},
|
||||
"expr": "histogram_quantile(0.99, sum(rate(app_operation_queue_wait_seconds_bucket{cluster=\"$cluster\", namespace=\"default\"}[$__rate_interval])) by (le))",
|
||||
"legendFormat": "q0.99",
|
||||
"refId": "B"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${prom}"
|
||||
},
|
||||
"expr": "histogram_quantile(0.95, sum(rate(app_operation_queue_wait_seconds_bucket{cluster=\"$cluster\", namespace=\"default\"}[$__rate_interval])) by (le))",
|
||||
"legendFormat": "q0.95",
|
||||
"refId": "C"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${prom}"
|
||||
},
|
||||
"expr": "histogram_quantile(0.5, sum(rate(app_operation_queue_wait_seconds_bucket{cluster=\"$cluster\", namespace=\"default\"}[$__rate_interval])) by (le))",
|
||||
"legendFormat": "q0.5",
|
||||
"refId": "D"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${prom}"
|
||||
},
|
||||
"expr": "histogram_quantile(0.1, sum(rate(app_operation_queue_wait_seconds_bucket{cluster=\"$cluster\", namespace=\"default\"}[$__rate_interval])) by (le))",
|
||||
"legendFormat": "q0.1",
|
||||
"refId": "E"
|
||||
}
|
||||
],
|
||||
"title": "Queue Wait Time",
|
||||
"type": "timeseries"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"apiVersion": "v1",
|
||||
"type": "prometheus",
|
||||
"uid": "default-ds-uid"
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 8,
|
||||
"x": 8,
|
||||
"y": 29
|
||||
},
|
||||
"id": 19,
|
||||
"options": {
|
||||
"content": "Resource utilization monitoring for application containers",
|
||||
"mode": "markdown"
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"apiVersion": "v1",
|
||||
"type": "prometheus",
|
||||
"uid": "default-ds-uid"
|
||||
},
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Resource Monitoring",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${prom}"
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 8,
|
||||
"x": 16,
|
||||
"y": 29
|
||||
},
|
||||
"id": 20,
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${prom}"
|
||||
},
|
||||
"expr": "count by (cluster, channel)(label_replace(label_replace(kube_pod_container_info{namespace=\"default\", container=\"app-worker\", pod=~\"app-worker.*\", cluster=~\"$cluster\"}, \"version\", \"$1\", \"image\", \".+:(.+)\"), \"channel\", \"$1\", \"container\", \".+-(.+)\"))",
|
||||
"legendFormat": "{{cluster}}",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Running Pod(s)",
|
||||
"type": "timeseries"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${prom}"
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 8,
|
||||
"x": 0,
|
||||
"y": 36
|
||||
},
|
||||
"id": 21,
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${prom}"
|
||||
},
|
||||
"expr": "max(kube_pod_container_resource_requests{namespace=\"default\", resource=\"memory\", cluster=~\"$cluster\", container=\"app-worker\", pod=~\"app-worker.*\"})",
|
||||
"legendFormat": "Memory Request",
|
||||
"refId": "A"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${prom}"
|
||||
},
|
||||
"expr": "max(kube_pod_container_resource_limits{namespace=\"default\", resource=\"memory\", cluster=~\"$cluster\", container=\"app-worker\", pod=~\"app-worker.*\"})",
|
||||
"legendFormat": "Memory Limit",
|
||||
"refId": "B"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${prom}"
|
||||
},
|
||||
"expr": "max(container_memory_usage_bytes{namespace=\"default\",cluster=~\"$cluster\", container=\"app-worker\", pod=~\"app-worker.*\"}) by (pod)",
|
||||
"legendFormat": "Container usage {{pod}}",
|
||||
"refId": "C"
|
||||
}
|
||||
],
|
||||
"title": "Memory Utilization",
|
||||
"type": "timeseries"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${prom}"
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 8,
|
||||
"x": 8,
|
||||
"y": 36
|
||||
},
|
||||
"id": 22,
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${prom}"
|
||||
},
|
||||
"expr": "sum(irate(container_cpu_usage_seconds_total{namespace=\"default\", cluster=~\"$cluster\", container=\"app-worker\", pod=~\"app-worker-.*\"}[$__rate_interval])) by (pod, container, cpu)",
|
||||
"legendFormat": "Usage {{pod}}",
|
||||
"refId": "A"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${prom}"
|
||||
},
|
||||
"expr": "sum(irate(container_cpu_cfs_throttled_seconds_total{namespace=\"default\", cluster=~\"$cluster\", container=\"app-worker\", pod=~\"app-worker-.*\"}[$__rate_interval])) by (pod, container)",
|
||||
"legendFormat": "Throttling {{pod}}",
|
||||
"refId": "B"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${prom}"
|
||||
},
|
||||
"expr": "max(kube_pod_container_resource_limits{namespace=\"default\", cluster=~\"$cluster\", container=\"app-worker\", pod=~\"app-worker-.*\", resource=\"cpu\"})",
|
||||
"legendFormat": "CPU limit",
|
||||
"refId": "C"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${prom}"
|
||||
},
|
||||
"expr": "max(kube_pod_container_resource_requests{namespace=\"default\", cluster=~\"$cluster\", container=\"app-worker\", pod=~\"app-worker-.*\", resource=\"cpu\"})",
|
||||
"legendFormat": "CPU request",
|
||||
"refId": "D"
|
||||
}
|
||||
],
|
||||
"title": "CPU Utilization",
|
||||
"type": "timeseries"
|
||||
}
|
||||
],
|
||||
"refresh": "10s",
|
||||
"schemaVersion": 42,
|
||||
"tags": [
|
||||
"as-code"
|
||||
],
|
||||
"templating": {
|
||||
"list": [
|
||||
{
|
||||
"current": {
|
||||
"value": "prometheus-datasource"
|
||||
},
|
||||
"hide": 0,
|
||||
"label": "Data source",
|
||||
"name": "datasource",
|
||||
"options": [],
|
||||
"query": "prometheus",
|
||||
"refresh": 1,
|
||||
"regex": "",
|
||||
"type": "datasource"
|
||||
},
|
||||
{
|
||||
"current": {
|
||||
"value": "prometheus-datasource"
|
||||
},
|
||||
"name": "prom",
|
||||
"options": [],
|
||||
"query": "prometheus",
|
||||
"refresh": 1,
|
||||
"regex": "",
|
||||
"type": "datasource"
|
||||
},
|
||||
{
|
||||
"current": {
|
||||
"value": "loki-datasource"
|
||||
},
|
||||
"name": "loki",
|
||||
"options": [],
|
||||
"query": "loki",
|
||||
"refresh": 1,
|
||||
"regex": "",
|
||||
"type": "datasource"
|
||||
},
|
||||
{
|
||||
"current": {
|
||||
"text": "tempo-datasource",
|
||||
"value": "tempo-datasource"
|
||||
},
|
||||
"name": "tempo",
|
||||
"options": [],
|
||||
"query": "tempo",
|
||||
"refresh": 1,
|
||||
"regex": ".*tempo.*",
|
||||
"type": "datasource"
|
||||
},
|
||||
{
|
||||
"current": {
|
||||
"text": "demo-cluster",
|
||||
"value": "demo-cluster"
|
||||
},
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${prom}"
|
||||
},
|
||||
"name": "cluster",
|
||||
"options": [],
|
||||
"query": "label_values(app_worker_threads_active,cluster)",
|
||||
"refresh": 1,
|
||||
"type": "query"
|
||||
}
|
||||
]
|
||||
},
|
||||
"time": {
|
||||
"from": "now-6h",
|
||||
"to": "now"
|
||||
},
|
||||
"timepicker": {
|
||||
"refresh_intervals": [
|
||||
"5s",
|
||||
"10s",
|
||||
"30s",
|
||||
"1m",
|
||||
"5m",
|
||||
"15m",
|
||||
"30m",
|
||||
"1h",
|
||||
"2h",
|
||||
"1d"
|
||||
]
|
||||
},
|
||||
"timezone": "utc",
|
||||
"title": "Span Zero Demo Dashboard",
|
||||
"uid": "span-zero-demo-dashboard",
|
||||
"weekStart": ""
|
||||
}
|
694
apps/dashboard/pkg/migration/testdata/output/single_version/v16.span_zero_demo.v16.json
vendored
Normal file
694
apps/dashboard/pkg/migration/testdata/output/single_version/v16.span_zero_demo.v16.json
vendored
Normal file
|
@ -0,0 +1,694 @@
|
|||
{
|
||||
"annotations": {
|
||||
"list": [
|
||||
{
|
||||
"builtIn": 1,
|
||||
"datasource": {
|
||||
"type": "grafana",
|
||||
"uid": "-- Grafana --"
|
||||
},
|
||||
"enable": true,
|
||||
"hide": true,
|
||||
"iconColor": "rgba(0, 211, 255, 1)",
|
||||
"name": "Annotations \u0026 Alerts",
|
||||
"type": "dashboard"
|
||||
}
|
||||
]
|
||||
},
|
||||
"editable": false,
|
||||
"fiscalYearStartMonth": 0,
|
||||
"graphTooltip": 0,
|
||||
"links": [
|
||||
{
|
||||
"icon": "external link",
|
||||
"targetBlank": true,
|
||||
"title": "External Documentation",
|
||||
"type": "link",
|
||||
"url": "https://example.com/docs"
|
||||
}
|
||||
],
|
||||
"panels": [
|
||||
{
|
||||
"gridPos": {
|
||||
"h": 3,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"id": 1,
|
||||
"options": {
|
||||
"content": "This dashboard demonstrates various monitoring components for application observability and performance metrics.\n",
|
||||
"mode": "markdown"
|
||||
},
|
||||
"title": "Application Monitoring",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"collapsed": false,
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"id": 23,
|
||||
"panels": [],
|
||||
"title": "Application Service",
|
||||
"type": "row"
|
||||
},
|
||||
{
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 8,
|
||||
"x": 0,
|
||||
"y": 1
|
||||
},
|
||||
"id": 6,
|
||||
"options": {
|
||||
"content": "This service handles background processing tasks for the application system. It manages various types of operations including data synchronization, resource management, and batch processing.\n\nSupported operation types:\n1. Sync: Synchronizes data between different systems\n2. Process: Handles batch data processing tasks\n3. Cleanup: Removes outdated or temporary resources\n4. Update: Applies configuration changes across services\n\nService dependencies:\n- Data API: For reading and writing application data\n- Configuration Service: For managing system settings\n- Queue Service: For handling task scheduling\n- Storage Service: For persistent data management\n- Auth Service: For authentication and authorization\n- Metrics Service: For collecting operational statistics\n",
|
||||
"mode": "markdown"
|
||||
},
|
||||
"title": "Service Overview",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 8,
|
||||
"x": 8,
|
||||
"y": 1
|
||||
},
|
||||
"id": 7,
|
||||
"options": {
|
||||
"content": "Error monitoring helps identify issues in the system. This section displays error logs and success rates for operations.",
|
||||
"mode": "markdown"
|
||||
},
|
||||
"title": "Error Monitoring",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${prom}"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "red",
|
||||
"value": 0
|
||||
},
|
||||
{
|
||||
"color": "yellow",
|
||||
"value": 0.95
|
||||
},
|
||||
{
|
||||
"color": "green",
|
||||
"value": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
"unit": "percentunit"
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 8,
|
||||
"x": 16,
|
||||
"y": 1
|
||||
},
|
||||
"id": 8,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "sum by (action) (app_jobs_processed_total{outcome=\"success\", cluster=\"$cluster\", namespace=\"default\"})\n/\nsum by (action) (app_jobs_processed_total{cluster=\"$cluster\", namespace=\"default\"})\n",
|
||||
"legendFormat": "{{action}}",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Job Success Rate",
|
||||
"type": "stat"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "loki",
|
||||
"uid": "${loki}"
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 8,
|
||||
"x": 0,
|
||||
"y": 8
|
||||
},
|
||||
"id": 9,
|
||||
"options": {
|
||||
"enableLogDetails": true,
|
||||
"showTime": false,
|
||||
"sortOrder": "Descending",
|
||||
"wrapLogMessage": true
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"expr": "{namespace=\"default\", cluster=\"$cluster\", job=\"app-service\"} | logfmt | level=\"error\"",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Errors",
|
||||
"type": "logs"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "loki",
|
||||
"uid": "${loki}"
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 8,
|
||||
"x": 8,
|
||||
"y": 8
|
||||
},
|
||||
"id": 10,
|
||||
"options": {
|
||||
"enableLogDetails": true,
|
||||
"showTime": false,
|
||||
"sortOrder": "Descending",
|
||||
"wrapLogMessage": true
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"expr": "{namespace=\"default\", cluster=\"$cluster\", job=\"app-service\"} | logfmt",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "All",
|
||||
"type": "logs"
|
||||
},
|
||||
{
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 8,
|
||||
"x": 16,
|
||||
"y": 8
|
||||
},
|
||||
"id": 11,
|
||||
"options": {
|
||||
"content": "Performance monitoring examines factors that affect system response times, including operation duration, queue lengths, and processing delays. This section provides metrics and traces for performance analysis.\n",
|
||||
"mode": "markdown"
|
||||
},
|
||||
"title": "Performance Analysis",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${prom}"
|
||||
},
|
||||
"description": "Number of concurrent processing threads available for handling operations",
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 8,
|
||||
"x": 0,
|
||||
"y": 15
|
||||
},
|
||||
"id": 12,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "max(app_worker_threads_active{cluster=\"$cluster\", namespace=\"default\"})",
|
||||
"instant": true,
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Concurrent Job Drivers",
|
||||
"type": "stat"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "tempo",
|
||||
"uid": "${tempo}"
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 8,
|
||||
"x": 8,
|
||||
"y": 15
|
||||
},
|
||||
"id": 13,
|
||||
"targets": [
|
||||
{
|
||||
"filters": [
|
||||
{
|
||||
"id": "span-name",
|
||||
"operator": "=",
|
||||
"scope": "span",
|
||||
"tag": "name",
|
||||
"value": [
|
||||
"provisioning.sync.process"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "k8s-cluster-name",
|
||||
"operator": "=",
|
||||
"scope": "resource",
|
||||
"tag": "k8s.cluster.name",
|
||||
"value": [
|
||||
"$cluster"
|
||||
]
|
||||
}
|
||||
],
|
||||
"query": "{name=\"app.operation.process\"}",
|
||||
"queryType": "traceqlSearch",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Recent Operation Traces",
|
||||
"type": "table"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${prom}"
|
||||
},
|
||||
"description": "Histogram showing p99, p95, p50, and p10 percentiles for job processing duration based on number of resources changed",
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"value": 0
|
||||
},
|
||||
{
|
||||
"color": "yellow",
|
||||
"value": 2
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 5
|
||||
}
|
||||
]
|
||||
},
|
||||
"unit": "s"
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 8,
|
||||
"x": 16,
|
||||
"y": 15
|
||||
},
|
||||
"id": 14,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "histogram_quantile(0.99, sum(rate(app_operation_duration_seconds_bucket{cluster=\"$cluster\", namespace=\"default\"}[7d])) by (le, resources_changed_bucket, action)) and on(resources_changed_bucket, action) sum(rate(app_operation_duration_seconds_bucket{cluster=\"$cluster\", namespace=\"default\"}[7d])) by (resources_changed_bucket, action) \u003e 0",
|
||||
"legendFormat": "{{action}} q0.99 - size {{resources_changed_bucket}}",
|
||||
"refId": "B"
|
||||
},
|
||||
{
|
||||
"expr": "histogram_quantile(0.9, sum(rate(app_operation_duration_seconds_bucket{cluster=\"$cluster\", namespace=\"default\"}[7d])) by (le, resources_changed_bucket, action)) and on(resources_changed_bucket, action) sum(rate(app_operation_duration_seconds_bucket{cluster=\"$cluster\", namespace=\"default\"}[7d])) by (resources_changed_bucket, action) \u003e 0",
|
||||
"legendFormat": "{{action}} q0.95 - size {{resources_changed_bucket}}",
|
||||
"refId": "C"
|
||||
},
|
||||
{
|
||||
"expr": "histogram_quantile(0.5, sum(rate(app_operation_duration_seconds_bucket{cluster=\"$cluster\", namespace=\"default\"}[7d])) by (le, resources_changed_bucket, action)) and on(resources_changed_bucket, action) sum(rate(app_operation_duration_seconds_bucket{cluster=\"$cluster\", namespace=\"default\"}[7d])) by (resources_changed_bucket, action) \u003e 0",
|
||||
"legendFormat": "{{action}} q0.5 - size {{resources_changed_bucket}}",
|
||||
"refId": "D"
|
||||
},
|
||||
{
|
||||
"expr": "histogram_quantile(0.1, sum(rate(app_operation_duration_seconds_bucket{cluster=\"$cluster\", namespace=\"default\"}[7d])) by (le, resources_changed_bucket, action)) and on(resources_changed_bucket, action) sum(rate(app_operation_duration_seconds_bucket{cluster=\"$cluster\", namespace=\"default\"}[7d])) by (resources_changed_bucket, action) \u003e 0",
|
||||
"legendFormat": "{{action}} q0.1 - size {{resources_changed_bucket}}",
|
||||
"refId": "E"
|
||||
}
|
||||
],
|
||||
"timeFrom": "7d",
|
||||
"title": "7d avg of job durations",
|
||||
"transformations": [
|
||||
{
|
||||
"id": "reduce",
|
||||
"options": {
|
||||
"mode": "seriesToRows",
|
||||
"reducers": [
|
||||
"mean"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "seriesToRows"
|
||||
},
|
||||
{
|
||||
"id": "organize",
|
||||
"options": {
|
||||
"renameByName": {
|
||||
"Field": "Type",
|
||||
"Mean": "Avg Duration",
|
||||
"Metric": "Legend",
|
||||
"Value": "Duration"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"type": "table"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${prom}"
|
||||
},
|
||||
"description": "Histogram showing p99, p95, p50, and p10 percentiles for job processing duration based on number of resources changed",
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 8,
|
||||
"x": 0,
|
||||
"y": 22
|
||||
},
|
||||
"id": 15,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "histogram_quantile(0.99, sum(rate(app_operation_duration_seconds_bucket{cluster=\"$cluster\", namespace=\"default\"}[5m])) by (le, resources_changed_bucket, action))",
|
||||
"legendFormat": "{{action}} q0.99 - size {{resources_changed_bucket}}",
|
||||
"refId": "B"
|
||||
},
|
||||
{
|
||||
"expr": "histogram_quantile(0.95, sum(rate(app_operation_duration_seconds_bucket{cluster=\"$cluster\", namespace=\"default\"}[5m])) by (le, resources_changed_bucket, action))",
|
||||
"legendFormat": "{{action}} q0.95 - size {{resources_changed_bucket}}",
|
||||
"refId": "C"
|
||||
},
|
||||
{
|
||||
"expr": "histogram_quantile(0.5, sum(rate(app_operation_duration_seconds_bucket{cluster=\"$cluster\", namespace=\"default\"}[5m])) by (le, resources_changed_bucket, action))",
|
||||
"legendFormat": "{{action}} q0.5 - size {{resources_changed_bucket}}",
|
||||
"refId": "D"
|
||||
},
|
||||
{
|
||||
"expr": "histogram_quantile(0.1, sum(rate(app_operation_duration_seconds_bucket{cluster=\"$cluster\", namespace=\"default\"}[5m])) by (le, resources_changed_bucket, action))",
|
||||
"legendFormat": "{{action}} q0.1 - size {{resources_changed_bucket}}",
|
||||
"refId": "E"
|
||||
}
|
||||
],
|
||||
"title": "Job Duration",
|
||||
"type": "timeseries"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${prom}"
|
||||
},
|
||||
"description": "Total number of jobs waiting to be processed",
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 8,
|
||||
"x": 8,
|
||||
"y": 22
|
||||
},
|
||||
"id": 16,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "clamp_min(sum(app_operation_queue_size{cluster=\"$cluster\", namespace=\"default\"}), 0)",
|
||||
"legendFormat": "Queue size",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Queue Size",
|
||||
"type": "stat"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${prom}"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"unit": "s"
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 8,
|
||||
"x": 16,
|
||||
"y": 22
|
||||
},
|
||||
"id": 17,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "avg(histogram_quantile(0.5, sum(rate(app_operation_queue_wait_seconds_bucket{cluster=\"$cluster\", namespace=\"default\"}[7d])) by (le)))",
|
||||
"legendFormat": "Queue size",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"timeFrom": "7d",
|
||||
"title": "7d avg Queue Wait Time",
|
||||
"type": "stat"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${prom}"
|
||||
},
|
||||
"description": "How long a job is in the queue before being picked up",
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 8,
|
||||
"x": 0,
|
||||
"y": 29
|
||||
},
|
||||
"id": 18,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "histogram_quantile(0.99, sum(rate(app_operation_queue_wait_seconds_bucket{cluster=\"$cluster\", namespace=\"default\"}[$__rate_interval])) by (le))",
|
||||
"legendFormat": "q0.99",
|
||||
"refId": "B"
|
||||
},
|
||||
{
|
||||
"expr": "histogram_quantile(0.95, sum(rate(app_operation_queue_wait_seconds_bucket{cluster=\"$cluster\", namespace=\"default\"}[$__rate_interval])) by (le))",
|
||||
"legendFormat": "q0.95",
|
||||
"refId": "C"
|
||||
},
|
||||
{
|
||||
"expr": "histogram_quantile(0.5, sum(rate(app_operation_queue_wait_seconds_bucket{cluster=\"$cluster\", namespace=\"default\"}[$__rate_interval])) by (le))",
|
||||
"legendFormat": "q0.5",
|
||||
"refId": "D"
|
||||
},
|
||||
{
|
||||
"expr": "histogram_quantile(0.1, sum(rate(app_operation_queue_wait_seconds_bucket{cluster=\"$cluster\", namespace=\"default\"}[$__rate_interval])) by (le))",
|
||||
"legendFormat": "q0.1",
|
||||
"refId": "E"
|
||||
}
|
||||
],
|
||||
"title": "Queue Wait Time",
|
||||
"type": "timeseries"
|
||||
},
|
||||
{
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 8,
|
||||
"x": 8,
|
||||
"y": 29
|
||||
},
|
||||
"id": 19,
|
||||
"options": {
|
||||
"content": "Resource utilization monitoring for application containers",
|
||||
"mode": "markdown"
|
||||
},
|
||||
"title": "Resource Monitoring",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${prom}"
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 8,
|
||||
"x": 16,
|
||||
"y": 29
|
||||
},
|
||||
"id": 20,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "count by (cluster, channel)(label_replace(label_replace(kube_pod_container_info{namespace=\"default\", container=\"app-worker\", pod=~\"app-worker.*\", cluster=~\"$cluster\"}, \"version\", \"$1\", \"image\", \".+:(.+)\"), \"channel\", \"$1\", \"container\", \".+-(.+)\"))",
|
||||
"legendFormat": "{{cluster}}",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Running Pod(s)",
|
||||
"type": "timeseries"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${prom}"
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 8,
|
||||
"x": 0,
|
||||
"y": 36
|
||||
},
|
||||
"id": 21,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "max(kube_pod_container_resource_requests{namespace=\"default\", resource=\"memory\", cluster=~\"$cluster\", container=\"app-worker\", pod=~\"app-worker.*\"})",
|
||||
"legendFormat": "Memory Request",
|
||||
"refId": "A"
|
||||
},
|
||||
{
|
||||
"expr": "max(kube_pod_container_resource_limits{namespace=\"default\", resource=\"memory\", cluster=~\"$cluster\", container=\"app-worker\", pod=~\"app-worker.*\"})",
|
||||
"legendFormat": "Memory Limit",
|
||||
"refId": "B"
|
||||
},
|
||||
{
|
||||
"expr": "max(container_memory_usage_bytes{namespace=\"default\",cluster=~\"$cluster\", container=\"app-worker\", pod=~\"app-worker.*\"}) by (pod)",
|
||||
"legendFormat": "Container usage {{pod}}",
|
||||
"refId": "C"
|
||||
}
|
||||
],
|
||||
"title": "Memory Utilization",
|
||||
"type": "timeseries"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${prom}"
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 8,
|
||||
"x": 8,
|
||||
"y": 36
|
||||
},
|
||||
"id": 22,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "sum(irate(container_cpu_usage_seconds_total{namespace=\"default\", cluster=~\"$cluster\", container=\"app-worker\", pod=~\"app-worker-.*\"}[$__rate_interval])) by (pod, container, cpu)",
|
||||
"legendFormat": "Usage {{pod}}",
|
||||
"refId": "A"
|
||||
},
|
||||
{
|
||||
"expr": "sum(irate(container_cpu_cfs_throttled_seconds_total{namespace=\"default\", cluster=~\"$cluster\", container=\"app-worker\", pod=~\"app-worker-.*\"}[$__rate_interval])) by (pod, container)",
|
||||
"legendFormat": "Throttling {{pod}}",
|
||||
"refId": "B"
|
||||
},
|
||||
{
|
||||
"expr": "max(kube_pod_container_resource_limits{namespace=\"default\", cluster=~\"$cluster\", container=\"app-worker\", pod=~\"app-worker-.*\", resource=\"cpu\"})",
|
||||
"legendFormat": "CPU limit",
|
||||
"refId": "C"
|
||||
},
|
||||
{
|
||||
"expr": "max(kube_pod_container_resource_requests{namespace=\"default\", cluster=~\"$cluster\", container=\"app-worker\", pod=~\"app-worker-.*\", resource=\"cpu\"})",
|
||||
"legendFormat": "CPU request",
|
||||
"refId": "D"
|
||||
}
|
||||
],
|
||||
"title": "CPU Utilization",
|
||||
"type": "timeseries"
|
||||
}
|
||||
],
|
||||
"refresh": "10s",
|
||||
"schemaVersion": 16,
|
||||
"tags": [
|
||||
"as-code"
|
||||
],
|
||||
"templating": {
|
||||
"list": [
|
||||
{
|
||||
"current": {
|
||||
"value": "prometheus-datasource"
|
||||
},
|
||||
"hide": 0,
|
||||
"label": "Data source",
|
||||
"name": "datasource",
|
||||
"options": [],
|
||||
"query": "prometheus",
|
||||
"refresh": 1,
|
||||
"regex": "",
|
||||
"type": "datasource"
|
||||
},
|
||||
{
|
||||
"current": {
|
||||
"value": "prometheus-datasource"
|
||||
},
|
||||
"name": "prom",
|
||||
"options": [],
|
||||
"query": "prometheus",
|
||||
"refresh": 1,
|
||||
"regex": "",
|
||||
"type": "datasource"
|
||||
},
|
||||
{
|
||||
"current": {
|
||||
"value": "loki-datasource"
|
||||
},
|
||||
"name": "loki",
|
||||
"options": [],
|
||||
"query": "loki",
|
||||
"refresh": 1,
|
||||
"regex": "",
|
||||
"type": "datasource"
|
||||
},
|
||||
{
|
||||
"current": {
|
||||
"text": "tempo-datasource",
|
||||
"value": "tempo-datasource"
|
||||
},
|
||||
"name": "tempo",
|
||||
"options": [],
|
||||
"query": "tempo",
|
||||
"refresh": 1,
|
||||
"regex": ".*tempo.*",
|
||||
"type": "datasource"
|
||||
},
|
||||
{
|
||||
"current": {
|
||||
"text": "demo-cluster",
|
||||
"value": "demo-cluster"
|
||||
},
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${prom}"
|
||||
},
|
||||
"name": "cluster",
|
||||
"options": [],
|
||||
"query": "label_values(app_worker_threads_active,cluster)",
|
||||
"refresh": 1,
|
||||
"type": "query"
|
||||
}
|
||||
]
|
||||
},
|
||||
"time": {
|
||||
"from": "now-6h",
|
||||
"to": "now"
|
||||
},
|
||||
"timepicker": {
|
||||
"refresh_intervals": [
|
||||
"5s",
|
||||
"10s",
|
||||
"30s",
|
||||
"1m",
|
||||
"5m",
|
||||
"15m",
|
||||
"30m",
|
||||
"1h",
|
||||
"2h",
|
||||
"1d"
|
||||
],
|
||||
"time_options": [
|
||||
"5m",
|
||||
"15m",
|
||||
"1h",
|
||||
"6h",
|
||||
"12h",
|
||||
"24h",
|
||||
"2d",
|
||||
"7d",
|
||||
"30d"
|
||||
]
|
||||
},
|
||||
"timezone": "utc",
|
||||
"title": "Span Zero Demo Dashboard",
|
||||
"uid": "span-zero-demo-dashboard",
|
||||
"weekStart": ""
|
||||
}
|
|
@ -3,7 +3,7 @@ module github.com/grafana/grafana/apps/folder
|
|||
go 1.24.6
|
||||
|
||||
require (
|
||||
github.com/grafana/grafana-app-sdk v0.45.0
|
||||
github.com/grafana/grafana-app-sdk v0.46.0
|
||||
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250514132646-acbc7b54ed9e
|
||||
k8s.io/apimachinery v0.34.1
|
||||
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b
|
||||
|
@ -13,7 +13,7 @@ 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.12.2 // 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
|
||||
|
|
|
@ -6,8 +6,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
|||
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.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU=
|
||||
github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
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=
|
||||
|
@ -31,8 +31,8 @@ github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX
|
|||
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.45.0 h1:niFqYovxuw9vnUB9qoxEgmupqriG7Gns9ZGwB2uuOyE=
|
||||
github.com/grafana/grafana-app-sdk v0.45.0/go.mod h1:1pYGEBrgG8i6pKmmsNXvtAr15jZ4iLtyHU4yj7T6XaI=
|
||||
github.com/grafana/grafana-app-sdk v0.46.0 h1:gvzQvCQgZJ/73BfAcbDt/6TAMhnVikVPxZt/UwDl+oc=
|
||||
github.com/grafana/grafana-app-sdk v0.46.0/go.mod h1:LCTrqR1SwBS13XGVYveBmM7giJDDjzuXK+M9VzPuPWc=
|
||||
github.com/grafana/grafana-app-sdk/logging v0.45.0 h1:0SH6nYZpiLBZRwUq4J6+1vo8xuHKJjnO95/2pGOoA8w=
|
||||
github.com/grafana/grafana-app-sdk/logging v0.45.0/go.mod h1:Gh/nBWnspK3oDNWtiM5qUF/fardHzOIEez+SPI3JeHA=
|
||||
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250514132646-acbc7b54ed9e h1:BTKk7LHuG1kmAkucwTA7DuMbKpKvJTKrGdBmUNO4dfQ=
|
||||
|
|
|
@ -20,6 +20,7 @@ import (
|
|||
var appManifestData = app.ManifestData{
|
||||
AppName: "folder",
|
||||
Group: "folder.grafana.app",
|
||||
PreferredVersion: "v1beta1",
|
||||
Versions: []app.ManifestVersion{
|
||||
{
|
||||
Name: "v1beta1",
|
||||
|
|
|
@ -22,7 +22,7 @@ replace github.com/prometheus/alertmanager => github.com/grafana/prometheus-aler
|
|||
|
||||
require (
|
||||
github.com/grafana/grafana v0.0.0-00010101000000-000000000000
|
||||
github.com/grafana/grafana-app-sdk v0.45.0
|
||||
github.com/grafana/grafana-app-sdk v0.46.0
|
||||
github.com/grafana/grafana-app-sdk/logging v0.45.0
|
||||
github.com/grafana/grafana/apps/folder v0.0.0
|
||||
github.com/grafana/grafana/pkg/apimachinery v0.0.0
|
||||
|
@ -145,7 +145,7 @@ require (
|
|||
github.com/dolthub/vitess v0.0.0-20250410090211-143e6b272ad4 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/elazarl/goproxy v1.7.2 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.12.2 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.13.0 // indirect
|
||||
github.com/emirpasic/gods v1.18.1 // indirect
|
||||
github.com/envoyproxy/go-control-plane/envoy v1.32.4 // indirect
|
||||
github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect
|
||||
|
@ -201,7 +201,7 @@ require (
|
|||
github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.14.2 // indirect
|
||||
github.com/gorilla/mux v1.8.1 // indirect
|
||||
github.com/grafana/alerting v0.0.0-20250925200825-7a889aa4934d // indirect
|
||||
github.com/grafana/alerting v0.0.0-20251006163224-3da3b9a5202d // indirect
|
||||
github.com/grafana/authlib v0.0.0-20250930082137-a40e2c2b094f // indirect
|
||||
github.com/grafana/authlib/types v0.0.0-20250926065801-df98203cff37 // indirect
|
||||
github.com/grafana/dataplane/sdata v0.0.9 // indirect
|
||||
|
|
|
@ -438,8 +438,8 @@ github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFP
|
|||
github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
|
||||
github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o=
|
||||
github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE=
|
||||
github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU=
|
||||
github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
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/emicklei/proto v1.13.2 h1:z/etSFO3uyXeuEsVPzfl56WNgzcvIr42aQazXaQmFZY=
|
||||
github.com/emicklei/proto v1.13.2/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A=
|
||||
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
|
||||
|
@ -721,8 +721,8 @@ github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
|||
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
||||
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo=
|
||||
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
|
||||
github.com/grafana/alerting v0.0.0-20250925200825-7a889aa4934d h1:zzEty7HgfXbQ/RiBCJFMqaZiJlqiXuz/Zbc6/H6ksuM=
|
||||
github.com/grafana/alerting v0.0.0-20250925200825-7a889aa4934d/go.mod h1:T5sitas9VhVj8/S9LeRLy6H75kTBdh/sCCqHo7gaQI8=
|
||||
github.com/grafana/alerting v0.0.0-20251006163224-3da3b9a5202d h1:1dj/mcA4zGJpTrfSDBeF4x9/gihn21T8uydC3PnBRmw=
|
||||
github.com/grafana/alerting v0.0.0-20251006163224-3da3b9a5202d/go.mod h1:VGjS5gDwWEADPP6pF/drqLxEImgeuHlEW5u8E5EfIrM=
|
||||
github.com/grafana/authlib v0.0.0-20250930082137-a40e2c2b094f h1:Cbm6OKkOcJ+7CSZsGsEJzktC/SIa5bxVeYKQLuYK86o=
|
||||
github.com/grafana/authlib v0.0.0-20250930082137-a40e2c2b094f/go.mod h1:axY0cdOg3q0TZHwpHnIz5x16xZ8ZBxJHShsSHHXcHQg=
|
||||
github.com/grafana/authlib/types v0.0.0-20250926065801-df98203cff37 h1:qEwZ+7MbPjzRvTi31iT9w7NBhKIpKwZrFbYmOZLqkwA=
|
||||
|
@ -733,8 +733,8 @@ github.com/grafana/dataplane/sdata v0.0.9 h1:AGL1LZnCUG4MnQtnWpBPbQ8ZpptaZs14w6k
|
|||
github.com/grafana/dataplane/sdata v0.0.9/go.mod h1:Jvs5ddpGmn6vcxT7tCTWAZ1mgi4sbcdFt9utQx5uMAU=
|
||||
github.com/grafana/dskit v0.0.0-20250908063411-6b6da59b5cc4 h1:jSojuc7njleS3UOz223WDlXOinmuLAIPI0z2vtq8EgI=
|
||||
github.com/grafana/dskit v0.0.0-20250908063411-6b6da59b5cc4/go.mod h1:VahT+GtfQIM+o8ht2StR6J9g+Ef+C2Vokh5uuSmOD/4=
|
||||
github.com/grafana/grafana-app-sdk v0.45.0 h1:niFqYovxuw9vnUB9qoxEgmupqriG7Gns9ZGwB2uuOyE=
|
||||
github.com/grafana/grafana-app-sdk v0.45.0/go.mod h1:1pYGEBrgG8i6pKmmsNXvtAr15jZ4iLtyHU4yj7T6XaI=
|
||||
github.com/grafana/grafana-app-sdk v0.46.0 h1:gvzQvCQgZJ/73BfAcbDt/6TAMhnVikVPxZt/UwDl+oc=
|
||||
github.com/grafana/grafana-app-sdk v0.46.0/go.mod h1:LCTrqR1SwBS13XGVYveBmM7giJDDjzuXK+M9VzPuPWc=
|
||||
github.com/grafana/grafana-app-sdk/logging v0.45.0 h1:0SH6nYZpiLBZRwUq4J6+1vo8xuHKJjnO95/2pGOoA8w=
|
||||
github.com/grafana/grafana-app-sdk/logging v0.45.0/go.mod h1:Gh/nBWnspK3oDNWtiM5qUF/fardHzOIEez+SPI3JeHA=
|
||||
github.com/grafana/grafana-aws-sdk v1.2.0 h1:LLR4/g91WBuCRwm2cbWfCREq565+GxIFe08nqqIcIuw=
|
||||
|
|
|
@ -4,12 +4,13 @@ TeamBindingSpec: {
|
|||
#Subject: {
|
||||
// uid of the identity
|
||||
name: string
|
||||
// permission of the identity in the team
|
||||
permission: TeamPermission
|
||||
}
|
||||
|
||||
subjects: [...#Subject]
|
||||
subject: #Subject
|
||||
teamRef: TeamRef
|
||||
|
||||
// permission of the identity in the team
|
||||
permission: TeamPermission
|
||||
}
|
||||
|
||||
TeamRef:{
|
||||
|
|
|
@ -6,8 +6,6 @@ package v0alpha1
|
|||
type TeamBindingspecSubject struct {
|
||||
// uid of the identity
|
||||
Name string `json:"name"`
|
||||
// permission of the identity in the team
|
||||
Permission TeamBindingTeamPermission `json:"permission"`
|
||||
}
|
||||
|
||||
// NewTeamBindingspecSubject creates a new TeamBindingspecSubject object.
|
||||
|
@ -15,14 +13,6 @@ func NewTeamBindingspecSubject() *TeamBindingspecSubject {
|
|||
return &TeamBindingspecSubject{}
|
||||
}
|
||||
|
||||
// +k8s:openapi-gen=true
|
||||
type TeamBindingTeamPermission string
|
||||
|
||||
const (
|
||||
TeamBindingTeamPermissionAdmin TeamBindingTeamPermission = "admin"
|
||||
TeamBindingTeamPermissionMember TeamBindingTeamPermission = "member"
|
||||
)
|
||||
|
||||
// +k8s:openapi-gen=true
|
||||
type TeamBindingTeamRef struct {
|
||||
// Name is the unique identifier for a team.
|
||||
|
@ -34,16 +24,26 @@ func NewTeamBindingTeamRef() *TeamBindingTeamRef {
|
|||
return &TeamBindingTeamRef{}
|
||||
}
|
||||
|
||||
// +k8s:openapi-gen=true
|
||||
type TeamBindingTeamPermission string
|
||||
|
||||
const (
|
||||
TeamBindingTeamPermissionAdmin TeamBindingTeamPermission = "admin"
|
||||
TeamBindingTeamPermissionMember TeamBindingTeamPermission = "member"
|
||||
)
|
||||
|
||||
// +k8s:openapi-gen=true
|
||||
type TeamBindingSpec struct {
|
||||
Subjects []TeamBindingspecSubject `json:"subjects"`
|
||||
Subject TeamBindingspecSubject `json:"subject"`
|
||||
TeamRef TeamBindingTeamRef `json:"teamRef"`
|
||||
// permission of the identity in the team
|
||||
Permission TeamBindingTeamPermission `json:"permission"`
|
||||
}
|
||||
|
||||
// NewTeamBindingSpec creates a new TeamBindingSpec object.
|
||||
func NewTeamBindingSpec() *TeamBindingSpec {
|
||||
return &TeamBindingSpec{
|
||||
Subjects: []TeamBindingspecSubject{},
|
||||
Subject: *NewTeamBindingspecSubject(),
|
||||
TeamRef: *NewTeamBindingTeamRef(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2103,27 +2103,28 @@ func schema_pkg_apis_iam_v0alpha1_TeamBindingSpec(ref common.ReferenceCallback)
|
|||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"subjects": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"array"},
|
||||
Items: &spec.SchemaOrArray{
|
||||
Schema: &spec.Schema{
|
||||
"subject": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.TeamBindingspecSubject"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"teamRef": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.TeamBindingTeamRef"),
|
||||
},
|
||||
},
|
||||
"permission": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "permission of the identity in the team",
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
Required: []string{"subjects", "teamRef"},
|
||||
},
|
||||
},
|
||||
Required: []string{"subject", "teamRef", "permission"},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
|
@ -2210,16 +2211,8 @@ func schema_pkg_apis_iam_v0alpha1_TeamBindingspecSubject(ref common.ReferenceCal
|
|||
Format: "",
|
||||
},
|
||||
},
|
||||
"permission": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "permission of the identity in the team",
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"name", "permission"},
|
||||
Required: []string{"name"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import (
|
|||
var appManifestData = app.ManifestData{
|
||||
AppName: "iam",
|
||||
Group: "iam.grafana.app",
|
||||
PreferredVersion: "v0alpha1",
|
||||
Versions: []app.ManifestVersion{
|
||||
{
|
||||
Name: "v0alpha1",
|
||||
|
|
|
@ -36,7 +36,7 @@ func Provider(appCfg app.SpecificConfig) app.Provider {
|
|||
}
|
||||
|
||||
func generateInformerSupplier(informerConfig InformerConfig, metrics *reconcilers.ReconcilerMetrics) simple.InformerSupplier {
|
||||
return func(kind resource.Kind, clients resource.ClientGenerator, options operator.ListWatchOptions) (operator.Informer, error) {
|
||||
return func(kind resource.Kind, clients resource.ClientGenerator, options operator.InformerOptions) (operator.Informer, error) {
|
||||
client, err := clients.ClientFor(kind)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -44,9 +44,7 @@ func generateInformerSupplier(informerConfig InformerConfig, metrics *reconciler
|
|||
|
||||
informer, err := operator.NewKubernetesBasedInformer(
|
||||
kind, client,
|
||||
operator.KubernetesBasedInformerOptions{
|
||||
ListWatchOptions: options,
|
||||
},
|
||||
options,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -92,6 +90,7 @@ func New(cfg app.Config) (app.App, error) {
|
|||
KubeConfig: cfg.KubeConfig,
|
||||
InformerConfig: simple.AppInformerConfig{
|
||||
InformerSupplier: generateInformerSupplier(appSpecificConfig.InformerConfig, metrics),
|
||||
InformerOptions: operator.InformerOptions{
|
||||
ErrorHandler: func(ctx context.Context, err error) {
|
||||
logging.FromContext(ctx).With("error", err).Error("Informer processing error")
|
||||
if metrics != nil {
|
||||
|
@ -100,6 +99,7 @@ func New(cfg app.Config) (app.App, error) {
|
|||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
UnmanagedKinds: []simple.AppUnmanagedKind{
|
||||
{
|
||||
Kind: foldersKind.FolderKind(),
|
||||
|
|
|
@ -3,7 +3,7 @@ module github.com/grafana/grafana/apps/investigations
|
|||
go 1.24.6
|
||||
|
||||
require (
|
||||
github.com/grafana/grafana-app-sdk v0.45.0
|
||||
github.com/grafana/grafana-app-sdk v0.46.0
|
||||
k8s.io/apimachinery v0.34.1
|
||||
k8s.io/klog/v2 v2.130.1
|
||||
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b
|
||||
|
@ -15,7 +15,7 @@ require (
|
|||
github.com/cenkalti/backoff/v5 v5.0.3 // 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.12.2 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.13.0 // indirect
|
||||
github.com/evanphx/json-patch v5.9.11+incompatible // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
|
||||
github.com/getkin/kin-openapi v0.133.0 // indirect
|
||||
|
|
|
@ -10,8 +10,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
|||
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.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU=
|
||||
github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
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/evanphx/json-patch v5.9.11+incompatible h1:ixHHqfcGvxhWkniF1tWxBHA0yb4Z+d1UQi45df52xW8=
|
||||
github.com/evanphx/json-patch v5.9.11+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
|
||||
|
@ -46,8 +46,8 @@ github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J
|
|||
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
|
||||
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.45.0 h1:niFqYovxuw9vnUB9qoxEgmupqriG7Gns9ZGwB2uuOyE=
|
||||
github.com/grafana/grafana-app-sdk v0.45.0/go.mod h1:1pYGEBrgG8i6pKmmsNXvtAr15jZ4iLtyHU4yj7T6XaI=
|
||||
github.com/grafana/grafana-app-sdk v0.46.0 h1:gvzQvCQgZJ/73BfAcbDt/6TAMhnVikVPxZt/UwDl+oc=
|
||||
github.com/grafana/grafana-app-sdk v0.46.0/go.mod h1:LCTrqR1SwBS13XGVYveBmM7giJDDjzuXK+M9VzPuPWc=
|
||||
github.com/grafana/grafana-app-sdk/logging v0.45.0 h1:0SH6nYZpiLBZRwUq4J6+1vo8xuHKJjnO95/2pGOoA8w=
|
||||
github.com/grafana/grafana-app-sdk/logging v0.45.0/go.mod h1:Gh/nBWnspK3oDNWtiM5qUF/fardHzOIEez+SPI3JeHA=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU=
|
||||
|
|
|
@ -76,7 +76,7 @@ func (c *InvestigationClient) Patch(ctx context.Context, identifier resource.Ide
|
|||
return c.client.Patch(ctx, identifier, req, opts)
|
||||
}
|
||||
|
||||
func (c *InvestigationClient) UpdateStatus(ctx context.Context, newStatus InvestigationStatus, opts resource.UpdateOptions) (*Investigation, error) {
|
||||
func (c *InvestigationClient) UpdateStatus(ctx context.Context, identifier resource.Identifier, newStatus InvestigationStatus, opts resource.UpdateOptions) (*Investigation, error) {
|
||||
return c.client.Update(ctx, &Investigation{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: InvestigationKind().Kind(),
|
||||
|
@ -84,6 +84,8 @@ func (c *InvestigationClient) UpdateStatus(ctx context.Context, newStatus Invest
|
|||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
ResourceVersion: opts.ResourceVersion,
|
||||
Namespace: identifier.Namespace,
|
||||
Name: identifier.Name,
|
||||
},
|
||||
Status: newStatus,
|
||||
}, resource.UpdateOptions{
|
||||
|
|
|
@ -76,7 +76,7 @@ func (c *InvestigationIndexClient) Patch(ctx context.Context, identifier resourc
|
|||
return c.client.Patch(ctx, identifier, req, opts)
|
||||
}
|
||||
|
||||
func (c *InvestigationIndexClient) UpdateStatus(ctx context.Context, newStatus InvestigationIndexStatus, opts resource.UpdateOptions) (*InvestigationIndex, error) {
|
||||
func (c *InvestigationIndexClient) UpdateStatus(ctx context.Context, identifier resource.Identifier, newStatus InvestigationIndexStatus, opts resource.UpdateOptions) (*InvestigationIndex, error) {
|
||||
return c.client.Update(ctx, &InvestigationIndex{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: InvestigationIndexKind().Kind(),
|
||||
|
@ -84,6 +84,8 @@ func (c *InvestigationIndexClient) UpdateStatus(ctx context.Context, newStatus I
|
|||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
ResourceVersion: opts.ResourceVersion,
|
||||
Namespace: identifier.Namespace,
|
||||
Name: identifier.Name,
|
||||
},
|
||||
Status: newStatus,
|
||||
}, resource.UpdateOptions{
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
"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"
|
||||
|
||||
v0alpha1 "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1"
|
||||
)
|
||||
|
@ -29,6 +30,7 @@ var (
|
|||
var appManifestData = app.ManifestData{
|
||||
AppName: "investigations",
|
||||
Group: "investigations.grafana.app",
|
||||
PreferredVersion: "v0alpha1",
|
||||
Versions: []app.ManifestVersion{
|
||||
{
|
||||
Name: "v0alpha1",
|
||||
|
@ -50,6 +52,10 @@ var appManifestData = app.ManifestData{
|
|||
Schema: &versionSchemaInvestigationIndexv0alpha1,
|
||||
},
|
||||
},
|
||||
Routes: app.ManifestVersionRoutes{
|
||||
Namespaced: map[string]spec3.PathProps{},
|
||||
Cluster: map[string]spec3.PathProps{},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -79,6 +85,7 @@ 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:]
|
||||
|
@ -97,8 +104,22 @@ func ManifestCustomRouteQueryAssociator(kind, version, path, verb string) (goTyp
|
|||
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)
|
||||
}
|
||||
|
@ -108,3 +129,6 @@ func (g *GoTypeAssociator) CustomRouteReturnGoType(kind, version, path, verb str
|
|||
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)
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"context"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/app"
|
||||
"github.com/grafana/grafana-app-sdk/operator"
|
||||
"github.com/grafana/grafana-app-sdk/resource"
|
||||
"github.com/grafana/grafana-app-sdk/simple"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
|
@ -18,10 +19,12 @@ func New(cfg app.Config) (app.App, error) {
|
|||
Name: "investigation",
|
||||
KubeConfig: cfg.KubeConfig,
|
||||
InformerConfig: simple.AppInformerConfig{
|
||||
InformerOptions: operator.InformerOptions{
|
||||
ErrorHandler: func(_ context.Context, err error) {
|
||||
klog.ErrorS(err, "Informer processing error")
|
||||
},
|
||||
},
|
||||
},
|
||||
ManagedKinds: []simple.AppManagedKind{
|
||||
{
|
||||
Kind: investigationsv0alpha1.InvestigationKind(),
|
||||
|
|
|
@ -3,7 +3,7 @@ module github.com/grafana/grafana/apps/playlist
|
|||
go 1.24.6
|
||||
|
||||
require (
|
||||
github.com/grafana/grafana-app-sdk v0.45.0
|
||||
github.com/grafana/grafana-app-sdk v0.46.0
|
||||
k8s.io/apimachinery v0.34.1
|
||||
k8s.io/client-go v0.34.1
|
||||
k8s.io/klog/v2 v2.130.1
|
||||
|
@ -16,7 +16,7 @@ require (
|
|||
github.com/cenkalti/backoff/v5 v5.0.3 // 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.12.2 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.13.0 // indirect
|
||||
github.com/evanphx/json-patch v5.9.11+incompatible // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
|
||||
github.com/getkin/kin-openapi v0.133.0 // indirect
|
||||
|
|
|
@ -10,8 +10,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
|||
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.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU=
|
||||
github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
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/evanphx/json-patch v5.9.11+incompatible h1:ixHHqfcGvxhWkniF1tWxBHA0yb4Z+d1UQi45df52xW8=
|
||||
github.com/evanphx/json-patch v5.9.11+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
|
||||
|
@ -46,8 +46,8 @@ github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J
|
|||
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
|
||||
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.45.0 h1:niFqYovxuw9vnUB9qoxEgmupqriG7Gns9ZGwB2uuOyE=
|
||||
github.com/grafana/grafana-app-sdk v0.45.0/go.mod h1:1pYGEBrgG8i6pKmmsNXvtAr15jZ4iLtyHU4yj7T6XaI=
|
||||
github.com/grafana/grafana-app-sdk v0.46.0 h1:gvzQvCQgZJ/73BfAcbDt/6TAMhnVikVPxZt/UwDl+oc=
|
||||
github.com/grafana/grafana-app-sdk v0.46.0/go.mod h1:LCTrqR1SwBS13XGVYveBmM7giJDDjzuXK+M9VzPuPWc=
|
||||
github.com/grafana/grafana-app-sdk/logging v0.45.0 h1:0SH6nYZpiLBZRwUq4J6+1vo8xuHKJjnO95/2pGOoA8w=
|
||||
github.com/grafana/grafana-app-sdk/logging v0.45.0/go.mod h1:Gh/nBWnspK3oDNWtiM5qUF/fardHzOIEez+SPI3JeHA=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU=
|
||||
|
|
|
@ -27,6 +27,7 @@ var (
|
|||
var appManifestData = app.ManifestData{
|
||||
AppName: "playlist",
|
||||
Group: "playlist.grafana.app",
|
||||
PreferredVersion: "v0alpha1",
|
||||
Versions: []app.ManifestVersion{
|
||||
{
|
||||
Name: "v0alpha1",
|
||||
|
|
|
@ -50,10 +50,12 @@ func New(cfg app.Config) (app.App, error) {
|
|||
Name: "playlist",
|
||||
KubeConfig: cfg.KubeConfig,
|
||||
InformerConfig: simple.AppInformerConfig{
|
||||
InformerOptions: operator.InformerOptions{
|
||||
ErrorHandler: func(ctx context.Context, err error) {
|
||||
klog.ErrorS(err, "Informer processing error")
|
||||
},
|
||||
},
|
||||
},
|
||||
ManagedKinds: []simple.AppManagedKind{
|
||||
{
|
||||
Kind: playlistv0alpha1.PlaylistKind(),
|
||||
|
|
|
@ -4,7 +4,7 @@ go 1.24.4
|
|||
|
||||
require (
|
||||
github.com/grafana/authlib/types v0.0.0-20250926065801-df98203cff37
|
||||
github.com/grafana/grafana-app-sdk v0.45.0
|
||||
github.com/grafana/grafana-app-sdk v0.46.0
|
||||
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250428110029-a8ea72012bde
|
||||
k8s.io/apimachinery v0.34.1
|
||||
k8s.io/apiserver v0.34.1
|
||||
|
@ -19,7 +19,7 @@ require (
|
|||
github.com/cenkalti/backoff/v5 v5.0.3 // 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.12.2 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.13.0 // indirect
|
||||
github.com/evanphx/json-patch v5.9.11+incompatible // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
|
||||
github.com/getkin/kin-openapi v0.133.0 // indirect
|
||||
|
|
|
@ -12,8 +12,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
|||
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.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU=
|
||||
github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
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/evanphx/json-patch v5.9.11+incompatible h1:ixHHqfcGvxhWkniF1tWxBHA0yb4Z+d1UQi45df52xW8=
|
||||
github.com/evanphx/json-patch v5.9.11+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
|
||||
|
@ -56,8 +56,8 @@ github.com/grafana/authlib/types v0.0.0-20250926065801-df98203cff37 h1:qEwZ+7MbP
|
|||
github.com/grafana/authlib/types v0.0.0-20250926065801-df98203cff37/go.mod h1:qeWYbnWzaYGl88JlL9+DsP1GT2Cudm58rLtx13fKZdw=
|
||||
github.com/grafana/dskit v0.0.0-20250908063411-6b6da59b5cc4 h1:jSojuc7njleS3UOz223WDlXOinmuLAIPI0z2vtq8EgI=
|
||||
github.com/grafana/dskit v0.0.0-20250908063411-6b6da59b5cc4/go.mod h1:VahT+GtfQIM+o8ht2StR6J9g+Ef+C2Vokh5uuSmOD/4=
|
||||
github.com/grafana/grafana-app-sdk v0.45.0 h1:niFqYovxuw9vnUB9qoxEgmupqriG7Gns9ZGwB2uuOyE=
|
||||
github.com/grafana/grafana-app-sdk v0.45.0/go.mod h1:1pYGEBrgG8i6pKmmsNXvtAr15jZ4iLtyHU4yj7T6XaI=
|
||||
github.com/grafana/grafana-app-sdk v0.46.0 h1:gvzQvCQgZJ/73BfAcbDt/6TAMhnVikVPxZt/UwDl+oc=
|
||||
github.com/grafana/grafana-app-sdk v0.46.0/go.mod h1:LCTrqR1SwBS13XGVYveBmM7giJDDjzuXK+M9VzPuPWc=
|
||||
github.com/grafana/grafana-app-sdk/logging v0.45.0 h1:0SH6nYZpiLBZRwUq4J6+1vo8xuHKJjnO95/2pGOoA8w=
|
||||
github.com/grafana/grafana-app-sdk/logging v0.45.0/go.mod h1:Gh/nBWnspK3oDNWtiM5qUF/fardHzOIEez+SPI3JeHA=
|
||||
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250428110029-a8ea72012bde h1:ydSrBIOCxJQ84+JU+cyYsOLL40QeXrB7rYfsY/ezU4w=
|
||||
|
|
|
@ -30,6 +30,7 @@ var (
|
|||
var appManifestData = app.ManifestData{
|
||||
AppName: "plugins",
|
||||
Group: "plugins.grafana.app",
|
||||
PreferredVersion: "v0alpha1",
|
||||
Versions: []app.ManifestVersion{
|
||||
{
|
||||
Name: "v0alpha1",
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"context"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/app"
|
||||
"github.com/grafana/grafana-app-sdk/operator"
|
||||
"github.com/grafana/grafana-app-sdk/resource"
|
||||
"github.com/grafana/grafana-app-sdk/simple"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
|
@ -26,10 +27,12 @@ func New(cfg app.Config) (app.App, error) {
|
|||
Name: "plugins",
|
||||
KubeConfig: cfg.KubeConfig,
|
||||
InformerConfig: simple.AppInformerConfig{
|
||||
InformerOptions: operator.InformerOptions{
|
||||
ErrorHandler: func(ctx context.Context, err error) {
|
||||
klog.ErrorS(err, "Informer processing error")
|
||||
},
|
||||
},
|
||||
},
|
||||
ManagedKinds: managedKinds,
|
||||
}
|
||||
|
||||
|
|
|
@ -3,8 +3,9 @@ module github.com/grafana/grafana/apps/preferences
|
|||
go 1.24.6
|
||||
|
||||
require (
|
||||
github.com/grafana/grafana-app-sdk v0.45.0
|
||||
github.com/grafana/grafana-app-sdk v0.46.0
|
||||
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250804150913-990f1c69ecc2
|
||||
github.com/stretchr/testify v1.11.1
|
||||
k8s.io/apimachinery v0.34.1
|
||||
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b
|
||||
)
|
||||
|
@ -13,7 +14,7 @@ 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.12.2 // 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
|
||||
|
@ -42,7 +43,6 @@ require (
|
|||
github.com/prometheus/common v0.66.1 // indirect
|
||||
github.com/prometheus/procfs v0.16.1 // indirect
|
||||
github.com/stretchr/objx v0.5.2 // indirect
|
||||
github.com/stretchr/testify v1.11.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
|
||||
|
|
|
@ -6,8 +6,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
|||
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.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU=
|
||||
github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
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=
|
||||
|
@ -31,8 +31,8 @@ github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX
|
|||
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.45.0 h1:niFqYovxuw9vnUB9qoxEgmupqriG7Gns9ZGwB2uuOyE=
|
||||
github.com/grafana/grafana-app-sdk v0.45.0/go.mod h1:1pYGEBrgG8i6pKmmsNXvtAr15jZ4iLtyHU4yj7T6XaI=
|
||||
github.com/grafana/grafana-app-sdk v0.46.0 h1:gvzQvCQgZJ/73BfAcbDt/6TAMhnVikVPxZt/UwDl+oc=
|
||||
github.com/grafana/grafana-app-sdk v0.46.0/go.mod h1:LCTrqR1SwBS13XGVYveBmM7giJDDjzuXK+M9VzPuPWc=
|
||||
github.com/grafana/grafana-app-sdk/logging v0.45.0 h1:0SH6nYZpiLBZRwUq4J6+1vo8xuHKJjnO95/2pGOoA8w=
|
||||
github.com/grafana/grafana-app-sdk/logging v0.45.0/go.mod h1:Gh/nBWnspK3oDNWtiM5qUF/fardHzOIEez+SPI3JeHA=
|
||||
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250804150913-990f1c69ecc2 h1:X0cnaFdR+iz+sDSuoZmkryFSjOirchHe2MdKSRwBWgM=
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
package v1alpha1
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (stars *StarsSpec) Add(group, kind, name string) {
|
||||
for i, r := range stars.Resource {
|
||||
if r.Group == group && r.Kind == kind {
|
||||
r.Names = append(r.Names, name)
|
||||
slices.Sort(r.Names)
|
||||
stars.Resource[i].Names = slices.Compact(r.Names)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Add the resource kind
|
||||
stars.Resource = append(stars.Resource, StarsResource{
|
||||
Group: group,
|
||||
Kind: kind,
|
||||
Names: []string{name},
|
||||
})
|
||||
stars.Normalize()
|
||||
}
|
||||
|
||||
func (stars *StarsSpec) Remove(group, kind, name string) {
|
||||
for i, r := range stars.Resource {
|
||||
if r.Group == group && r.Kind == kind {
|
||||
idx := slices.Index(r.Names, name)
|
||||
if idx < 0 {
|
||||
return // does not exist
|
||||
}
|
||||
r.Names = append(r.Names[:idx], r.Names[idx+1:]...)
|
||||
stars.Resource[i].Names = r.Names
|
||||
if len(r.Names) == 0 {
|
||||
stars.Normalize()
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Makes sure everything is in sorted order
|
||||
func (stars *StarsSpec) Normalize() {
|
||||
resources := make([]StarsResource, 0, len(stars.Resource))
|
||||
for _, r := range stars.Resource {
|
||||
if len(r.Names) > 0 {
|
||||
slices.Sort(r.Names)
|
||||
r.Names = slices.Compact(r.Names) // removes any duplicates
|
||||
resources = append(resources, r)
|
||||
}
|
||||
}
|
||||
slices.SortFunc(resources, func(a StarsResource, b StarsResource) int {
|
||||
return strings.Compare(a.Group+a.Kind, b.Group+b.Kind)
|
||||
})
|
||||
if len(resources) == 0 {
|
||||
resources = nil
|
||||
}
|
||||
stars.Resource = resources
|
||||
}
|
||||
|
||||
func Changes(current []string, target []string) (added []string, removed []string, same []string) {
|
||||
lookup := map[string]bool{}
|
||||
for _, k := range current {
|
||||
lookup[k] = true
|
||||
}
|
||||
for _, k := range target {
|
||||
if lookup[k] {
|
||||
same = append(same, k)
|
||||
delete(lookup, k)
|
||||
} else {
|
||||
added = append(added, k)
|
||||
}
|
||||
}
|
||||
for k := range lookup {
|
||||
removed = append(removed, k)
|
||||
}
|
||||
return
|
||||
}
|
|
@ -0,0 +1,235 @@
|
|||
package v1alpha1
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type starItem struct {
|
||||
group string
|
||||
kind string
|
||||
name string
|
||||
}
|
||||
|
||||
func TestStarsWrite(t *testing.T) {
|
||||
t.Run("apply", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
spec *StarsSpec
|
||||
item starItem
|
||||
remove bool
|
||||
expect *StarsSpec
|
||||
}{{
|
||||
name: "add to an existing array",
|
||||
spec: &StarsSpec{
|
||||
Resource: []StarsResource{{
|
||||
Group: "g",
|
||||
Kind: "k",
|
||||
Names: []string{"a", "b", "x"},
|
||||
}},
|
||||
},
|
||||
item: starItem{
|
||||
group: "g",
|
||||
kind: "k",
|
||||
name: "c",
|
||||
},
|
||||
remove: false,
|
||||
expect: &StarsSpec{
|
||||
Resource: []StarsResource{{
|
||||
Group: "g",
|
||||
Kind: "k",
|
||||
Names: []string{"a", "b", "c", "x"}, // added "b" (and sorted)
|
||||
}},
|
||||
},
|
||||
}, {
|
||||
name: "remove from an existing array",
|
||||
spec: &StarsSpec{
|
||||
Resource: []StarsResource{{
|
||||
Group: "g",
|
||||
Kind: "k",
|
||||
Names: []string{"a", "b", "c"},
|
||||
}},
|
||||
},
|
||||
item: starItem{
|
||||
group: "g",
|
||||
kind: "k",
|
||||
name: "b",
|
||||
},
|
||||
remove: true,
|
||||
expect: &StarsSpec{
|
||||
Resource: []StarsResource{{
|
||||
Group: "g",
|
||||
Kind: "k",
|
||||
Names: []string{"a", "c"}, // removed "b"
|
||||
}},
|
||||
},
|
||||
}, {
|
||||
name: "add to empty spec",
|
||||
spec: &StarsSpec{},
|
||||
item: starItem{
|
||||
group: "g",
|
||||
kind: "k",
|
||||
name: "a",
|
||||
},
|
||||
remove: false,
|
||||
expect: &StarsSpec{
|
||||
Resource: []StarsResource{{
|
||||
Group: "g",
|
||||
Kind: "k",
|
||||
Names: []string{"a"},
|
||||
}},
|
||||
},
|
||||
}, {
|
||||
name: "remove item that does not exist",
|
||||
spec: &StarsSpec{
|
||||
Resource: []StarsResource{{
|
||||
Group: "g",
|
||||
Kind: "k",
|
||||
Names: []string{"x"},
|
||||
}},
|
||||
},
|
||||
item: starItem{
|
||||
group: "g",
|
||||
kind: "k",
|
||||
name: "a",
|
||||
},
|
||||
remove: true,
|
||||
}, {
|
||||
name: "add item that already exist",
|
||||
spec: &StarsSpec{
|
||||
Resource: []StarsResource{{
|
||||
Group: "g",
|
||||
Kind: "k",
|
||||
Names: []string{"x"},
|
||||
}},
|
||||
},
|
||||
item: starItem{
|
||||
group: "g",
|
||||
kind: "k",
|
||||
name: "x",
|
||||
},
|
||||
remove: false,
|
||||
}, {
|
||||
name: "remove from empty",
|
||||
spec: &StarsSpec{},
|
||||
item: starItem{
|
||||
group: "g",
|
||||
kind: "k",
|
||||
name: "a",
|
||||
},
|
||||
remove: true,
|
||||
}, {
|
||||
name: "remove item that does not exist",
|
||||
spec: &StarsSpec{
|
||||
Resource: []StarsResource{{
|
||||
Group: "g",
|
||||
Kind: "k",
|
||||
Names: []string{"a", "b", "c"},
|
||||
}},
|
||||
},
|
||||
item: starItem{
|
||||
group: "g",
|
||||
kind: "k",
|
||||
name: "X",
|
||||
},
|
||||
remove: true,
|
||||
}, {
|
||||
name: "remove last item",
|
||||
spec: &StarsSpec{
|
||||
Resource: []StarsResource{{
|
||||
Group: "g",
|
||||
Kind: "k",
|
||||
Names: []string{"a"},
|
||||
}},
|
||||
},
|
||||
item: starItem{
|
||||
group: "g",
|
||||
kind: "k",
|
||||
name: "a",
|
||||
},
|
||||
remove: true,
|
||||
expect: &StarsSpec{}, // empty object
|
||||
}, {
|
||||
name: "remove last item (with others)",
|
||||
spec: &StarsSpec{
|
||||
Resource: []StarsResource{{
|
||||
Group: "g",
|
||||
Kind: "k",
|
||||
Names: []string{"a"},
|
||||
}, {
|
||||
Group: "g2",
|
||||
Kind: "k2",
|
||||
Names: []string{"a"},
|
||||
}}},
|
||||
item: starItem{
|
||||
group: "g",
|
||||
kind: "k",
|
||||
name: "a",
|
||||
},
|
||||
remove: true,
|
||||
expect: &StarsSpec{
|
||||
Resource: []StarsResource{{
|
||||
Group: "g2",
|
||||
Kind: "k2",
|
||||
Names: []string{"a"},
|
||||
}}},
|
||||
}}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.expect == nil {
|
||||
tt.expect = tt.spec.DeepCopy()
|
||||
}
|
||||
|
||||
if tt.remove {
|
||||
tt.spec.Remove(tt.item.group, tt.item.kind, tt.item.name)
|
||||
} else {
|
||||
tt.spec.Add(tt.item.group, tt.item.kind, tt.item.name)
|
||||
}
|
||||
|
||||
require.Equal(t, tt.expect, tt.spec)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("changes", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
current []string
|
||||
target []string
|
||||
added []string
|
||||
removed []string
|
||||
same []string
|
||||
}{{
|
||||
name: "same",
|
||||
current: []string{"a"},
|
||||
target: []string{"a"},
|
||||
same: []string{"a"},
|
||||
}, {
|
||||
name: "adding one",
|
||||
current: []string{"a"},
|
||||
target: []string{"a", "b"},
|
||||
same: []string{"a"},
|
||||
added: []string{"b"},
|
||||
}, {
|
||||
name: "removing one",
|
||||
current: []string{"a", "b"},
|
||||
target: []string{"a"},
|
||||
same: []string{"a"},
|
||||
removed: []string{"b"},
|
||||
}, {
|
||||
name: "removed to empty",
|
||||
current: []string{"a"},
|
||||
target: []string{},
|
||||
removed: []string{"a"},
|
||||
}}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
a, r, s := Changes(tt.current, tt.target)
|
||||
require.Equal(t, tt.added, a, "added")
|
||||
require.Equal(t, tt.removed, r, "removed")
|
||||
require.Equal(t, tt.same, s, "same")
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
|
@ -30,6 +30,7 @@ var (
|
|||
var appManifestData = app.ManifestData{
|
||||
AppName: "preferences",
|
||||
Group: "preferences.grafana.app",
|
||||
PreferredVersion: "v1alpha1",
|
||||
Versions: []app.ManifestVersion{
|
||||
{
|
||||
Name: "v1alpha1",
|
||||
|
|
|
@ -25,7 +25,7 @@ require (
|
|||
github.com/blang/semver/v4 v4.0.0 // 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.12.2 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.13.0 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
|
||||
github.com/go-jose/go-jose/v3 v3.0.4 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.1.2 // indirect
|
||||
|
@ -42,7 +42,7 @@ require (
|
|||
github.com/gorilla/mux v1.8.1 // indirect
|
||||
github.com/grafana/authlib/types v0.0.0-20250926065801-df98203cff37 // indirect
|
||||
github.com/grafana/dskit v0.0.0-20250908063411-6b6da59b5cc4 // indirect
|
||||
github.com/grafana/grafana-app-sdk v0.45.0 // indirect
|
||||
github.com/grafana/grafana-app-sdk v0.46.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/compress v1.18.0 // indirect
|
||||
|
|
|
@ -8,8 +8,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
|||
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.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU=
|
||||
github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
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/go-jose/go-jose/v3 v3.0.4 h1:Wp5HA7bLQcKnf6YYao/4kpRpVMp/yf6+pJKV8WFSaNY=
|
||||
|
@ -58,8 +58,8 @@ github.com/grafana/authlib/types v0.0.0-20250926065801-df98203cff37 h1:qEwZ+7MbP
|
|||
github.com/grafana/authlib/types v0.0.0-20250926065801-df98203cff37/go.mod h1:qeWYbnWzaYGl88JlL9+DsP1GT2Cudm58rLtx13fKZdw=
|
||||
github.com/grafana/dskit v0.0.0-20250908063411-6b6da59b5cc4 h1:jSojuc7njleS3UOz223WDlXOinmuLAIPI0z2vtq8EgI=
|
||||
github.com/grafana/dskit v0.0.0-20250908063411-6b6da59b5cc4/go.mod h1:VahT+GtfQIM+o8ht2StR6J9g+Ef+C2Vokh5uuSmOD/4=
|
||||
github.com/grafana/grafana-app-sdk v0.45.0 h1:niFqYovxuw9vnUB9qoxEgmupqriG7Gns9ZGwB2uuOyE=
|
||||
github.com/grafana/grafana-app-sdk v0.45.0/go.mod h1:1pYGEBrgG8i6pKmmsNXvtAr15jZ4iLtyHU4yj7T6XaI=
|
||||
github.com/grafana/grafana-app-sdk v0.46.0 h1:gvzQvCQgZJ/73BfAcbDt/6TAMhnVikVPxZt/UwDl+oc=
|
||||
github.com/grafana/grafana-app-sdk v0.46.0/go.mod h1:LCTrqR1SwBS13XGVYveBmM7giJDDjzuXK+M9VzPuPWc=
|
||||
github.com/grafana/grafana-app-sdk/logging v0.45.0 h1:0SH6nYZpiLBZRwUq4J6+1vo8xuHKJjnO95/2pGOoA8w=
|
||||
github.com/grafana/grafana-app-sdk/logging v0.45.0/go.mod h1:Gh/nBWnspK3oDNWtiM5qUF/fardHzOIEez+SPI3JeHA=
|
||||
github.com/grafana/grafana/apps/secret v0.0.0-20250902093454-b56b7add012f h1:f+Z5Xpfp1WNYjUe23ginerWsHWUsRgOWrr3WGu3SlWs=
|
||||
|
|
|
@ -222,7 +222,7 @@ func (in JobStatus) ToSyncStatus(jobId string) SyncStatus {
|
|||
|
||||
type JobResourceSummary struct {
|
||||
Group string `json:"group,omitempty"`
|
||||
Resource string `json:"resource,omitempty"`
|
||||
Kind string `json:"kind,omitempty"`
|
||||
Total int64 `json:"total,omitempty"` // the count (if known)
|
||||
|
||||
Create int64 `json:"create,omitempty"`
|
||||
|
|
|
@ -846,7 +846,7 @@ func schema_pkg_apis_provisioning_v0alpha1_JobResourceSummary(ref common.Referen
|
|||
Format: "",
|
||||
},
|
||||
},
|
||||
"resource": {
|
||||
"kind": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
|
|
|
@ -8,7 +8,7 @@ package v0alpha1
|
|||
// with apply.
|
||||
type JobResourceSummaryApplyConfiguration struct {
|
||||
Group *string `json:"group,omitempty"`
|
||||
Resource *string `json:"resource,omitempty"`
|
||||
Kind *string `json:"kind,omitempty"`
|
||||
Total *int64 `json:"total,omitempty"`
|
||||
Create *int64 `json:"create,omitempty"`
|
||||
Update *int64 `json:"update,omitempty"`
|
||||
|
@ -33,11 +33,11 @@ func (b *JobResourceSummaryApplyConfiguration) WithGroup(value string) *JobResou
|
|||
return b
|
||||
}
|
||||
|
||||
// WithResource sets the Resource field in the declarative configuration to the given value
|
||||
// WithKind sets the Kind field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the Resource field is set to the value of the last call.
|
||||
func (b *JobResourceSummaryApplyConfiguration) WithResource(value string) *JobResourceSummaryApplyConfiguration {
|
||||
b.Resource = &value
|
||||
// If called multiple times, the Kind field is set to the value of the last call.
|
||||
func (b *JobResourceSummaryApplyConfiguration) WithKind(value string) *JobResourceSummaryApplyConfiguration {
|
||||
b.Kind = &value
|
||||
return b
|
||||
}
|
||||
|
||||
|
|
105
apps/provisioning/pkg/repository/github/testdata/webhook-push-keep_file_only.json
vendored
Normal file
105
apps/provisioning/pkg/repository/github/testdata/webhook-push-keep_file_only.json
vendored
Normal file
|
@ -0,0 +1,105 @@
|
|||
{
|
||||
"ref": "refs/heads/main",
|
||||
"before": "72096e3adc646c5a5b8a91744f962b12bac06045",
|
||||
"after": "1234567890abcdef1234567890abcdef12345678",
|
||||
"repository": {
|
||||
"id": 888020043,
|
||||
"node_id": "R_kgDONO4cSw",
|
||||
"name": "git-ui-sync-demo",
|
||||
"full_name": "grafana/git-ui-sync-demo",
|
||||
"private": true,
|
||||
"owner": {
|
||||
"name": "grafana",
|
||||
"email": "hello@grafana.com",
|
||||
"login": "grafana",
|
||||
"id": 7195757,
|
||||
"node_id": "MDEyOk9yZ2FuaXphdGlvbjcxOTU3NTc=",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/7195757?v=4",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/grafana",
|
||||
"html_url": "https://github.com/grafana",
|
||||
"type": "Organization",
|
||||
"site_admin": false
|
||||
},
|
||||
"html_url": "https://github.com/grafana/git-ui-sync-demo",
|
||||
"description": "A repository containing Grafana dashboards to demo the Github Sync feature in Grafana.",
|
||||
"fork": false,
|
||||
"url": "https://github.com/grafana/git-ui-sync-demo",
|
||||
"default_branch": "main",
|
||||
"master_branch": "main",
|
||||
"organization": "grafana"
|
||||
},
|
||||
"pusher": {
|
||||
"name": "testuser",
|
||||
"email": "test@grafana.com"
|
||||
},
|
||||
"organization": {
|
||||
"login": "grafana",
|
||||
"id": 7195757,
|
||||
"node_id": "MDEyOk9yZ2FuaXphdGlvbjcxOTU3NTc=",
|
||||
"url": "https://api.github.com/orgs/grafana",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/7195757?v=4"
|
||||
},
|
||||
"sender": {
|
||||
"login": "testuser",
|
||||
"id": 123456,
|
||||
"node_id": "MDQ6VXNlcjEyMzQ1Ng==",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/123456?v=4",
|
||||
"type": "User",
|
||||
"site_admin": false
|
||||
},
|
||||
"created": false,
|
||||
"deleted": false,
|
||||
"forced": false,
|
||||
"base_ref": null,
|
||||
"compare": "https://github.com/grafana/git-ui-sync-demo/compare/72096e3adc64...1234567890ab",
|
||||
"commits": [
|
||||
{
|
||||
"id": "1234567890abcdef1234567890abcdef12345678",
|
||||
"tree_id": "abcdef1234567890abcdef1234567890abcdef12",
|
||||
"distinct": true,
|
||||
"message": "Remove empty folder by deleting .keep file",
|
||||
"timestamp": "2024-12-09T11:00:48+03:00",
|
||||
"url": "https://github.com/grafana/git-ui-sync-demo/commit/1234567890abcdef1234567890abcdef12345678",
|
||||
"author": {
|
||||
"name": "Test User",
|
||||
"email": "test@grafana.com",
|
||||
"username": "testuser"
|
||||
},
|
||||
"committer": {
|
||||
"name": "Test User",
|
||||
"email": "test@grafana.com",
|
||||
"username": "testuser"
|
||||
},
|
||||
"added": [],
|
||||
"removed": [
|
||||
"empty-folder/.keep"
|
||||
],
|
||||
"modified": []
|
||||
}
|
||||
],
|
||||
"head_commit": {
|
||||
"id": "1234567890abcdef1234567890abcdef12345678",
|
||||
"tree_id": "abcdef1234567890abcdef1234567890abcdef12",
|
||||
"distinct": true,
|
||||
"message": "Remove empty folder by deleting .keep file",
|
||||
"timestamp": "2024-12-09T11:00:48+03:00",
|
||||
"url": "https://github.com/grafana/git-ui-sync-demo/commit/1234567890abcdef1234567890abcdef12345678",
|
||||
"author": {
|
||||
"name": "Test User",
|
||||
"email": "test@grafana.com",
|
||||
"username": "testuser"
|
||||
},
|
||||
"committer": {
|
||||
"name": "Test User",
|
||||
"email": "test@grafana.com",
|
||||
"username": "testuser"
|
||||
},
|
||||
"added": [],
|
||||
"removed": [
|
||||
"empty-folder/.keep"
|
||||
],
|
||||
"modified": []
|
||||
}
|
||||
}
|
||||
|
109
apps/provisioning/pkg/repository/github/testdata/webhook-push-keep_file_with_others.json
vendored
Normal file
109
apps/provisioning/pkg/repository/github/testdata/webhook-push-keep_file_with_others.json
vendored
Normal file
|
@ -0,0 +1,109 @@
|
|||
{
|
||||
"ref": "refs/heads/main",
|
||||
"before": "72096e3adc646c5a5b8a91744f962b12bac06045",
|
||||
"after": "2345678901bcdef2345678901bcdef2345678901",
|
||||
"repository": {
|
||||
"id": 888020043,
|
||||
"node_id": "R_kgDONO4cSw",
|
||||
"name": "git-ui-sync-demo",
|
||||
"full_name": "grafana/git-ui-sync-demo",
|
||||
"private": true,
|
||||
"owner": {
|
||||
"name": "grafana",
|
||||
"email": "hello@grafana.com",
|
||||
"login": "grafana",
|
||||
"id": 7195757,
|
||||
"node_id": "MDEyOk9yZ2FuaXphdGlvbjcxOTU3NTc=",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/7195757?v=4",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/grafana",
|
||||
"html_url": "https://github.com/grafana",
|
||||
"type": "Organization",
|
||||
"site_admin": false
|
||||
},
|
||||
"html_url": "https://github.com/grafana/git-ui-sync-demo",
|
||||
"description": "A repository containing Grafana dashboards to demo the Github Sync feature in Grafana.",
|
||||
"fork": false,
|
||||
"url": "https://github.com/grafana/git-ui-sync-demo",
|
||||
"default_branch": "main",
|
||||
"master_branch": "main",
|
||||
"organization": "grafana"
|
||||
},
|
||||
"pusher": {
|
||||
"name": "testuser",
|
||||
"email": "test@grafana.com"
|
||||
},
|
||||
"organization": {
|
||||
"login": "grafana",
|
||||
"id": 7195757,
|
||||
"node_id": "MDEyOk9yZ2FuaXphdGlvbjcxOTU3NTc=",
|
||||
"url": "https://api.github.com/orgs/grafana",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/7195757?v=4"
|
||||
},
|
||||
"sender": {
|
||||
"login": "testuser",
|
||||
"id": 123456,
|
||||
"node_id": "MDQ6VXNlcjEyMzQ1Ng==",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/123456?v=4",
|
||||
"type": "User",
|
||||
"site_admin": false
|
||||
},
|
||||
"created": false,
|
||||
"deleted": false,
|
||||
"forced": false,
|
||||
"base_ref": null,
|
||||
"compare": "https://github.com/grafana/git-ui-sync-demo/compare/72096e3adc64...2345678901bc",
|
||||
"commits": [
|
||||
{
|
||||
"id": "2345678901bcdef2345678901bcdef2345678901",
|
||||
"tree_id": "bcdef2345678901bcdef2345678901bcdef23456",
|
||||
"distinct": true,
|
||||
"message": "Remove folder with .keep and dashboard files",
|
||||
"timestamp": "2024-12-09T11:00:48+03:00",
|
||||
"url": "https://github.com/grafana/git-ui-sync-demo/commit/2345678901bcdef2345678901bcdef2345678901",
|
||||
"author": {
|
||||
"name": "Test User",
|
||||
"email": "test@grafana.com",
|
||||
"username": "testuser"
|
||||
},
|
||||
"committer": {
|
||||
"name": "Test User",
|
||||
"email": "test@grafana.com",
|
||||
"username": "testuser"
|
||||
},
|
||||
"added": [],
|
||||
"removed": [
|
||||
"dashboards/.keep",
|
||||
"dashboards/dashboard1.json",
|
||||
"dashboards/dashboard2.json"
|
||||
],
|
||||
"modified": []
|
||||
}
|
||||
],
|
||||
"head_commit": {
|
||||
"id": "2345678901bcdef2345678901bcdef2345678901",
|
||||
"tree_id": "bcdef2345678901bcdef2345678901bcdef23456",
|
||||
"distinct": true,
|
||||
"message": "Remove folder with .keep and dashboard files",
|
||||
"timestamp": "2024-12-09T11:00:48+03:00",
|
||||
"url": "https://github.com/grafana/git-ui-sync-demo/commit/2345678901bcdef2345678901bcdef2345678901",
|
||||
"author": {
|
||||
"name": "Test User",
|
||||
"email": "test@grafana.com",
|
||||
"username": "testuser"
|
||||
},
|
||||
"committer": {
|
||||
"name": "Test User",
|
||||
"email": "test@grafana.com",
|
||||
"username": "testuser"
|
||||
},
|
||||
"added": [],
|
||||
"removed": [
|
||||
"dashboards/.keep",
|
||||
"dashboards/dashboard1.json",
|
||||
"dashboards/dashboard2.json"
|
||||
],
|
||||
"modified": []
|
||||
}
|
||||
}
|
||||
|
109
apps/provisioning/pkg/repository/github/testdata/webhook-push-multiple_keep_files.json
vendored
Normal file
109
apps/provisioning/pkg/repository/github/testdata/webhook-push-multiple_keep_files.json
vendored
Normal file
|
@ -0,0 +1,109 @@
|
|||
{
|
||||
"ref": "refs/heads/main",
|
||||
"before": "72096e3adc646c5a5b8a91744f962b12bac06045",
|
||||
"after": "3456789012cdef3456789012cdef3456789012cd",
|
||||
"repository": {
|
||||
"id": 888020043,
|
||||
"node_id": "R_kgDONO4cSw",
|
||||
"name": "git-ui-sync-demo",
|
||||
"full_name": "grafana/git-ui-sync-demo",
|
||||
"private": true,
|
||||
"owner": {
|
||||
"name": "grafana",
|
||||
"email": "hello@grafana.com",
|
||||
"login": "grafana",
|
||||
"id": 7195757,
|
||||
"node_id": "MDEyOk9yZ2FuaXphdGlvbjcxOTU3NTc=",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/7195757?v=4",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/grafana",
|
||||
"html_url": "https://github.com/grafana",
|
||||
"type": "Organization",
|
||||
"site_admin": false
|
||||
},
|
||||
"html_url": "https://github.com/grafana/git-ui-sync-demo",
|
||||
"description": "A repository containing Grafana dashboards to demo the Github Sync feature in Grafana.",
|
||||
"fork": false,
|
||||
"url": "https://github.com/grafana/git-ui-sync-demo",
|
||||
"default_branch": "main",
|
||||
"master_branch": "main",
|
||||
"organization": "grafana"
|
||||
},
|
||||
"pusher": {
|
||||
"name": "testuser",
|
||||
"email": "test@grafana.com"
|
||||
},
|
||||
"organization": {
|
||||
"login": "grafana",
|
||||
"id": 7195757,
|
||||
"node_id": "MDEyOk9yZ2FuaXphdGlvbjcxOTU3NTc=",
|
||||
"url": "https://api.github.com/orgs/grafana",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/7195757?v=4"
|
||||
},
|
||||
"sender": {
|
||||
"login": "testuser",
|
||||
"id": 123456,
|
||||
"node_id": "MDQ6VXNlcjEyMzQ1Ng==",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/123456?v=4",
|
||||
"type": "User",
|
||||
"site_admin": false
|
||||
},
|
||||
"created": false,
|
||||
"deleted": false,
|
||||
"forced": false,
|
||||
"base_ref": null,
|
||||
"compare": "https://github.com/grafana/git-ui-sync-demo/compare/72096e3adc64...3456789012cd",
|
||||
"commits": [
|
||||
{
|
||||
"id": "3456789012cdef3456789012cdef3456789012cd",
|
||||
"tree_id": "cdef3456789012cdef3456789012cdef34567890",
|
||||
"distinct": true,
|
||||
"message": "Remove multiple folders, some with only .keep files",
|
||||
"timestamp": "2024-12-09T11:00:48+03:00",
|
||||
"url": "https://github.com/grafana/git-ui-sync-demo/commit/3456789012cdef3456789012cdef3456789012cd",
|
||||
"author": {
|
||||
"name": "Test User",
|
||||
"email": "test@grafana.com",
|
||||
"username": "testuser"
|
||||
},
|
||||
"committer": {
|
||||
"name": "Test User",
|
||||
"email": "test@grafana.com",
|
||||
"username": "testuser"
|
||||
},
|
||||
"added": [],
|
||||
"removed": [
|
||||
"empty-folder1/.keep",
|
||||
"dashboards-to-delete/.keep",
|
||||
"dashboards-to-delete/dashboard.json"
|
||||
],
|
||||
"modified": []
|
||||
}
|
||||
],
|
||||
"head_commit": {
|
||||
"id": "3456789012cdef3456789012cdef3456789012cd",
|
||||
"tree_id": "cdef3456789012cdef3456789012cdef34567890",
|
||||
"distinct": true,
|
||||
"message": "Remove multiple folders, some with only .keep files",
|
||||
"timestamp": "2024-12-09T11:00:48+03:00",
|
||||
"url": "https://github.com/grafana/git-ui-sync-demo/commit/3456789012cdef3456789012cdef3456789012cd",
|
||||
"author": {
|
||||
"name": "Test User",
|
||||
"email": "test@grafana.com",
|
||||
"username": "testuser"
|
||||
},
|
||||
"committer": {
|
||||
"name": "Test User",
|
||||
"email": "test@grafana.com",
|
||||
"username": "testuser"
|
||||
},
|
||||
"added": [],
|
||||
"removed": [
|
||||
"empty-folder1/.keep",
|
||||
"dashboards-to-delete/.keep",
|
||||
"dashboards-to-delete/dashboard.json"
|
||||
],
|
||||
"modified": []
|
||||
}
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@ import (
|
|||
"log/slog"
|
||||
"net/http"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/google/go-github/v70/github"
|
||||
"github.com/google/uuid"
|
||||
|
@ -15,6 +16,7 @@ import (
|
|||
"github.com/grafana/grafana-app-sdk/logging"
|
||||
provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1"
|
||||
"github.com/grafana/grafana/apps/provisioning/pkg/repository"
|
||||
"github.com/grafana/grafana/apps/provisioning/pkg/safepath"
|
||||
common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
|
||||
)
|
||||
|
||||
|
@ -119,13 +121,38 @@ func (r *githubWebhookRepository) parsePushEvent(event *github.PushEvent) (*prov
|
|||
return &provisioning.WebhookResponse{Code: http.StatusOK}, nil
|
||||
}
|
||||
|
||||
// whenever possible, we want to do incremental syncs to keep things performant.
|
||||
// however, if we get an event where just a .keep file is being deleted, and no other files in the folder
|
||||
// are being deleted, the folder could be gone from git, but not from grafana and we do not have a way
|
||||
// to get the grafana uid to delete the folder. so, instead, we will queue a full sync to clean things up.
|
||||
dirsWithKeepDeletes := make(map[string]struct{})
|
||||
dirsWithOtherDeletes := make(map[string]struct{})
|
||||
for _, change := range event.GetCommits() {
|
||||
for _, removedFile := range change.Removed {
|
||||
dir := safepath.Dir(removedFile)
|
||||
if strings.HasSuffix(removedFile, ".keep") {
|
||||
dirsWithKeepDeletes[dir] = struct{}{}
|
||||
} else {
|
||||
dirsWithOtherDeletes[dir] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
// if there are any keep files deleted that do not have other files deleted in the same folder, we need to queue a full sync
|
||||
incremental := true
|
||||
for dir := range dirsWithKeepDeletes {
|
||||
if _, exists := dirsWithOtherDeletes[dir]; !exists {
|
||||
incremental = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return &provisioning.WebhookResponse{
|
||||
Code: http.StatusAccepted,
|
||||
Job: &provisioning.JobSpec{
|
||||
Repository: r.config.GetName(),
|
||||
Action: provisioning.JobActionPull,
|
||||
Pull: &provisioning.SyncJobOptions{
|
||||
Incremental: true,
|
||||
Incremental: incremental,
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
|
|
|
@ -69,6 +69,36 @@ func TestParseWebhooks(t *testing.T) {
|
|||
},
|
||||
},
|
||||
}},
|
||||
{"push", "keep_file_only", provisioning.WebhookResponse{
|
||||
Code: http.StatusAccepted,
|
||||
Job: &provisioning.JobSpec{
|
||||
Repository: "unit-test-repo",
|
||||
Action: provisioning.JobActionPull,
|
||||
Pull: &provisioning.SyncJobOptions{
|
||||
Incremental: false,
|
||||
},
|
||||
},
|
||||
}},
|
||||
{"push", "keep_file_with_others", provisioning.WebhookResponse{
|
||||
Code: http.StatusAccepted,
|
||||
Job: &provisioning.JobSpec{
|
||||
Repository: "unit-test-repo",
|
||||
Action: provisioning.JobActionPull,
|
||||
Pull: &provisioning.SyncJobOptions{
|
||||
Incremental: true,
|
||||
},
|
||||
},
|
||||
}},
|
||||
{"push", "multiple_keep_files", provisioning.WebhookResponse{
|
||||
Code: http.StatusAccepted,
|
||||
Job: &provisioning.JobSpec{
|
||||
Repository: "unit-test-repo",
|
||||
Action: provisioning.JobActionPull,
|
||||
Pull: &provisioning.SyncJobOptions{
|
||||
Incremental: false,
|
||||
},
|
||||
},
|
||||
}},
|
||||
{"issue_comment", "created", provisioning.WebhookResponse{
|
||||
Code: http.StatusNotImplemented,
|
||||
}},
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
module github.com/grafana/grafana/apps/scope
|
||||
|
||||
go 1.24.6
|
||||
|
||||
require (
|
||||
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20251007081214-26e147d01f0a
|
||||
k8s.io/apimachinery v0.34.1
|
||||
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b
|
||||
)
|
||||
|
||||
require (
|
||||
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/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
||||
github.com/go-openapi/jsonreference v0.21.0 // indirect
|
||||
github.com/go-openapi/swag v0.23.0 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/google/gnostic-models v0.7.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/mailru/easyjson v0.9.0 // 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/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/stretchr/objx v0.5.2 // indirect
|
||||
github.com/stretchr/testify v1.11.1 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.2 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/net v0.44.0 // indirect
|
||||
golang.org/x/text v0.29.0 // indirect
|
||||
google.golang.org/protobuf v1.36.9 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // 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.0 // indirect
|
||||
)
|
|
@ -0,0 +1,118 @@
|
|||
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/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.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
|
||||
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
|
||||
github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
|
||||
github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
|
||||
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/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/grafana/grafana/pkg/apimachinery v0.0.0-20251007081214-26e147d01f0a h1:L7xgV9mP6MRF3L2/vDOjNR7heaBPbXPMGTDN9/jXSFQ=
|
||||
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20251007081214-26e147d01f0a/go.mod h1:OK8NwS87D5YphchOcAsiIWk/feMZ0EzfAGME1Kff860=
|
||||
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/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/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
|
||||
github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
|
||||
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/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/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/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.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
|
||||
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
|
||||
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.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I=
|
||||
golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
|
||||
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/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.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
|
||||
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
|
||||
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.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=
|
||||
google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
|
||||
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/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/apimachinery v0.34.1 h1:dTlxFls/eikpJxmAC7MVE8oOeP1zryV7iRyIjB0gky4=
|
||||
k8s.io/apimachinery v0.34.1/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw=
|
||||
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-20250710124328-f3f2b991d03b h1:MloQ9/bdJyIu9lb1PzujOPolHyvO06MXG5TUIj2mNAA=
|
||||
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b/go.mod h1:UZ2yyWbFTpuhSbFhv24aGNOdoRdJZgsIObGBUaYVsts=
|
||||
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.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=
|
||||
sigs.k8s.io/structured-merge-diff/v6 v6.3.0/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=
|
|
@ -0,0 +1,6 @@
|
|||
// +k8s:deepcopy-gen=package
|
||||
// +k8s:openapi-gen=true
|
||||
// +k8s:defaulter-gen=TypeMeta
|
||||
// +groupName=scope.grafana.app
|
||||
|
||||
package v0alpha1 // import "github.com/grafana/grafana/apps/pkg/apis/scope/v0alpha1"
|
|
@ -0,0 +1,168 @@
|
|||
package v0alpha1
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
const (
|
||||
GROUP = "scope.grafana.app"
|
||||
VERSION = "v0alpha1"
|
||||
APIVERSION = GROUP + "/" + VERSION
|
||||
)
|
||||
|
||||
var ScopeResourceInfo = utils.NewResourceInfo(GROUP, VERSION,
|
||||
"scopes", "scope", "Scope",
|
||||
func() runtime.Object { return &Scope{} },
|
||||
func() runtime.Object { return &ScopeList{} },
|
||||
utils.TableColumns{
|
||||
Definition: []metav1.TableColumnDefinition{
|
||||
{Name: "Name", Type: "string", Format: "name"},
|
||||
{Name: "Created At", Type: "date"},
|
||||
{Name: "Title", Type: "string"},
|
||||
{Name: "Filters", Type: "array"},
|
||||
},
|
||||
Reader: func(obj any) ([]interface{}, error) {
|
||||
m, ok := obj.(*Scope)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expected scope")
|
||||
}
|
||||
return []interface{}{
|
||||
m.Name,
|
||||
m.CreationTimestamp.UTC().Format(time.RFC3339),
|
||||
m.Spec.Title,
|
||||
m.Spec.Filters,
|
||||
}, nil
|
||||
},
|
||||
}, // default table converter
|
||||
)
|
||||
|
||||
var ScopeDashboardBindingResourceInfo = utils.NewResourceInfo(GROUP, VERSION,
|
||||
"scopedashboardbindings", "scopedashboardbinding", "ScopeDashboardBinding",
|
||||
func() runtime.Object { return &ScopeDashboardBinding{} },
|
||||
func() runtime.Object { return &ScopeDashboardBindingList{} },
|
||||
utils.TableColumns{
|
||||
Definition: []metav1.TableColumnDefinition{
|
||||
{Name: "Name", Type: "string", Format: "name"},
|
||||
{Name: "Created At", Type: "date"},
|
||||
{Name: "Dashboard", Type: "string"},
|
||||
{Name: "Scope", Type: "string"},
|
||||
},
|
||||
Reader: func(obj any) ([]interface{}, error) {
|
||||
m, ok := obj.(*ScopeDashboardBinding)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expected scope dashboard binding")
|
||||
}
|
||||
return []interface{}{
|
||||
m.Name,
|
||||
m.CreationTimestamp.UTC().Format(time.RFC3339),
|
||||
m.Spec.Dashboard,
|
||||
m.Spec.Scope,
|
||||
}, nil
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
var ScopeNavigationResourceInfo = utils.NewResourceInfo(GROUP, VERSION,
|
||||
"scopenavigations", "scopenavigation", "ScopeNavigation",
|
||||
func() runtime.Object { return &ScopeNavigation{} },
|
||||
func() runtime.Object { return &ScopeNavigationList{} },
|
||||
utils.TableColumns{
|
||||
Definition: []metav1.TableColumnDefinition{
|
||||
{Name: "Name", Type: "string", Format: "name"},
|
||||
{Name: "Created At", Type: "date"},
|
||||
{Name: "URL", Type: "string"},
|
||||
{Name: "Scope", Type: "string"},
|
||||
},
|
||||
Reader: func(obj any) ([]interface{}, error) {
|
||||
m, ok := obj.(*ScopeNavigation)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expected scope navigation")
|
||||
}
|
||||
return []interface{}{
|
||||
m.Name,
|
||||
m.CreationTimestamp.UTC().Format(time.RFC3339),
|
||||
m.Spec.URL,
|
||||
m.Spec.Scope,
|
||||
}, nil
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
var ScopeNodeResourceInfo = utils.NewResourceInfo(GROUP, VERSION,
|
||||
"scopenodes", "scopenode", "ScopeNode",
|
||||
func() runtime.Object { return &ScopeNode{} },
|
||||
func() runtime.Object { return &ScopeNodeList{} },
|
||||
utils.TableColumns{
|
||||
Definition: []metav1.TableColumnDefinition{
|
||||
{Name: "Name", Type: "string", Format: "name"},
|
||||
{Name: "Created At", Type: "date"},
|
||||
{Name: "Title", Type: "string"},
|
||||
{Name: "Parent Name", Type: "string"},
|
||||
{Name: "Node Type", Type: "string"},
|
||||
{Name: "Link Type", Type: "string"},
|
||||
{Name: "Link ID", Type: "string"},
|
||||
},
|
||||
Reader: func(obj any) ([]interface{}, error) {
|
||||
m, ok := obj.(*ScopeNode)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expected scope node")
|
||||
}
|
||||
return []interface{}{
|
||||
m.Name,
|
||||
m.CreationTimestamp.UTC().Format(time.RFC3339),
|
||||
m.Spec.Title,
|
||||
m.Spec.ParentName,
|
||||
m.Spec.NodeType,
|
||||
m.Spec.LinkType,
|
||||
m.Spec.LinkID,
|
||||
}, nil
|
||||
},
|
||||
}, // default table converter
|
||||
)
|
||||
|
||||
var (
|
||||
// SchemeGroupVersion is group version used to register these objects
|
||||
SchemeGroupVersion = schema.GroupVersion{Group: GROUP, Version: VERSION}
|
||||
InternalGroupVersion = schema.GroupVersion{Group: GROUP, Version: runtime.APIVersionInternal}
|
||||
|
||||
// SchemaBuilder is used by standard codegen
|
||||
SchemeBuilder runtime.SchemeBuilder
|
||||
localSchemeBuilder = &SchemeBuilder
|
||||
AddToScheme = localSchemeBuilder.AddToScheme
|
||||
)
|
||||
|
||||
func init() {
|
||||
localSchemeBuilder.Register(func(s *runtime.Scheme) error {
|
||||
return AddKnownTypes(SchemeGroupVersion, s)
|
||||
})
|
||||
}
|
||||
|
||||
// Adds the list of known types to the given scheme.
|
||||
func AddKnownTypes(gv schema.GroupVersion, scheme *runtime.Scheme) error {
|
||||
scheme.AddKnownTypes(gv,
|
||||
&Scope{},
|
||||
&ScopeList{},
|
||||
&ScopeDashboardBinding{},
|
||||
&ScopeDashboardBindingList{},
|
||||
&ScopeNode{},
|
||||
&ScopeNodeList{},
|
||||
&FindScopeNodeChildrenResults{},
|
||||
&FindScopeDashboardBindingsResults{},
|
||||
&ScopeNavigation{},
|
||||
&ScopeNavigationList{},
|
||||
&FindScopeNavigationsResults{},
|
||||
)
|
||||
//metav1.AddToGroupVersion(scheme, gv)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Resource takes an unqualified resource and returns a Group qualified GroupResource
|
||||
func Resource(resource string) schema.GroupResource {
|
||||
return SchemeGroupVersion.WithResource(resource).GroupResource()
|
||||
}
|
|
@ -0,0 +1,238 @@
|
|||
package v0alpha1
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
/*
|
||||
Please keep pkg/promlib/models/query.go and pkg/promlib/models/scope.go in sync
|
||||
with this file until this package is out of the grafana/grafana module.
|
||||
*/
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
type Scope struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
Spec ScopeSpec `json:"spec,omitempty"`
|
||||
}
|
||||
|
||||
type ScopeSpec struct {
|
||||
Title string `json:"title"`
|
||||
// Provides a default path for the scope. This refers to a list of nodes in the selector. This is used to display the title next to the selected scope and expand the selector to the proper path.
|
||||
// This will override whichever is selected from in the selector.
|
||||
// The path is a list of node ids, starting at the direct parent of the selected node towards the root.
|
||||
// +listType=atomic
|
||||
DefaultPath []string `json:"defaultPath,omitempty"`
|
||||
|
||||
// +listType=atomic
|
||||
Filters []ScopeFilter `json:"filters,omitempty"`
|
||||
}
|
||||
|
||||
type ScopeFilter struct {
|
||||
Key string `json:"key"`
|
||||
Value string `json:"value"`
|
||||
// Values is used for operators that require multiple values (e.g. one-of and not-one-of).
|
||||
// +listType=atomic
|
||||
Values []string `json:"values,omitempty"`
|
||||
Operator FilterOperator `json:"operator"`
|
||||
}
|
||||
|
||||
// Type of the filter operator.
|
||||
// +enum
|
||||
type FilterOperator string
|
||||
|
||||
// Defines values for FilterOperator.
|
||||
const (
|
||||
FilterOperatorEquals FilterOperator = "equals"
|
||||
FilterOperatorNotEquals FilterOperator = "not-equals"
|
||||
FilterOperatorRegexMatch FilterOperator = "regex-match"
|
||||
FilterOperatorRegexNotMatch FilterOperator = "regex-not-match"
|
||||
FilterOperatorOneOf FilterOperator = "one-of"
|
||||
FilterOperatorNotOneOf FilterOperator = "not-one-of"
|
||||
)
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
type ScopeList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
|
||||
Items []Scope `json:"items,omitempty"`
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
type ScopeDashboardBinding struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
Spec ScopeDashboardBindingSpec `json:"spec,omitempty"`
|
||||
Status ScopeDashboardBindingStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
type ScopeDashboardBindingList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
|
||||
Items []ScopeDashboardBinding `json:"items,omitempty"`
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
type FindScopeDashboardBindingsResults struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
|
||||
Items []ScopeDashboardBinding `json:"items,omitempty"`
|
||||
Message string `json:"message,omitempty"`
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
type ScopeNode struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
Spec ScopeNodeSpec `json:"spec,omitempty"`
|
||||
}
|
||||
|
||||
type ScopeDashboardBindingSpec struct {
|
||||
Dashboard string `json:"dashboard"`
|
||||
Scope string `json:"scope"`
|
||||
}
|
||||
|
||||
// Type of the item.
|
||||
// +enum
|
||||
// ScopeDashboardBindingStatus contains derived information about a ScopeDashboardBinding.
|
||||
type ScopeDashboardBindingStatus struct {
|
||||
// DashboardTitle should be populated and update from the dashboard
|
||||
DashboardTitle string `json:"dashboardTitle"`
|
||||
|
||||
// Groups is used for the grouping of dashboards that are suggested based
|
||||
// on a scope. The source of truth for this information has not been
|
||||
// determined yet.
|
||||
Groups []string `json:"groups,omitempty"`
|
||||
|
||||
// DashboardTitleConditions is a list of conditions that are used to determine if the dashboard title is valid.
|
||||
// +optional
|
||||
// +listType=map
|
||||
// +listMapKey=type
|
||||
DashboardTitleConditions []metav1.Condition `json:"dashboardTitleConditions,omitempty"`
|
||||
|
||||
// DashboardTitleConditions is a list of conditions that are used to determine if the list of groups is valid.
|
||||
// +optional
|
||||
// +listType=map
|
||||
// +listMapKey=type
|
||||
GroupsConditions []metav1.Condition `json:"groupsConditions,omitempty"`
|
||||
}
|
||||
|
||||
type NodeType string
|
||||
|
||||
// Defines values for ItemType.
|
||||
const (
|
||||
NodeTypeContainer NodeType = "container"
|
||||
NodeTypeLeaf NodeType = "leaf"
|
||||
)
|
||||
|
||||
// Type of the item.
|
||||
// +enum
|
||||
type LinkType string
|
||||
|
||||
// Defines values for ItemType.
|
||||
const (
|
||||
LinkTypeScope LinkType = "scope"
|
||||
)
|
||||
|
||||
type ScopeNodeSpec struct {
|
||||
//+optional
|
||||
ParentName string `json:"parentName,omitempty"`
|
||||
|
||||
NodeType NodeType `json:"nodeType"` // container | leaf
|
||||
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description,omitempty"`
|
||||
DisableMultiSelect bool `json:"disableMultiSelect"`
|
||||
|
||||
LinkType LinkType `json:"linkType,omitempty"` // scope (later more things)
|
||||
LinkID string `json:"linkId,omitempty"` // the k8s name
|
||||
// ?? should this be a slice of links
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
type ScopeNodeList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
|
||||
Items []ScopeNode `json:"items,omitempty"`
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
type FindScopeNodeChildrenResults struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
|
||||
Items []ScopeNode `json:"items,omitempty"`
|
||||
}
|
||||
|
||||
// Scoped navigation types
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
type ScopeNavigation struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
Spec ScopeNavigationSpec `json:"spec,omitempty"`
|
||||
Status ScopeNavigationStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
type ScopeNavigationList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
|
||||
Items []ScopeNavigation `json:"items,omitempty"`
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
type FindScopeNavigationsResults struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
|
||||
Items []ScopeNavigation `json:"items,omitempty"`
|
||||
Message string `json:"message,omitempty"`
|
||||
}
|
||||
|
||||
type ScopeNavigationSpec struct {
|
||||
URL string `json:"url"`
|
||||
Scope string `json:"scope"`
|
||||
}
|
||||
|
||||
// Type of the item.
|
||||
// +enum
|
||||
// ScopeNavigationStatus contains derived information about a ScopeNavigation.
|
||||
type ScopeNavigationStatus struct {
|
||||
// Title should be populated and update from the dashboard
|
||||
Title string `json:"title"`
|
||||
|
||||
// Groups is used for the grouping of dashboards that are suggested based
|
||||
// on a scope. The source of truth for this information has not been
|
||||
// determined yet.
|
||||
Groups []string `json:"groups,omitempty"`
|
||||
|
||||
// TitleConditions is a list of conditions that are used to determine if the title is valid.
|
||||
// +optional
|
||||
// +listType=map
|
||||
// +listMapKey=type
|
||||
TitleConditions []metav1.Condition `json:"titleConditions,omitempty"`
|
||||
|
||||
// GroupsConditions is a list of conditions that are used to determine if the list of groups is valid.
|
||||
// +optional
|
||||
// +listType=map
|
||||
// +listMapKey=type
|
||||
GroupsConditions []metav1.Condition `json:"groupsConditions,omitempty"`
|
||||
}
|
||||
|
||||
// Type of the filter operator.
|
||||
// +enum
|
||||
type ScopeNavigationLinkType string
|
||||
|
||||
// Defines values for FilterOperator.
|
||||
const (
|
||||
ScopeNavigationLinkTypeURL ScopeNavigationLinkType = "url"
|
||||
)
|
|
@ -0,0 +1,519 @@
|
|||
//go:build !ignore_autogenerated
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
// Code generated by deepcopy-gen. DO NOT EDIT.
|
||||
|
||||
package v0alpha1
|
||||
|
||||
import (
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *FindScopeDashboardBindingsResults) DeepCopyInto(out *FindScopeDashboardBindingsResults) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = make([]ScopeDashboardBinding, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FindScopeDashboardBindingsResults.
|
||||
func (in *FindScopeDashboardBindingsResults) DeepCopy() *FindScopeDashboardBindingsResults {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(FindScopeDashboardBindingsResults)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *FindScopeDashboardBindingsResults) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *FindScopeNavigationsResults) DeepCopyInto(out *FindScopeNavigationsResults) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = make([]ScopeNavigation, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FindScopeNavigationsResults.
|
||||
func (in *FindScopeNavigationsResults) DeepCopy() *FindScopeNavigationsResults {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(FindScopeNavigationsResults)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *FindScopeNavigationsResults) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *FindScopeNodeChildrenResults) DeepCopyInto(out *FindScopeNodeChildrenResults) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = make([]ScopeNode, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FindScopeNodeChildrenResults.
|
||||
func (in *FindScopeNodeChildrenResults) DeepCopy() *FindScopeNodeChildrenResults {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(FindScopeNodeChildrenResults)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *FindScopeNodeChildrenResults) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Scope) DeepCopyInto(out *Scope) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
in.Spec.DeepCopyInto(&out.Spec)
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Scope.
|
||||
func (in *Scope) DeepCopy() *Scope {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Scope)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *Scope) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ScopeDashboardBinding) DeepCopyInto(out *ScopeDashboardBinding) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
out.Spec = in.Spec
|
||||
in.Status.DeepCopyInto(&out.Status)
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScopeDashboardBinding.
|
||||
func (in *ScopeDashboardBinding) DeepCopy() *ScopeDashboardBinding {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ScopeDashboardBinding)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *ScopeDashboardBinding) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ScopeDashboardBindingList) DeepCopyInto(out *ScopeDashboardBindingList) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = make([]ScopeDashboardBinding, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScopeDashboardBindingList.
|
||||
func (in *ScopeDashboardBindingList) DeepCopy() *ScopeDashboardBindingList {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ScopeDashboardBindingList)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *ScopeDashboardBindingList) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ScopeDashboardBindingSpec) DeepCopyInto(out *ScopeDashboardBindingSpec) {
|
||||
*out = *in
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScopeDashboardBindingSpec.
|
||||
func (in *ScopeDashboardBindingSpec) DeepCopy() *ScopeDashboardBindingSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ScopeDashboardBindingSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ScopeDashboardBindingStatus) DeepCopyInto(out *ScopeDashboardBindingStatus) {
|
||||
*out = *in
|
||||
if in.Groups != nil {
|
||||
in, out := &in.Groups, &out.Groups
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.DashboardTitleConditions != nil {
|
||||
in, out := &in.DashboardTitleConditions, &out.DashboardTitleConditions
|
||||
*out = make([]v1.Condition, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
if in.GroupsConditions != nil {
|
||||
in, out := &in.GroupsConditions, &out.GroupsConditions
|
||||
*out = make([]v1.Condition, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScopeDashboardBindingStatus.
|
||||
func (in *ScopeDashboardBindingStatus) DeepCopy() *ScopeDashboardBindingStatus {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ScopeDashboardBindingStatus)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ScopeFilter) DeepCopyInto(out *ScopeFilter) {
|
||||
*out = *in
|
||||
if in.Values != nil {
|
||||
in, out := &in.Values, &out.Values
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScopeFilter.
|
||||
func (in *ScopeFilter) DeepCopy() *ScopeFilter {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ScopeFilter)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ScopeList) DeepCopyInto(out *ScopeList) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = make([]Scope, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScopeList.
|
||||
func (in *ScopeList) DeepCopy() *ScopeList {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ScopeList)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *ScopeList) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ScopeNavigation) DeepCopyInto(out *ScopeNavigation) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
out.Spec = in.Spec
|
||||
in.Status.DeepCopyInto(&out.Status)
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScopeNavigation.
|
||||
func (in *ScopeNavigation) DeepCopy() *ScopeNavigation {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ScopeNavigation)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *ScopeNavigation) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ScopeNavigationList) DeepCopyInto(out *ScopeNavigationList) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = make([]ScopeNavigation, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScopeNavigationList.
|
||||
func (in *ScopeNavigationList) DeepCopy() *ScopeNavigationList {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ScopeNavigationList)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *ScopeNavigationList) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ScopeNavigationSpec) DeepCopyInto(out *ScopeNavigationSpec) {
|
||||
*out = *in
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScopeNavigationSpec.
|
||||
func (in *ScopeNavigationSpec) DeepCopy() *ScopeNavigationSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ScopeNavigationSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ScopeNavigationStatus) DeepCopyInto(out *ScopeNavigationStatus) {
|
||||
*out = *in
|
||||
if in.Groups != nil {
|
||||
in, out := &in.Groups, &out.Groups
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.TitleConditions != nil {
|
||||
in, out := &in.TitleConditions, &out.TitleConditions
|
||||
*out = make([]v1.Condition, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
if in.GroupsConditions != nil {
|
||||
in, out := &in.GroupsConditions, &out.GroupsConditions
|
||||
*out = make([]v1.Condition, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScopeNavigationStatus.
|
||||
func (in *ScopeNavigationStatus) DeepCopy() *ScopeNavigationStatus {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ScopeNavigationStatus)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ScopeNode) DeepCopyInto(out *ScopeNode) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
out.Spec = in.Spec
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScopeNode.
|
||||
func (in *ScopeNode) DeepCopy() *ScopeNode {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ScopeNode)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *ScopeNode) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ScopeNodeList) DeepCopyInto(out *ScopeNodeList) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = make([]ScopeNode, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScopeNodeList.
|
||||
func (in *ScopeNodeList) DeepCopy() *ScopeNodeList {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ScopeNodeList)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *ScopeNodeList) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ScopeNodeSpec) DeepCopyInto(out *ScopeNodeSpec) {
|
||||
*out = *in
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScopeNodeSpec.
|
||||
func (in *ScopeNodeSpec) DeepCopy() *ScopeNodeSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ScopeNodeSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ScopeSpec) DeepCopyInto(out *ScopeSpec) {
|
||||
*out = *in
|
||||
if in.DefaultPath != nil {
|
||||
in, out := &in.DefaultPath, &out.DefaultPath
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.Filters != nil {
|
||||
in, out := &in.Filters, &out.Filters
|
||||
*out = make([]ScopeFilter, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScopeSpec.
|
||||
func (in *ScopeSpec) DeepCopy() *ScopeSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ScopeSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
//go:build !ignore_autogenerated
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
// Code generated by defaulter-gen. DO NOT EDIT.
|
||||
|
||||
package v0alpha1
|
||||
|
||||
import (
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// RegisterDefaults adds defaulters functions to the given scheme.
|
||||
// Public to allow building arbitrary schemes.
|
||||
// All generated defaulters are covering - they call all nested defaulters.
|
||||
func RegisterDefaults(scheme *runtime.Scheme) error {
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,934 @@
|
|||
//go:build !ignore_autogenerated
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
// Code generated by openapi-gen. DO NOT EDIT.
|
||||
|
||||
package v0alpha1
|
||||
|
||||
import (
|
||||
common "k8s.io/kube-openapi/pkg/common"
|
||||
spec "k8s.io/kube-openapi/pkg/validation/spec"
|
||||
)
|
||||
|
||||
func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition {
|
||||
return map[string]common.OpenAPIDefinition{
|
||||
"github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1.FindScopeDashboardBindingsResults": schema_pkg_apis_scope_v0alpha1_FindScopeDashboardBindingsResults(ref),
|
||||
"github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1.FindScopeNavigationsResults": schema_pkg_apis_scope_v0alpha1_FindScopeNavigationsResults(ref),
|
||||
"github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1.FindScopeNodeChildrenResults": schema_pkg_apis_scope_v0alpha1_FindScopeNodeChildrenResults(ref),
|
||||
"github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1.Scope": schema_pkg_apis_scope_v0alpha1_Scope(ref),
|
||||
"github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1.ScopeDashboardBinding": schema_pkg_apis_scope_v0alpha1_ScopeDashboardBinding(ref),
|
||||
"github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1.ScopeDashboardBindingList": schema_pkg_apis_scope_v0alpha1_ScopeDashboardBindingList(ref),
|
||||
"github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1.ScopeDashboardBindingSpec": schema_pkg_apis_scope_v0alpha1_ScopeDashboardBindingSpec(ref),
|
||||
"github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1.ScopeDashboardBindingStatus": schema_pkg_apis_scope_v0alpha1_ScopeDashboardBindingStatus(ref),
|
||||
"github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1.ScopeFilter": schema_pkg_apis_scope_v0alpha1_ScopeFilter(ref),
|
||||
"github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1.ScopeList": schema_pkg_apis_scope_v0alpha1_ScopeList(ref),
|
||||
"github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1.ScopeNavigation": schema_pkg_apis_scope_v0alpha1_ScopeNavigation(ref),
|
||||
"github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1.ScopeNavigationList": schema_pkg_apis_scope_v0alpha1_ScopeNavigationList(ref),
|
||||
"github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1.ScopeNavigationSpec": schema_pkg_apis_scope_v0alpha1_ScopeNavigationSpec(ref),
|
||||
"github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1.ScopeNavigationStatus": schema_pkg_apis_scope_v0alpha1_ScopeNavigationStatus(ref),
|
||||
"github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1.ScopeNode": schema_pkg_apis_scope_v0alpha1_ScopeNode(ref),
|
||||
"github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1.ScopeNodeList": schema_pkg_apis_scope_v0alpha1_ScopeNodeList(ref),
|
||||
"github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1.ScopeNodeSpec": schema_pkg_apis_scope_v0alpha1_ScopeNodeSpec(ref),
|
||||
"github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1.ScopeSpec": schema_pkg_apis_scope_v0alpha1_ScopeSpec(ref),
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_scope_v0alpha1_FindScopeDashboardBindingsResults(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"kind": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"apiVersion": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"items": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"array"},
|
||||
Items: &spec.SchemaOrArray{
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1.ScopeDashboardBinding"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"message": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
"github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1.ScopeDashboardBinding"},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_scope_v0alpha1_FindScopeNavigationsResults(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"kind": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"apiVersion": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"items": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"array"},
|
||||
Items: &spec.SchemaOrArray{
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1.ScopeNavigation"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"message": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
"github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1.ScopeNavigation"},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_scope_v0alpha1_FindScopeNodeChildrenResults(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"kind": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"apiVersion": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"metadata": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"),
|
||||
},
|
||||
},
|
||||
"items": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"array"},
|
||||
Items: &spec.SchemaOrArray{
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1.ScopeNode"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
"github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1.ScopeNode", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_scope_v0alpha1_Scope(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"kind": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"apiVersion": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"metadata": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"),
|
||||
},
|
||||
},
|
||||
"spec": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1.ScopeSpec"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
"github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1.ScopeSpec", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_scope_v0alpha1_ScopeDashboardBinding(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"kind": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"apiVersion": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"metadata": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"),
|
||||
},
|
||||
},
|
||||
"spec": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1.ScopeDashboardBindingSpec"),
|
||||
},
|
||||
},
|
||||
"status": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1.ScopeDashboardBindingStatus"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
"github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1.ScopeDashboardBindingSpec", "github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1.ScopeDashboardBindingStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_scope_v0alpha1_ScopeDashboardBindingList(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"kind": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"apiVersion": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"metadata": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"),
|
||||
},
|
||||
},
|
||||
"items": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"array"},
|
||||
Items: &spec.SchemaOrArray{
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1.ScopeDashboardBinding"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
"github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1.ScopeDashboardBinding", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_scope_v0alpha1_ScopeDashboardBindingSpec(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"dashboard": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"scope": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"dashboard", "scope"},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_scope_v0alpha1_ScopeDashboardBindingStatus(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Type of the item. ScopeDashboardBindingStatus contains derived information about a ScopeDashboardBinding.",
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"dashboardTitle": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "DashboardTitle should be populated and update from the dashboard",
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"groups": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Groups is used for the grouping of dashboards that are suggested based on a scope. The source of truth for this information has not been determined yet.",
|
||||
Type: []string{"array"},
|
||||
Items: &spec.SchemaOrArray{
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"dashboardTitleConditions": {
|
||||
VendorExtensible: spec.VendorExtensible{
|
||||
Extensions: spec.Extensions{
|
||||
"x-kubernetes-list-map-keys": []interface{}{
|
||||
"type",
|
||||
},
|
||||
"x-kubernetes-list-type": "map",
|
||||
},
|
||||
},
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "DashboardTitleConditions is a list of conditions that are used to determine if the dashboard title is valid.",
|
||||
Type: []string{"array"},
|
||||
Items: &spec.SchemaOrArray{
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Condition"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"groupsConditions": {
|
||||
VendorExtensible: spec.VendorExtensible{
|
||||
Extensions: spec.Extensions{
|
||||
"x-kubernetes-list-map-keys": []interface{}{
|
||||
"type",
|
||||
},
|
||||
"x-kubernetes-list-type": "map",
|
||||
},
|
||||
},
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "DashboardTitleConditions is a list of conditions that are used to determine if the list of groups is valid.",
|
||||
Type: []string{"array"},
|
||||
Items: &spec.SchemaOrArray{
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Condition"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"dashboardTitle"},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1.Condition"},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_scope_v0alpha1_ScopeFilter(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"key": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"value": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"values": {
|
||||
VendorExtensible: spec.VendorExtensible{
|
||||
Extensions: spec.Extensions{
|
||||
"x-kubernetes-list-type": "atomic",
|
||||
},
|
||||
},
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Values is used for operators that require multiple values (e.g. one-of and not-one-of).",
|
||||
Type: []string{"array"},
|
||||
Items: &spec.SchemaOrArray{
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"operator": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Possible enum values:\n - `\"equals\"`\n - `\"not-equals\"`\n - `\"not-one-of\"`\n - `\"one-of\"`\n - `\"regex-match\"`\n - `\"regex-not-match\"`",
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
Enum: []interface{}{"equals", "not-equals", "not-one-of", "one-of", "regex-match", "regex-not-match"},
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"key", "value", "operator"},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_scope_v0alpha1_ScopeList(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"kind": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"apiVersion": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"metadata": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"),
|
||||
},
|
||||
},
|
||||
"items": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"array"},
|
||||
Items: &spec.SchemaOrArray{
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1.Scope"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
"github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1.Scope", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_scope_v0alpha1_ScopeNavigation(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"kind": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"apiVersion": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"metadata": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"),
|
||||
},
|
||||
},
|
||||
"spec": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1.ScopeNavigationSpec"),
|
||||
},
|
||||
},
|
||||
"status": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1.ScopeNavigationStatus"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
"github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1.ScopeNavigationSpec", "github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1.ScopeNavigationStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_scope_v0alpha1_ScopeNavigationList(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"kind": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"apiVersion": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"metadata": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"),
|
||||
},
|
||||
},
|
||||
"items": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"array"},
|
||||
Items: &spec.SchemaOrArray{
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1.ScopeNavigation"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
"github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1.ScopeNavigation", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_scope_v0alpha1_ScopeNavigationSpec(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"url": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"scope": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"url", "scope"},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_scope_v0alpha1_ScopeNavigationStatus(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Type of the item. ScopeNavigationStatus contains derived information about a ScopeNavigation.",
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"title": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Title should be populated and update from the dashboard",
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"groups": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Groups is used for the grouping of dashboards that are suggested based on a scope. The source of truth for this information has not been determined yet.",
|
||||
Type: []string{"array"},
|
||||
Items: &spec.SchemaOrArray{
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"titleConditions": {
|
||||
VendorExtensible: spec.VendorExtensible{
|
||||
Extensions: spec.Extensions{
|
||||
"x-kubernetes-list-map-keys": []interface{}{
|
||||
"type",
|
||||
},
|
||||
"x-kubernetes-list-type": "map",
|
||||
},
|
||||
},
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "TitleConditions is a list of conditions that are used to determine if the title is valid.",
|
||||
Type: []string{"array"},
|
||||
Items: &spec.SchemaOrArray{
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Condition"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"groupsConditions": {
|
||||
VendorExtensible: spec.VendorExtensible{
|
||||
Extensions: spec.Extensions{
|
||||
"x-kubernetes-list-map-keys": []interface{}{
|
||||
"type",
|
||||
},
|
||||
"x-kubernetes-list-type": "map",
|
||||
},
|
||||
},
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "GroupsConditions is a list of conditions that are used to determine if the list of groups is valid.",
|
||||
Type: []string{"array"},
|
||||
Items: &spec.SchemaOrArray{
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Condition"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"title"},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1.Condition"},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_scope_v0alpha1_ScopeNode(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"kind": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"apiVersion": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"metadata": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"),
|
||||
},
|
||||
},
|
||||
"spec": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1.ScopeNodeSpec"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
"github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1.ScopeNodeSpec", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_scope_v0alpha1_ScopeNodeList(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"kind": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"apiVersion": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"metadata": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"),
|
||||
},
|
||||
},
|
||||
"items": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"array"},
|
||||
Items: &spec.SchemaOrArray{
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1.ScopeNode"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
"github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1.ScopeNode", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_scope_v0alpha1_ScopeNodeSpec(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"parentName": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"nodeType": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"title": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"description": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"disableMultiSelect": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: false,
|
||||
Type: []string{"boolean"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"linkType": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Possible enum values:\n - `\"scope\"`",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
Enum: []interface{}{"scope"},
|
||||
},
|
||||
},
|
||||
"linkId": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "scope (later more things)",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"nodeType", "title", "disableMultiSelect"},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_scope_v0alpha1_ScopeSpec(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"title": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"defaultPath": {
|
||||
VendorExtensible: spec.VendorExtensible{
|
||||
Extensions: spec.Extensions{
|
||||
"x-kubernetes-list-type": "atomic",
|
||||
},
|
||||
},
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Provides a default path for the scope. This refers to a list of nodes in the selector. This is used to display the title next to the selected scope and expand the selector to the proper path. This will override whichever is selected from in the selector. The path is a list of node ids, starting at the direct parent of the selected node towards the root.",
|
||||
Type: []string{"array"},
|
||||
Items: &spec.SchemaOrArray{
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"filters": {
|
||||
VendorExtensible: spec.VendorExtensible{
|
||||
Extensions: spec.Extensions{
|
||||
"x-kubernetes-list-type": "atomic",
|
||||
},
|
||||
},
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"array"},
|
||||
Items: &spec.SchemaOrArray{
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1.ScopeFilter"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"title"},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
"github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1.ScopeFilter"},
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1,FindScopeDashboardBindingsResults,Items
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1,FindScopeNavigationsResults,Items
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1,ScopeDashboardBindingStatus,Groups
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1,ScopeNavigationStatus,Groups
|
||||
API rule violation: names_match,github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1,ScopeNodeSpec,LinkID
|
||||
API rule violation: streaming_list_type_json_tags,github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1,FindScopeNodeChildrenResults,Items
|
||||
API rule violation: streaming_list_type_json_tags,github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1,ScopeDashboardBindingList,Items
|
||||
API rule violation: streaming_list_type_json_tags,github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1,ScopeList,Items
|
||||
API rule violation: streaming_list_type_json_tags,github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1,ScopeNavigationList,Items
|
||||
API rule violation: streaming_list_type_json_tags,github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1,ScopeNodeList,Items
|
|
@ -1,4 +1,4 @@
|
|||
APP_SDK_VERSION = v0.45.0
|
||||
APP_SDK_VERSION = v0.46.0
|
||||
APP_SDK_DIR = $(shell go env GOPATH)/bin/app-sdk-$(APP_SDK_VERSION)
|
||||
APP_SDK_BIN = $(APP_SDK_DIR)/grafana-app-sdk
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ module github.com/grafana/grafana/apps/secret
|
|||
go 1.24.6
|
||||
|
||||
require (
|
||||
github.com/grafana/grafana-app-sdk v0.45.0
|
||||
github.com/grafana/grafana-app-sdk v0.46.0
|
||||
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250710134100-1f3dc0533caf
|
||||
github.com/stretchr/testify v1.11.1
|
||||
google.golang.org/grpc v1.75.1
|
||||
|
@ -18,7 +18,7 @@ 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.12.2 // 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
|
||||
|
|
|
@ -6,8 +6,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
|||
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.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU=
|
||||
github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
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=
|
||||
|
@ -35,8 +35,8 @@ github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX
|
|||
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.45.0 h1:niFqYovxuw9vnUB9qoxEgmupqriG7Gns9ZGwB2uuOyE=
|
||||
github.com/grafana/grafana-app-sdk v0.45.0/go.mod h1:1pYGEBrgG8i6pKmmsNXvtAr15jZ4iLtyHU4yj7T6XaI=
|
||||
github.com/grafana/grafana-app-sdk v0.46.0 h1:gvzQvCQgZJ/73BfAcbDt/6TAMhnVikVPxZt/UwDl+oc=
|
||||
github.com/grafana/grafana-app-sdk v0.46.0/go.mod h1:LCTrqR1SwBS13XGVYveBmM7giJDDjzuXK+M9VzPuPWc=
|
||||
github.com/grafana/grafana-app-sdk/logging v0.45.0 h1:0SH6nYZpiLBZRwUq4J6+1vo8xuHKJjnO95/2pGOoA8w=
|
||||
github.com/grafana/grafana-app-sdk/logging v0.45.0/go.mod h1:Gh/nBWnspK3oDNWtiM5qUF/fardHzOIEez+SPI3JeHA=
|
||||
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250710134100-1f3dc0533caf h1:BBGDHffvVNLoYQlXEpbXcxE0vbpq7pm/8OWF5I+UDZg=
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
package v1beta1
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
type KeeperClient struct {
|
||||
client *resource.TypedClient[*Keeper, *KeeperList]
|
||||
}
|
||||
|
||||
func NewKeeperClient(client resource.Client) *KeeperClient {
|
||||
return &KeeperClient{
|
||||
client: resource.NewTypedClient[*Keeper, *KeeperList](client, KeeperKind()),
|
||||
}
|
||||
}
|
||||
|
||||
func NewKeeperClientFromGenerator(generator resource.ClientGenerator) (*KeeperClient, error) {
|
||||
c, err := generator.ClientFor(KeeperKind())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewKeeperClient(c), nil
|
||||
}
|
||||
|
||||
func (c *KeeperClient) Get(ctx context.Context, identifier resource.Identifier) (*Keeper, error) {
|
||||
return c.client.Get(ctx, identifier)
|
||||
}
|
||||
|
||||
func (c *KeeperClient) List(ctx context.Context, namespace string, opts resource.ListOptions) (*KeeperList, error) {
|
||||
return c.client.List(ctx, namespace, opts)
|
||||
}
|
||||
|
||||
func (c *KeeperClient) ListAll(ctx context.Context, namespace string, opts resource.ListOptions) (*KeeperList, 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 *KeeperClient) Create(ctx context.Context, obj *Keeper, opts resource.CreateOptions) (*Keeper, error) {
|
||||
// Make sure apiVersion and kind are set
|
||||
obj.APIVersion = GroupVersion.Identifier()
|
||||
obj.Kind = KeeperKind().Kind()
|
||||
return c.client.Create(ctx, obj, opts)
|
||||
}
|
||||
|
||||
func (c *KeeperClient) Update(ctx context.Context, obj *Keeper, opts resource.UpdateOptions) (*Keeper, error) {
|
||||
return c.client.Update(ctx, obj, opts)
|
||||
}
|
||||
|
||||
func (c *KeeperClient) Patch(ctx context.Context, identifier resource.Identifier, req resource.PatchRequest, opts resource.PatchOptions) (*Keeper, error) {
|
||||
return c.client.Patch(ctx, identifier, req, opts)
|
||||
}
|
||||
|
||||
func (c *KeeperClient) UpdateStatus(ctx context.Context, identifier resource.Identifier, newStatus KeeperStatus, opts resource.UpdateOptions) (*Keeper, error) {
|
||||
return c.client.Update(ctx, &Keeper{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: KeeperKind().Kind(),
|
||||
APIVersion: GroupVersion.Identifier(),
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
ResourceVersion: opts.ResourceVersion,
|
||||
Namespace: identifier.Namespace,
|
||||
Name: identifier.Name,
|
||||
},
|
||||
Status: newStatus,
|
||||
}, resource.UpdateOptions{
|
||||
Subresource: "status",
|
||||
ResourceVersion: opts.ResourceVersion,
|
||||
})
|
||||
}
|
||||
|
||||
func (c *KeeperClient) Delete(ctx context.Context, identifier resource.Identifier, opts resource.DeleteOptions) error {
|
||||
return c.client.Delete(ctx, identifier, opts)
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
package v1beta1
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
type SecureValueClient struct {
|
||||
client *resource.TypedClient[*SecureValue, *SecureValueList]
|
||||
}
|
||||
|
||||
func NewSecureValueClient(client resource.Client) *SecureValueClient {
|
||||
return &SecureValueClient{
|
||||
client: resource.NewTypedClient[*SecureValue, *SecureValueList](client, SecureValueKind()),
|
||||
}
|
||||
}
|
||||
|
||||
func NewSecureValueClientFromGenerator(generator resource.ClientGenerator) (*SecureValueClient, error) {
|
||||
c, err := generator.ClientFor(SecureValueKind())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewSecureValueClient(c), nil
|
||||
}
|
||||
|
||||
func (c *SecureValueClient) Get(ctx context.Context, identifier resource.Identifier) (*SecureValue, error) {
|
||||
return c.client.Get(ctx, identifier)
|
||||
}
|
||||
|
||||
func (c *SecureValueClient) List(ctx context.Context, namespace string, opts resource.ListOptions) (*SecureValueList, error) {
|
||||
return c.client.List(ctx, namespace, opts)
|
||||
}
|
||||
|
||||
func (c *SecureValueClient) ListAll(ctx context.Context, namespace string, opts resource.ListOptions) (*SecureValueList, 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 *SecureValueClient) Create(ctx context.Context, obj *SecureValue, opts resource.CreateOptions) (*SecureValue, error) {
|
||||
// Make sure apiVersion and kind are set
|
||||
obj.APIVersion = GroupVersion.Identifier()
|
||||
obj.Kind = SecureValueKind().Kind()
|
||||
return c.client.Create(ctx, obj, opts)
|
||||
}
|
||||
|
||||
func (c *SecureValueClient) Update(ctx context.Context, obj *SecureValue, opts resource.UpdateOptions) (*SecureValue, error) {
|
||||
return c.client.Update(ctx, obj, opts)
|
||||
}
|
||||
|
||||
func (c *SecureValueClient) Patch(ctx context.Context, identifier resource.Identifier, req resource.PatchRequest, opts resource.PatchOptions) (*SecureValue, error) {
|
||||
return c.client.Patch(ctx, identifier, req, opts)
|
||||
}
|
||||
|
||||
func (c *SecureValueClient) UpdateStatus(ctx context.Context, identifier resource.Identifier, newStatus SecureValueStatus, opts resource.UpdateOptions) (*SecureValue, error) {
|
||||
return c.client.Update(ctx, &SecureValue{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: SecureValueKind().Kind(),
|
||||
APIVersion: GroupVersion.Identifier(),
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
ResourceVersion: opts.ResourceVersion,
|
||||
Namespace: identifier.Namespace,
|
||||
Name: identifier.Name,
|
||||
},
|
||||
Status: newStatus,
|
||||
}, resource.UpdateOptions{
|
||||
Subresource: "status",
|
||||
ResourceVersion: opts.ResourceVersion,
|
||||
})
|
||||
}
|
||||
|
||||
func (c *SecureValueClient) Delete(ctx context.Context, identifier resource.Identifier, opts resource.DeleteOptions) error {
|
||||
return c.client.Delete(ctx, identifier, opts)
|
||||
}
|
|
@ -11,6 +11,8 @@ import (
|
|||
|
||||
"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"
|
||||
|
||||
v1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1"
|
||||
)
|
||||
|
@ -18,6 +20,7 @@ import (
|
|||
var appManifestData = app.ManifestData{
|
||||
AppName: "secret",
|
||||
Group: "secret.grafana.app",
|
||||
PreferredVersion: "v1beta1",
|
||||
Versions: []app.ManifestVersion{
|
||||
{
|
||||
Name: "v1beta1",
|
||||
|
@ -37,6 +40,10 @@ var appManifestData = app.ManifestData{
|
|||
Conversion: false,
|
||||
},
|
||||
},
|
||||
Routes: app.ManifestVersionRoutes{
|
||||
Namespaced: map[string]spec3.PathProps{},
|
||||
Cluster: map[string]spec3.PathProps{},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -66,6 +73,7 @@ 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:]
|
||||
|
@ -73,3 +81,42 @@ func ManifestCustomRouteResponsesAssociator(kind, version, path, verb string) (g
|
|||
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)
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ module github.com/grafana/grafana/apps/shorturl
|
|||
go 1.24.6
|
||||
|
||||
require (
|
||||
github.com/grafana/grafana-app-sdk v0.45.0
|
||||
github.com/grafana/grafana-app-sdk v0.46.0
|
||||
github.com/grafana/grafana-app-sdk/logging v0.45.0
|
||||
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250915132226-585b53bc7dba
|
||||
k8s.io/apimachinery v0.34.1
|
||||
|
@ -17,7 +17,7 @@ require (
|
|||
github.com/cenkalti/backoff/v5 v5.0.3 // 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.12.2 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.13.0 // indirect
|
||||
github.com/evanphx/json-patch v5.9.11+incompatible // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
|
||||
github.com/getkin/kin-openapi v0.133.0 // indirect
|
||||
|
|
|
@ -10,8 +10,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
|||
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.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU=
|
||||
github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
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/evanphx/json-patch v5.9.11+incompatible h1:ixHHqfcGvxhWkniF1tWxBHA0yb4Z+d1UQi45df52xW8=
|
||||
github.com/evanphx/json-patch v5.9.11+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
|
||||
|
@ -54,8 +54,8 @@ github.com/grafana/authlib/types v0.0.0-20250926065801-df98203cff37 h1:qEwZ+7MbP
|
|||
github.com/grafana/authlib/types v0.0.0-20250926065801-df98203cff37/go.mod h1:qeWYbnWzaYGl88JlL9+DsP1GT2Cudm58rLtx13fKZdw=
|
||||
github.com/grafana/dskit v0.0.0-20250908063411-6b6da59b5cc4 h1:jSojuc7njleS3UOz223WDlXOinmuLAIPI0z2vtq8EgI=
|
||||
github.com/grafana/dskit v0.0.0-20250908063411-6b6da59b5cc4/go.mod h1:VahT+GtfQIM+o8ht2StR6J9g+Ef+C2Vokh5uuSmOD/4=
|
||||
github.com/grafana/grafana-app-sdk v0.45.0 h1:niFqYovxuw9vnUB9qoxEgmupqriG7Gns9ZGwB2uuOyE=
|
||||
github.com/grafana/grafana-app-sdk v0.45.0/go.mod h1:1pYGEBrgG8i6pKmmsNXvtAr15jZ4iLtyHU4yj7T6XaI=
|
||||
github.com/grafana/grafana-app-sdk v0.46.0 h1:gvzQvCQgZJ/73BfAcbDt/6TAMhnVikVPxZt/UwDl+oc=
|
||||
github.com/grafana/grafana-app-sdk v0.46.0/go.mod h1:LCTrqR1SwBS13XGVYveBmM7giJDDjzuXK+M9VzPuPWc=
|
||||
github.com/grafana/grafana-app-sdk/logging v0.45.0 h1:0SH6nYZpiLBZRwUq4J6+1vo8xuHKJjnO95/2pGOoA8w=
|
||||
github.com/grafana/grafana-app-sdk/logging v0.45.0/go.mod h1:Gh/nBWnspK3oDNWtiM5qUF/fardHzOIEez+SPI3JeHA=
|
||||
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250915132226-585b53bc7dba h1:Qam8QzVRsyZN39zgZ9Vj6e8PEfswvv2McnqCZ/v5NcI=
|
||||
|
|
|
@ -20,6 +20,7 @@ shorturl: {
|
|||
response: {
|
||||
url: string
|
||||
}
|
||||
responseMetadata: typeMeta: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ var (
|
|||
var appManifestData = app.ManifestData{
|
||||
AppName: "shorturl",
|
||||
Group: "shorturl.grafana.app",
|
||||
PreferredVersion: "v1alpha1",
|
||||
Versions: []app.ManifestVersion{
|
||||
{
|
||||
Name: "v1alpha1",
|
||||
|
@ -52,7 +53,7 @@ var appManifestData = app.ManifestData{
|
|||
Get: &spec3.Operation{
|
||||
OperationProps: spec3.OperationProps{
|
||||
|
||||
OperationId: "GetGoto",
|
||||
OperationId: "getGoto",
|
||||
|
||||
Responses: &spec3.Responses{
|
||||
ResponsesProps: spec3.ResponsesProps{
|
||||
|
|
|
@ -15,6 +15,7 @@ import (
|
|||
"github.com/grafana/grafana-app-sdk/app"
|
||||
"github.com/grafana/grafana-app-sdk/k8s"
|
||||
"github.com/grafana/grafana-app-sdk/logging"
|
||||
"github.com/grafana/grafana-app-sdk/operator"
|
||||
"github.com/grafana/grafana-app-sdk/resource"
|
||||
"github.com/grafana/grafana-app-sdk/simple"
|
||||
shorturlv1alpha1 "github.com/grafana/grafana/apps/shorturl/pkg/apis/shorturl/v1alpha1"
|
||||
|
@ -39,10 +40,12 @@ func New(cfg app.Config) (app.App, error) {
|
|||
Name: "shorturl",
|
||||
KubeConfig: cfg.KubeConfig,
|
||||
InformerConfig: simple.AppInformerConfig{
|
||||
InformerOptions: operator.InformerOptions{
|
||||
ErrorHandler: func(ctx context.Context, err error) {
|
||||
klog.ErrorS(err, "Informer processing error")
|
||||
},
|
||||
},
|
||||
},
|
||||
ManagedKinds: []simple.AppManagedKind{
|
||||
{
|
||||
Kind: shorturlv1alpha1.ShortURLKind(),
|
||||
|
|
|
@ -14,7 +14,30 @@ Once your feature toggle is defined, you can then wrap your feature around a che
|
|||
Examples:
|
||||
|
||||
- [Backend](https://github.com/grafana/grafana/blob/feb2b5878b3e3ec551d64872c35edec2a0187812/pkg/services/authn/clients/session.go#L57): Use the `IsEnabled` function and pass in your feature toggle.
|
||||
- [Frontend](https://github.com/grafana/grafana/blob/feb2b5878b3e3ec551d64872c35edec2a0187812/public/app/features/search/service/folders.ts#L14): Check the config for your feature toggle.
|
||||
|
||||
### Frontend
|
||||
|
||||
Use the new OpenFeature-based feature flag client for all new feature flags. There are some differences compared to the legacy `config.featureToggles` system:
|
||||
|
||||
- Feature flag initialisation is async, but will be finished by the time the UI is rendered. This means you cannot get the value of a feature flag at the 'top level' of a module/file
|
||||
- Call `evaluateBooleanFlag("flagName")` from `@grafana/runtime/internal` instead to get the value of a feature flag
|
||||
- Feature flag values _may_ change over the lifetime of the session. Do not store the value in a variable that is used for longer than a single render - always call `evaluateBooleanFlag` lazily when you use the value.
|
||||
|
||||
e.g.
|
||||
|
||||
```ts
|
||||
import { evaluateBooleanFlag } from '@grafana/runtime/internal';
|
||||
|
||||
// BAD - Don't do this. The feature toggle will not evaluate correctly
|
||||
const isEnabled = evaluateBooleanFlag('newPreferences', false);
|
||||
|
||||
function makeAPICall() {
|
||||
// GOOD - The feature toggle should be called after app initialisation
|
||||
if (evaluateBooleanFlag('newPreferences', false)) {
|
||||
// do new things
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Enabling toggles in development
|
||||
|
||||
|
|
|
@ -174,7 +174,7 @@ resource "grafana_role" "my_new_role" {
|
|||
description = "My test role"
|
||||
version = 1
|
||||
uid = "newroleuid"
|
||||
global = true
|
||||
global = false
|
||||
|
||||
permissions {
|
||||
action = "org.users:add"
|
||||
|
|
|
@ -220,6 +220,24 @@ To preview label values, select `Use notification policy`, and then click on `Pr
|
|||
|
||||
{{< figure src="/media/docs/alerting/alert-instance-routing-preview.png" max-width="1200px" alt="Routing preview displays label values" >}}
|
||||
|
||||
## Grafana Cloud AI-generated templates
|
||||
|
||||
Grafana Cloud users can use built-in AI tool to generate templates in the appropriate [alerting template language](/docs/grafana-cloud/alerting-and-irm/alerting/configure-notifications/template-notifications/language/) for you.
|
||||
|
||||
To use AI to create your template, follow these steps:
|
||||
|
||||
1. Go to **Alerting -> Contact points**.
|
||||
|
||||
1. Click the Notification Templates tab then, click the **+ Add notification template group** button.
|
||||
|
||||
1. Name your template.
|
||||
|
||||
1. In the Template group section, click the **Generate with AI** button.
|
||||
|
||||
1. Supply the AI tool with a prompt or select from one of the example prompts and edit that if necessary.
|
||||
|
||||
1. Click **Save**.
|
||||
|
||||
## More information
|
||||
|
||||
For further details on how to template alert rules, refer to:
|
||||
|
|
|
@ -12,9 +12,14 @@ labels:
|
|||
- enterprise
|
||||
- oss
|
||||
menuTitle: Email
|
||||
title: Configure email for Alerting
|
||||
title: Configure email for alert notifications
|
||||
weight: 110
|
||||
refs:
|
||||
configure-contact-points:
|
||||
- pattern: /docs/grafana/
|
||||
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/configure-notifications/manage-contact-points/
|
||||
- pattern: /docs/grafana-cloud/
|
||||
destination: /docs/grafana-cloud/alerting-and-irm/alerting/configure-notifications/manage-contact-points/
|
||||
notification-templates:
|
||||
- pattern: /docs/grafana/
|
||||
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/configure-notifications/template-notifications/
|
||||
|
@ -22,83 +27,70 @@ refs:
|
|||
destination: /docs/grafana-cloud/alerting-and-irm/alerting/configure-notifications/template-notifications/
|
||||
---
|
||||
|
||||
# Configure email for Alerting
|
||||
# Configure email for alert notifications
|
||||
|
||||
Use the Grafana Alerting - email integration to send email notifications when your alerts are firing. An email is sent when an alert fires and when an alert gets resolved.
|
||||
Use the email integration to send alert notifications to one or more addresses.
|
||||
|
||||
Note that you can customize the `subject` and `message` of the email using [notification templates](ref:notification-templates). However, you cannot add HTML and CSS to email notifications for visual changes.
|
||||
You can customize the [subject and main section of the email body](#optional-settings-using-templates). By default, the subject and body are generated from the alert data included in the notification.
|
||||
|
||||
## Before you begin
|
||||
|
||||
In Grafana OSS, you must configure SMTP settings before you can enable email notifications.
|
||||
|
||||
{{<admonition type="note">}}
|
||||
This section is for Grafana OSS only. For Grafana Cloud, SMTP configuration is not required.
|
||||
In Grafana Cloud, SMTP configuration is not required.
|
||||
{{</admonition>}}
|
||||
|
||||
For Grafana OSS, you enable email notifications by first configuring [SMTP settings](https://grafana.com/docs/grafana/next/setup-grafana/configure-grafana/#smtp) in the Grafana configuration settings.
|
||||
1. Open the [configuration file](/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/). The file is typically named `grafana.ini` or `custom.ini` and located in the `conf` directory of your Grafana installation.
|
||||
|
||||
### SMTP configuration
|
||||
1. Configure the [SMTP settings](/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/#smtp) for your email server in the `[smtp]` section.
|
||||
|
||||
1. Access the configuration file.
|
||||
1. Save your changes and restart Grafana.
|
||||
|
||||
Locate the Grafana configuration file. This file is typically named `grafana.ini` or `custom.ini` and is located in the `conf` directory within the Grafana installation directory.
|
||||
1. Test email notifications by creating a contact point.
|
||||
|
||||
1. Open the configuration file:
|
||||
## Configure Email for a contact point
|
||||
|
||||
Open the configuration file using a text editor.
|
||||
|
||||
1. Locate SMTP settings section.
|
||||
|
||||
Search for the [SMTP settings section](https://grafana.com/docs/grafana/next/setup-grafana/configure-grafana/#smtp) in the configuration file. It starts with `[smtp]`.
|
||||
|
||||
1. Configure SMTP settings.
|
||||
|
||||
Within the `[smtp]` settings section, specify the following parameters:
|
||||
- `enabled = true`: Enables SMTP.
|
||||
- `host`: The hostname or IP address of your SMTP server, and the port number of your SMTP server (commonly 25, 465, or 587). Default is `localhost:25`.
|
||||
- `user`: Your SMTP username (if authentication is required).
|
||||
- `password`: Your SMTP password (if authentication is required).
|
||||
- `from_address`: The email address from which Grafana notifications will be sent.
|
||||
- `from_name`: The name associated with the from_address.
|
||||
- `skip_verify = true`: Skip SSL/TLS certificate verification (useful for testing, but not recommended for production).
|
||||
|
||||
1. Save and close the configuration file.
|
||||
|
||||
After configuring the SMTP settings, save the changes to the configuration file and close the text editor.
|
||||
|
||||
1. Restart Grafana.
|
||||
|
||||
Restart the Grafana service to apply the changes made to the configuration file. The method for restarting Grafana depends on your operating system and how Grafana was installed (e.g., `systemctl restart grafana-server` for systems using systemd).
|
||||
|
||||
1. Test email notifications.
|
||||
|
||||
After restarting Grafana, test the email notification functionality by creating an email contact point.
|
||||
|
||||
## Procedure
|
||||
|
||||
To set up email integration, complete the following steps.
|
||||
To create a contact point with a email integration, complete the following steps.
|
||||
|
||||
1. Navigate to **Alerts & IRM** -> **Alerting** -> **Contact points**.
|
||||
1. Click **+ Add contact point**.
|
||||
1. Enter a contact point name.
|
||||
1. From the Integration list, select **Email**.
|
||||
1. Enter the email addresses you want to send notifications to.
|
||||
|
||||
E-mail addresses are case sensitive. Ensure that the e-mail address entered is correct.
|
||||
For large numbers of emails, the **Single email** checkbox in **Optional Email Settings** allows you to send a single email to all contact points rather than send individual emails to all addresses in the contact point.
|
||||
|
||||
1. For Grafana Alertmanager: click **Test** to check that your integration works.
|
||||
|
||||
1. Click **+ Create contact point**.
|
||||
1. Enter a name for the contact point.
|
||||
1. From the **Integration** list, select **Email**.
|
||||
1. Set up the required [settings](#email-settings) for your Email configuration.
|
||||
1. Click **Save contact point**.
|
||||
|
||||
## Next steps
|
||||
For more details on contact points, including how to test them and enable notifications, refer to [Configure contact points](ref:configure-contact-points).
|
||||
|
||||
The email contact point is ready to receive alert notifications.
|
||||
## Email settings
|
||||
|
||||
To add this contact point to your alert, complete the following steps.
|
||||
| Option | Description |
|
||||
| --------- | ------------------------------------------------------------------------------------------ |
|
||||
| Addresses | The list of email addresses to send the notifications. Email addresses are case sensitive. |
|
||||
|
||||
1. In Grafana, navigate to **Alerting** > **Alert rules**.
|
||||
1. Edit or create a new alert rule.
|
||||
1. Scroll down to the **Configure labels and notifications** section.
|
||||
1. Under Notifications click **Select contact point**.
|
||||
1. From the drop-down menu, select the previously created contact point.
|
||||
1. **Click Save rule and exit**.
|
||||
#### Optional settings
|
||||
|
||||
| Option | Description |
|
||||
| ------------ | ------------------------------------------------------------------------- |
|
||||
| Single email | Send a single email to all email addresses rather than individual emails. |
|
||||
|
||||
#### Optional settings using templates
|
||||
|
||||
{{<admonition type="note">}}
|
||||
You can customize the email subject and main section of the email body, but you can't edit HTML or CSS for visual changes.
|
||||
|
||||
In Grafana OSS and Enterprise, you can edit the full email template. However, this is not officially supported because it's an internal API that may change without prior notice.
|
||||
{{</admonition>}}
|
||||
|
||||
| Option | Description |
|
||||
| ------- | --------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Subject | Sets the email subject, replacing the default template. Supports [notification templates](ref:notification-templates). |
|
||||
| Message | Sets the main section of the email body, replacing the default template. Supports [notification templates](ref:notification-templates). |
|
||||
|
||||
{{< figure src="/media/docs/alerting/custom-email-message5.png" caption="Email notification with custom message." max-width="750px" >}}
|
||||
|
||||
#### Optional notification settings
|
||||
|
||||
| Option | Description |
|
||||
| ------------------------ | ------------------------------------------------------------------- |
|
||||
| Disable resolved message | Enable this option to prevent notifications when an alert resolves. |
|
||||
|
|
|
@ -156,7 +156,7 @@ On this screen, you can see:
|
|||
|
||||
- The earliest time a user has been active in a dashboard
|
||||
- When they last accessed a shared dashboard
|
||||
- The dashboards to they have access
|
||||
- The dashboards they have access to
|
||||
- Their role
|
||||
|
||||
You can also revoke a user's access to all shared dashboards on from this tab.
|
||||
|
|
|
@ -0,0 +1,347 @@
|
|||
---
|
||||
aliases:
|
||||
- ../data-sources/prometheus/
|
||||
- ../features/datasources/prometheus/
|
||||
description: Guide for authenticating with Amazon Managed Service for Prometheus in Grafana
|
||||
keywords:
|
||||
- grafana
|
||||
- prometheus
|
||||
- guide
|
||||
labels:
|
||||
products:
|
||||
- cloud
|
||||
- enterprise
|
||||
- oss
|
||||
menuTitle: Authenticating with SigV4
|
||||
title: Configure the Prometheus data source
|
||||
weight: 200
|
||||
refs:
|
||||
intro-to-prometheus:
|
||||
- pattern: /docs/grafana/
|
||||
destination: /docs/grafana/<GRAFANA_VERSION>/fundamentals/intro-to-prometheus/
|
||||
- pattern: /docs/grafana-cloud/
|
||||
destination: /docs/grafana/<GRAFANA_VERSION>/fundamentals/intro-to-prometheus/
|
||||
exemplars:
|
||||
- pattern: /docs/grafana/
|
||||
destination: /docs/grafana/<GRAFANA_VERSION>/fundamentals/exemplars/
|
||||
- pattern: /docs/grafana-cloud/
|
||||
destination: /docs/grafana/<GRAFANA_VERSION>/fundamentals/exemplars/
|
||||
configure-data-links-value-variables:
|
||||
- pattern: /docs/grafana/
|
||||
destination: /docs/grafana/<GRAFANA_VERSION>/panels-visualizations/configure-data-links/#value-variables
|
||||
- pattern: /docs/grafana-cloud/
|
||||
destination: /docs/grafana/<GRAFANA_VERSION>/panels-visualizations/configure-data-links/#value-variables
|
||||
alerting-alert-rules:
|
||||
- pattern: /docs/grafana/
|
||||
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/fundamentals/alert-rules/
|
||||
- pattern: /docs/grafana-cloud/
|
||||
destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rules/
|
||||
add-a-data-source:
|
||||
- pattern: /docs/grafana/
|
||||
destination: /docs/grafana/<GRAFANA_VERSION>/datasources/#add-a-data-source
|
||||
- pattern: /docs/grafana-cloud/
|
||||
destination: /docs/grafana/<GRAFANA_VERSION>/datasources/#add-a-data-source
|
||||
prom-query-editor:
|
||||
- pattern: /docs/grafana/
|
||||
destination: /docs/grafana/<GRAFANA_VERSION>/datasources/prometheus/query-editor
|
||||
- pattern: /docs/grafana-cloud/
|
||||
destination: /docs/grafana/<GRAFANA_VERSION>/datasources/prometheus/query-editor
|
||||
default-manage-alerts-ui-toggle:
|
||||
- pattern: /docs/grafana/
|
||||
destination: /docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/#default_manage_alerts_ui_toggle
|
||||
- pattern: /docs/grafana-cloud/
|
||||
destination: /docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/#default_manage_alerts_ui_toggle
|
||||
provision-grafana:
|
||||
- pattern: /docs/grafana/
|
||||
destination: /docs/grafana/<GRAFANA_VERSION>/administration/provisioning/
|
||||
- pattern: /docs/grafana-cloud/
|
||||
destination: /docs/grafana/<GRAFANA_VERSION>/administration/provisioning/
|
||||
manage-alerts-toggle:
|
||||
- pattern: /docs/grafana/
|
||||
destination: /docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/#default_manage_alerts_ui_toggle
|
||||
- pattern: /docs/grafana-cloud/
|
||||
destination: /docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/#default_manage_alerts_ui_toggle
|
||||
manage-recording-rules-toggle:
|
||||
- pattern: /docs/grafana/
|
||||
destination: /docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/#default_allow_recording_rules_target_alerts_ui_toggle
|
||||
- pattern: /docs/grafana-cloud/
|
||||
destination: /docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/#default_allow_recording_rules_target_alerts_ui_toggle
|
||||
private-data-source-connect:
|
||||
- pattern: /docs/grafana/
|
||||
destination: docs/grafana-cloud/connect-externally-hosted/private-data-source-connect/
|
||||
- pattern: /docs/grafana-cloud/
|
||||
destination: docs/grafana-cloud/connect-externally-hosted/private-data-source-connect/
|
||||
configure-pdc:
|
||||
- pattern: /docs/grafana/
|
||||
destination: /docs/grafana-cloud/connect-externally-hosted/private-data-source-connect/configure-pdc/#configure-grafana-private-data-source-connect-pdc
|
||||
- pattern: /docs/grafana-cloud/
|
||||
destination: /docs/grafana-cloud/connect-externally-hosted/private-data-source-connect/configure-pdc/#configure-grafana-private-data-source-connect-pdc
|
||||
azure-active-directory:
|
||||
- pattern: /docs/grafana/
|
||||
destination: /docs/grafana/<GRAFANA_VERSION>/datasources/azure-monitor/#configure-azure-active-directory-ad-authentication
|
||||
- pattern: /docs/grafana-cloud/
|
||||
destination: /docs/grafana/<GRAFANA_VERSION>/datasources/azure-monitor/#configure-azure-active-directory-ad-authentication
|
||||
configure-grafana-configuration-file-location:
|
||||
- pattern: /docs/grafana/
|
||||
destination: /docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/#configuration-file-location
|
||||
- pattern: /docs/grafana-cloud/
|
||||
destination: /docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/#configuration-file-location
|
||||
grafana-managed-recording-rules:
|
||||
- pattern: /docs/grafana/
|
||||
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/alerting-rules/create-recording-rules/create-grafana-managed-recording-rules/
|
||||
- pattern: /docs/grafana-cloud/
|
||||
destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/create-recording-rules/create-grafana-managed-recording-rules/
|
||||
---
|
||||
|
||||
# Connect to Amazon Managed Service for Prometheus
|
||||
|
||||
1. In the data source configuration page, locate the **Auth** section
|
||||
2. Enable **SigV4 auth**
|
||||
3. Configure the following settings:
|
||||
|
||||
| Setting | Description | Example |
|
||||
| --------------------------- | ---------------------------------------------- | --------------------------------------------------------------- |
|
||||
| **Authentication Provider** | Choose your auth method | `AWS SDK Default`, `Access & secret key`, or `Credentials file` |
|
||||
| **Default Region** | AWS region for your workspace | `us-west-2` |
|
||||
| **Access Key ID** | Your AWS access key (if using access key auth) | `AKIA...` |
|
||||
| **Secret Access Key** | Your AWS secret key (if using access key auth) | `wJalrXUtn...` |
|
||||
| **Assume Role ARN** | IAM role ARN (optional) | `arn:aws:iam::123456789:role/GrafanaRole` |
|
||||
|
||||
4. Set the **HTTP URL** to your Amazon Managed Service for Prometheus workspace endpoint: `https://aps-workspaces.us-west-2.amazonaws.com/workspaces/ws-12345678-1234-1234-1234-123456789012/`
|
||||
|
||||
5. Click **Save & test** to verify the connection
|
||||
|
||||
## Example configuration
|
||||
|
||||
```yaml
|
||||
# Example provisioning configuration
|
||||
apiVersion: 1
|
||||
datasources:
|
||||
- name: 'Amazon Managed Prometheus'
|
||||
type: 'grafana-amazonprometheus-datasource'
|
||||
url: 'https://aps-workspaces.us-west-2.amazonaws.com/workspaces/ws-12345678-1234-1234-1234-123456789012/'
|
||||
jsonData:
|
||||
httpMethod: 'POST'
|
||||
sigV4Auth: true
|
||||
sigV4AuthType: 'keys'
|
||||
sigV4Region: 'us-east-2'
|
||||
secureJsonData:
|
||||
sigV4AccessKey: '<access key>'
|
||||
sigV4SecretKey: '<secret key>'
|
||||
```
|
||||
|
||||
## Migrate to Amazon Managed Service for Prometheus
|
||||
|
||||
Learn more about why this is happening: [Prometheus data source update: Redefining our big tent philosophy](https://grafana.com/blog/2025/06/16/prometheus-data-source-update-redefining-our-big-tent-philosophy/)
|
||||
|
||||
Before you begin, ensure you have the organization administrator role. If you are self-hosting Grafana, back up your existing dashboard configurations and queries.
|
||||
|
||||
Grafana Cloud users will be automatically migrated to the relevant version of Prometheus, so no action needs to be taken.
|
||||
|
||||
For air-gapped environments, download and install [Amazon Managed Service for Prometheus](https://grafana.com/grafana/plugins/grafana-amazonprometheus-datasource/), then follow the standard migration process.
|
||||
|
||||
### Migrate
|
||||
|
||||
1. Enable the `prometheusTypeMigration` feature toggle. For more information on feature toggles, refer to [Manage feature toggles](/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/feature-toggles/#manage-feature-toggles).
|
||||
2. Restart Grafana for the changes to take effect.
|
||||
|
||||
{{< admonition type="note" >}}
|
||||
This feature toggle will be removed in Grafana 13, and the migration will be automatic.
|
||||
{{< /admonition >}}
|
||||
|
||||
### Check migration status
|
||||
|
||||
To determine if your Prometheus data sources have been migrated:
|
||||
|
||||
1. Navigate to **Connections** > **Data sources**
|
||||
2. Select your Prometheus data source
|
||||
3. Look for a migration banner at the top of the configuration page
|
||||
|
||||
The banner displays one of the following messages:
|
||||
|
||||
- **"Migration Notice"** - The data source has already been migrated
|
||||
- **"Deprecation Notice"** - The data source has not been migrated
|
||||
- **No banner** - No migration is needed for this data source
|
||||
|
||||
## Common migration issues
|
||||
|
||||
The following sections contain troubleshooting guidance.
|
||||
|
||||
**Migration banner not appearing**
|
||||
|
||||
- Verify the `prometheusTypeMigration` feature toggle is enabled
|
||||
- Restart Grafana after enabling the feature toggle
|
||||
|
||||
**Amazon Managed Service for Prometheus is not installed**
|
||||
|
||||
- Verify that Amazon Managed Service for Prometheus is installed by going to **Connections** > **Add new connection** and search for "Amazon Managed Service for Prometheus"
|
||||
- Install Amazon Managed Service for Prometheus if not already installed
|
||||
|
||||
**After migrating, my data source returns "401 Unauthorized"**
|
||||
|
||||
- If you are using self-hosted Grafana, check your .ini for `grafana-amazonprometheus-datasource` is included in `forward_settings_to_plugins` under the `[aws]` heading.
|
||||
- If you are using Grafana Cloud, contact Grafana support.
|
||||
|
||||
### Rollback self-hosted Grafana without a backup
|
||||
|
||||
If you don’t have a backup of your Grafana instance before the migration, remove the `prometheusTypeMigration` feature toggle, and run the following script. It reverts all Amazon Managed Service for Prometheus data sources back to core Prometheus.
|
||||
|
||||
To revert the migration:
|
||||
|
||||
1. Disable the `prometheusTypeMigration` feature toggle. For more information on feature toggles, refer to [Manage feature toggles](/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/feature-toggles/#manage-feature-toggles).
|
||||
2. Obtain a bearer token that has `read` and `write` permissions for your Grafana data source API. For more information on the data source API, refer to [Data source API](/docs/grafana/<GRAFANA_VERSION>/developers/http_api/data_source/).
|
||||
3. Run the script below. Make sure to provide your Grafana URL and bearer token.
|
||||
4. (Optional) Report the issue you were experiencing on the [Grafana repository](https://github.com/grafana/grafana/issues). Tag the issue with "datasource/migrate-prometheus-type"
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
|
||||
# Configuration
|
||||
GRAFANA_URL=""
|
||||
BEARER_TOKEN=""
|
||||
LOG_FILE="grafana_migration_$(date +%Y%m%d_%H%M%S).log"
|
||||
|
||||
# Function to log messages to both console and file
|
||||
log_message() {
|
||||
local message="$1"
|
||||
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
||||
echo "[$timestamp] $message" | tee -a "$LOG_FILE"
|
||||
}
|
||||
|
||||
# Function to update a data source
|
||||
update_data_source() {
|
||||
local uid="$1"
|
||||
local data="$2"
|
||||
|
||||
response=$(curl -s -w "\n%{http_code}" -X PUT \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer $BEARER_TOKEN" \
|
||||
-d "$data" \
|
||||
"$GRAFANA_URL/api/datasources/uid/$uid")
|
||||
|
||||
http_code=$(echo "$response" | tail -n1)
|
||||
response_body=$(echo "$response" | sed '$d')
|
||||
|
||||
if [[ "$http_code" -ge 200 && "$http_code" -lt 300 ]]; then
|
||||
log_message "$uid successful"
|
||||
else
|
||||
log_message "$uid error: HTTP $http_code - $response_body"
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to process and update data source types
|
||||
update_data_source_type() {
|
||||
local result="$1"
|
||||
local processed_count=0
|
||||
local updated_count=0
|
||||
local readonly_count=0
|
||||
local skipped_count=0
|
||||
|
||||
# Use jq to parse and process JSON
|
||||
echo "$result" | jq -c '.[]' | while read -r data; do
|
||||
uid=$(echo "$data" | jq -r '.uid')
|
||||
prometheus_type_migration=$(echo "$data" | jq -r '.jsonData["prometheus-type-migration"] // false')
|
||||
data_type=$(echo "$data" | jq -r '.type')
|
||||
read_only=$(echo "$data" | jq -r '.readOnly // false')
|
||||
|
||||
processed_count=$((processed_count + 1))
|
||||
|
||||
# Check conditions
|
||||
if [[ "$prometheus_type_migration" != "true" ]] || [[ "$data_type" != "grafana-amazonprometheus-datasource" ]]; then
|
||||
skipped_count=$((skipped_count + 1))
|
||||
continue
|
||||
fi
|
||||
|
||||
if [[ "$read_only" == "true" ]]; then
|
||||
readonly_count=$((readonly_count + 1))
|
||||
log_message "$uid is readOnly. If this data source is provisioned, edit the data source type to be \`prometheus\` in the provisioning file."
|
||||
continue
|
||||
fi
|
||||
|
||||
# Update the data
|
||||
updated_data=$(echo "$data" | jq '.type = "prometheus" | .jsonData["prometheus-type-migration"] = false')
|
||||
update_data_source "$uid" "$updated_data"
|
||||
updated_count=$((updated_count + 1))
|
||||
|
||||
# Log the raw data for debugging (optional - uncomment if needed)
|
||||
# log_message "DEBUG - Updated data for $uid: $updated_data"
|
||||
done
|
||||
|
||||
# Note: These counts won't work in the while loop due to subshell
|
||||
# Moving summary to the main function instead
|
||||
}
|
||||
|
||||
# Function to get summary statistics
|
||||
get_summary_stats() {
|
||||
local result="$1"
|
||||
local total_datasources=$(echo "$result" | jq '. | length')
|
||||
local migration_candidates=$(echo "$result" | jq '[.[] | select(.jsonData["prometheus-type-migration"] == true and .type == "grafana-amazonprometheus-datasource")] | length')
|
||||
local readonly_candidates=$(echo "$result" | jq '[.[] | select(.jsonData["prometheus-type-migration"] == true and .type == "grafana-amazonprometheus-datasource" and .readOnly == true)] | length')
|
||||
local updateable_candidates=$(echo "$result" | jq '[.[] | select(.jsonData["prometheus-type-migration"] == true and .type == "grafana-amazonprometheus-datasource" and (.readOnly == false or .readOnly == null))] | length')
|
||||
|
||||
log_message "=== MIGRATION SUMMARY ==="
|
||||
log_message "Total data sources found: $total_datasources"
|
||||
log_message "Migration candidates found: $migration_candidates"
|
||||
log_message "Read-only candidates (will be skipped): $readonly_candidates"
|
||||
log_message "Updateable candidates: $updateable_candidates"
|
||||
log_message "=========================="
|
||||
}
|
||||
|
||||
# Main function to remove Prometheus type migration
|
||||
remove_prometheus_type_migration() {
|
||||
log_message "Starting remove Azure Prometheus migration"
|
||||
log_message "Log file: $LOG_FILE"
|
||||
log_message "Grafana URL: $GRAFANA_URL"
|
||||
|
||||
response=$(curl -s -w "\n%{http_code}" -X GET \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer $BEARER_TOKEN" \
|
||||
"$GRAFANA_URL/api/datasources/")
|
||||
|
||||
http_code=$(echo "$response" | tail -n1)
|
||||
response_body=$(echo "$response" | sed '$d')
|
||||
|
||||
if [[ "$http_code" -ge 200 && "$http_code" -lt 300 ]]; then
|
||||
log_message "Successfully fetched data sources"
|
||||
get_summary_stats "$response_body"
|
||||
update_data_source_type "$response_body"
|
||||
log_message "Migration process completed"
|
||||
else
|
||||
log_message "error fetching data sources: HTTP $http_code - $response_body"
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to initialize log file
|
||||
initialize_log() {
|
||||
echo "=== Grafana Azure Prometheus Migration Log ===" > "$LOG_FILE"
|
||||
echo "Started at: $(date)" >> "$LOG_FILE"
|
||||
echo "=============================================" >> "$LOG_FILE"
|
||||
echo "" >> "$LOG_FILE"
|
||||
}
|
||||
|
||||
# Check if jq is installed
|
||||
if ! command -v jq &> /dev/null; then
|
||||
echo "Error: jq is required but not installed. Please install jq to run this script."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if required variables are set
|
||||
if [[ -z "$GRAFANA_URL" || -z "$BEARER_TOKEN" ]]; then
|
||||
echo "Error: Please set GRAFANA_URL and BEARER_TOKEN variables at the top of the script."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Initialize log file
|
||||
initialize_log
|
||||
|
||||
# Execute main function
|
||||
log_message "Script started"
|
||||
remove_prometheus_type_migration
|
||||
log_message "Script completed"
|
||||
|
||||
# Final log message
|
||||
echo ""
|
||||
echo "Migration completed. Full log available at: $LOG_FILE"
|
||||
```
|
||||
|
||||
If you continue to experience issues, check the Grafana server logs for detailed error messages and contact [Grafana Support](https://grafana.com/help/) with your troubleshooting results.
|
|
@ -0,0 +1,359 @@
|
|||
---
|
||||
aliases:
|
||||
- ../data-sources/prometheus/
|
||||
- ../features/datasources/prometheus/
|
||||
description: Guide for authenticating with Azure Monitor Managed Service for Prometheus in Grafana
|
||||
keywords:
|
||||
- grafana
|
||||
- prometheus
|
||||
- guide
|
||||
labels:
|
||||
products:
|
||||
- cloud
|
||||
- enterprise
|
||||
- oss
|
||||
menuTitle: Authenticating with Azure
|
||||
title: Configure the Prometheus data source
|
||||
weight: 200
|
||||
refs:
|
||||
intro-to-prometheus:
|
||||
- pattern: /docs/grafana/
|
||||
destination: /docs/grafana/<GRAFANA_VERSION>/fundamentals/intro-to-prometheus/
|
||||
- pattern: /docs/grafana-cloud/
|
||||
destination: /docs/grafana/<GRAFANA_VERSION>/fundamentals/intro-to-prometheus/
|
||||
exemplars:
|
||||
- pattern: /docs/grafana/
|
||||
destination: /docs/grafana/<GRAFANA_VERSION>/fundamentals/exemplars/
|
||||
- pattern: /docs/grafana-cloud/
|
||||
destination: /docs/grafana/<GRAFANA_VERSION>/fundamentals/exemplars/
|
||||
configure-data-links-value-variables:
|
||||
- pattern: /docs/grafana/
|
||||
destination: /docs/grafana/<GRAFANA_VERSION>/panels-visualizations/configure-data-links/#value-variables
|
||||
- pattern: /docs/grafana-cloud/
|
||||
destination: /docs/grafana/<GRAFANA_VERSION>/panels-visualizations/configure-data-links/#value-variables
|
||||
alerting-alert-rules:
|
||||
- pattern: /docs/grafana/
|
||||
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/fundamentals/alert-rules/
|
||||
- pattern: /docs/grafana-cloud/
|
||||
destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rules/
|
||||
add-a-data-source:
|
||||
- pattern: /docs/grafana/
|
||||
destination: /docs/grafana/<GRAFANA_VERSION>/datasources/#add-a-data-source
|
||||
- pattern: /docs/grafana-cloud/
|
||||
destination: /docs/grafana/<GRAFANA_VERSION>/datasources/#add-a-data-source
|
||||
prom-query-editor:
|
||||
- pattern: /docs/grafana/
|
||||
destination: /docs/grafana/<GRAFANA_VERSION>/datasources/prometheus/query-editor
|
||||
- pattern: /docs/grafana-cloud/
|
||||
destination: /docs/grafana/<GRAFANA_VERSION>/datasources/prometheus/query-editor
|
||||
default-manage-alerts-ui-toggle:
|
||||
- pattern: /docs/grafana/
|
||||
destination: /docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/#default_manage_alerts_ui_toggle
|
||||
- pattern: /docs/grafana-cloud/
|
||||
destination: /docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/#default_manage_alerts_ui_toggle
|
||||
provision-grafana:
|
||||
- pattern: /docs/grafana/
|
||||
destination: /docs/grafana/<GRAFANA_VERSION>/administration/provisioning/
|
||||
- pattern: /docs/grafana-cloud/
|
||||
destination: /docs/grafana/<GRAFANA_VERSION>/administration/provisioning/
|
||||
manage-alerts-toggle:
|
||||
- pattern: /docs/grafana/
|
||||
destination: /docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/#default_manage_alerts_ui_toggle
|
||||
- pattern: /docs/grafana-cloud/
|
||||
destination: /docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/#default_manage_alerts_ui_toggle
|
||||
manage-recording-rules-toggle:
|
||||
- pattern: /docs/grafana/
|
||||
destination: /docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/#default_allow_recording_rules_target_alerts_ui_toggle
|
||||
- pattern: /docs/grafana-cloud/
|
||||
destination: /docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/#default_allow_recording_rules_target_alerts_ui_toggle
|
||||
private-data-source-connect:
|
||||
- pattern: /docs/grafana/
|
||||
destination: docs/grafana-cloud/connect-externally-hosted/private-data-source-connect/
|
||||
- pattern: /docs/grafana-cloud/
|
||||
destination: docs/grafana-cloud/connect-externally-hosted/private-data-source-connect/
|
||||
configure-pdc:
|
||||
- pattern: /docs/grafana/
|
||||
destination: /docs/grafana-cloud/connect-externally-hosted/private-data-source-connect/configure-pdc/#configure-grafana-private-data-source-connect-pdc
|
||||
- pattern: /docs/grafana-cloud/
|
||||
destination: /docs/grafana-cloud/connect-externally-hosted/private-data-source-connect/configure-pdc/#configure-grafana-private-data-source-connect-pdc
|
||||
azure-active-directory:
|
||||
- pattern: /docs/grafana/
|
||||
destination: /docs/grafana/<GRAFANA_VERSION>/datasources/azure-monitor/#configure-azure-active-directory-ad-authentication
|
||||
- pattern: /docs/grafana-cloud/
|
||||
destination: /docs/grafana/<GRAFANA_VERSION>/datasources/azure-monitor/#configure-azure-active-directory-ad-authentication
|
||||
configure-grafana-configuration-file-location:
|
||||
- pattern: /docs/grafana/
|
||||
destination: /docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/#configuration-file-location
|
||||
- pattern: /docs/grafana-cloud/
|
||||
destination: /docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/#configuration-file-location
|
||||
grafana-managed-recording-rules:
|
||||
- pattern: /docs/grafana/
|
||||
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/alerting-rules/create-recording-rules/create-grafana-managed-recording-rules/
|
||||
- pattern: /docs/grafana-cloud/
|
||||
destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/create-recording-rules/create-grafana-managed-recording-rules/
|
||||
---
|
||||
|
||||
# Connect to Azure Monitor Managed Service for Prometheus
|
||||
|
||||
After creating a Azure Monitor Managed Service for Prometheus data source:
|
||||
|
||||
1. In the data source configuration page, locate the **Authentication** section
|
||||
2. Select your authentication method:
|
||||
- **Managed Identity**: For Azure-hosted Grafana instances. To learn more about Entra login for Grafana, refer to [Configure Azure AD/Entra ID OAuth authentication](/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-security/configure-authentication/azuread/#configure-azure-adentra-id-oauth-authentication)
|
||||
- **App Registration**: For service principal authentication
|
||||
- **Current User**: Uses the current user's Azure AD credentials
|
||||
|
||||
3. Configure based on your chosen method:
|
||||
|
||||
| Setting | Description | Example |
|
||||
| --------------------------- | ------------------------------- | -------------------------------------- |
|
||||
| **Directory (tenant) ID** | Your Azure AD tenant ID | `12345678-1234-1234-1234-123456789012` |
|
||||
| **Application (client) ID** | Your app registration client ID | `87654321-4321-4321-4321-210987654321` |
|
||||
| **Client secret** | Your app registration secret | `your-client-secret` |
|
||||
|
||||
When using Managed Identity for authentication:
|
||||
|
||||
- No additional configuration required if using system-assigned identity.
|
||||
- For user-assigned identity, provide the **Client ID**.
|
||||
|
||||
4. Set the **Prometheus server URL** to your Azure Monitor workspace endpoint:
|
||||
|
||||
```
|
||||
https://your-workspace.eastus2.prometheus.monitor.azure.com
|
||||
```
|
||||
|
||||
5. Click **Save & test** to verify the connection
|
||||
|
||||
## Example configuration
|
||||
|
||||
```yaml
|
||||
# Example provisioning configuration for App Registration
|
||||
apiVersion: 1
|
||||
datasources:
|
||||
- name: 'Azure Monitor Prometheus'
|
||||
type: 'grafana-azureprometheus-datasource'
|
||||
url: 'https://your-workspace.eastus2.prometheus.monitor.azure.com'
|
||||
jsonData:
|
||||
azureCredentials:
|
||||
authType: 'clientsecret'
|
||||
azureCloud: 'AzureCloud'
|
||||
clientId: '<client_id>'
|
||||
httpMethod: 'POST'
|
||||
tenantId: '<tenant_id>'
|
||||
secureJsonData:
|
||||
clientSecret: 'your-client-secret'
|
||||
```
|
||||
|
||||
## Migrate to Azure Monitor Managed Service for Prometheus
|
||||
|
||||
Learn more about why this is happening: [Prometheus data source update: Redefining our big tent philosophy](https://grafana.com/blog/2025/06/16/prometheus-data-source-update-redefining-our-big-tent-philosophy/)
|
||||
|
||||
Before you begin, ensure you have the organization administrator role. If you are self-hosting Grafana, back up your existing dashboard configurations and queries.
|
||||
|
||||
Grafana Cloud users will be automatically migrated to the relevant version of Prometheus, so no action needs to be taken.
|
||||
|
||||
For air-gapped environments, download and install [Azure Monitor Managed Service for Prometheus](https://grafana.com/grafana/plugins/grafana-azureprometheus-datasource/), then follow the standard migration process.
|
||||
|
||||
### Migrate
|
||||
|
||||
1. Enable the `prometheusTypeMigration` feature toggle. For more information on feature toggles, refer to [Manage feature toggles](/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/feature-toggles/#manage-feature-toggles).
|
||||
2. Restart Grafana for the changes to take effect.
|
||||
|
||||
{{< admonition type="note" >}}
|
||||
This feature toggle will be removed in Grafana 13, and the migration will be automatic.
|
||||
{{< /admonition >}}
|
||||
|
||||
To determine if your Prometheus data sources have been migrated:
|
||||
|
||||
1. Navigate to **Connections** > **Data sources**
|
||||
2. Select your Prometheus data source
|
||||
3. Look for a migration banner at the top of the configuration page
|
||||
|
||||
The banner displays one of the following messages:
|
||||
|
||||
- **"Migration Notice"** - The data source has already been migrated
|
||||
- **"Deprecation Notice"** - The data source has not been migrated
|
||||
- **No banner** - No migration is needed for this data source
|
||||
|
||||
## Common migration issues
|
||||
|
||||
The following sections contain troubleshooting guidance.
|
||||
|
||||
**Migration banner not appearing**
|
||||
|
||||
- Verify the `prometheusTypeMigration` feature toggle is enabled.
|
||||
- Restart Grafana after enabling the feature toggle
|
||||
|
||||
**Azure Monitor Managed Service for Prometheus is not installed**
|
||||
|
||||
- Verify that Azure Monitor Managed Service for Prometheus is installed by going to **Connections** > **Add new connection** and search for "Azure Monitor Managed Service for Prometheus"
|
||||
- Install Azure Monitor Managed Service for Prometheus if not already installed
|
||||
|
||||
**After migrating, my data source returns "401 Unauthorized"**
|
||||
|
||||
- If you are using self-hosted Grafana, check your .ini for `grafana-azureprometheus-datasource` is included in `forward_settings_to_plugins` under the `[azure]` heading.
|
||||
- If you are using Grafana Cloud, contact Grafana support.
|
||||
|
||||
### Rollback self-hosted Grafana without a backup
|
||||
|
||||
If you don’t have a backup of your Grafana instance before the migration, remove the `prometheusTypeMigration` feature toggle, and run the following script. It reverts all the Azure Monitor Managed Service data source instances back to core Prometheus.
|
||||
|
||||
To revert the migration:
|
||||
|
||||
1. Disable the `prometheusTypeMigration` feature toggle. For more information on feature toggles, refer to [Manage feature toggles](/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/feature-toggles/#manage-feature-toggles).
|
||||
2. Obtain a bearer token that has `read` and `write` permissions for your Grafana data source API. For more information on the data source API, refer to [Data source API](/docs/grafana/<GRAFANA_VERSION>/developers/http_api/data_source/).
|
||||
3. Run the script below. Make sure to provide your Grafana URL and bearer token.
|
||||
4. (Optional) Report the issue you were experiencing on the [Grafana repository](https://github.com/grafana/grafana/issues). Tag the issue with "datasource/migrate-prometheus-type"
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
|
||||
# Configuration
|
||||
GRAFANA_URL=""
|
||||
BEARER_TOKEN=""
|
||||
LOG_FILE="grafana_migration_$(date +%Y%m%d_%H%M%S).log"
|
||||
|
||||
# Function to log messages to both console and file
|
||||
log_message() {
|
||||
local message="$1"
|
||||
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
||||
echo "[$timestamp] $message" | tee -a "$LOG_FILE"
|
||||
}
|
||||
|
||||
# Function to update a data source
|
||||
update_data_source() {
|
||||
local uid="$1"
|
||||
local data="$2"
|
||||
|
||||
response=$(curl -s -w "\n%{http_code}" -X PUT \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer $BEARER_TOKEN" \
|
||||
-d "$data" \
|
||||
"$GRAFANA_URL/api/datasources/uid/$uid")
|
||||
|
||||
http_code=$(echo "$response" | tail -n1)
|
||||
response_body=$(echo "$response" | sed '$d')
|
||||
|
||||
if [[ "$http_code" -ge 200 && "$http_code" -lt 300 ]]; then
|
||||
log_message "$uid successful"
|
||||
else
|
||||
log_message "$uid error: HTTP $http_code - $response_body"
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to process and update data source types
|
||||
update_data_source_type() {
|
||||
local result="$1"
|
||||
local processed_count=0
|
||||
local updated_count=0
|
||||
local readonly_count=0
|
||||
local skipped_count=0
|
||||
|
||||
# Use jq to parse and process JSON
|
||||
echo "$result" | jq -c '.[]' | while read -r data; do
|
||||
uid=$(echo "$data" | jq -r '.uid')
|
||||
prometheus_type_migration=$(echo "$data" | jq -r '.jsonData["prometheus-type-migration"] // false')
|
||||
data_type=$(echo "$data" | jq -r '.type')
|
||||
read_only=$(echo "$data" | jq -r '.readOnly // false')
|
||||
|
||||
processed_count=$((processed_count + 1))
|
||||
|
||||
# Check conditions
|
||||
if [[ "$prometheus_type_migration" != "true" ]] || [[ "$data_type" != "grafana-azureprometheus-datasource" ]]; then
|
||||
skipped_count=$((skipped_count + 1))
|
||||
continue
|
||||
fi
|
||||
|
||||
if [[ "$read_only" == "true" ]]; then
|
||||
readonly_count=$((readonly_count + 1))
|
||||
log_message "$uid is readOnly. If this data source is provisioned, edit the data source type to be \`prometheus\` in the provisioning file."
|
||||
continue
|
||||
fi
|
||||
|
||||
# Update the data
|
||||
updated_data=$(echo "$data" | jq '.type = "prometheus" | .jsonData["prometheus-type-migration"] = false')
|
||||
update_data_source "$uid" "$updated_data"
|
||||
updated_count=$((updated_count + 1))
|
||||
|
||||
# Log the raw data for debugging (optional - uncomment if needed)
|
||||
# log_message "DEBUG - Updated data for $uid: $updated_data"
|
||||
done
|
||||
|
||||
# Note: These counts won't work in the while loop due to subshell
|
||||
# Moving summary to the main function instead
|
||||
}
|
||||
|
||||
# Function to get summary statistics
|
||||
get_summary_stats() {
|
||||
local result="$1"
|
||||
local total_datasources=$(echo "$result" | jq '. | length')
|
||||
local migration_candidates=$(echo "$result" | jq '[.[] | select(.jsonData["prometheus-type-migration"] == true and .type == "grafana-azureprometheus-datasource")] | length')
|
||||
local readonly_candidates=$(echo "$result" | jq '[.[] | select(.jsonData["prometheus-type-migration"] == true and .type == "grafana-azureprometheus-datasource" and .readOnly == true)] | length')
|
||||
local updateable_candidates=$(echo "$result" | jq '[.[] | select(.jsonData["prometheus-type-migration"] == true and .type == "grafana-azureprometheus-datasource" and (.readOnly == false or .readOnly == null))] | length')
|
||||
|
||||
log_message "=== MIGRATION SUMMARY ==="
|
||||
log_message "Total data sources found: $total_datasources"
|
||||
log_message "Migration candidates found: $migration_candidates"
|
||||
log_message "Read-only candidates (will be skipped): $readonly_candidates"
|
||||
log_message "Updateable candidates: $updateable_candidates"
|
||||
log_message "=========================="
|
||||
}
|
||||
|
||||
# Main function to remove Prometheus type migration
|
||||
remove_prometheus_type_migration() {
|
||||
log_message "Starting remove Azure Prometheus migration"
|
||||
log_message "Log file: $LOG_FILE"
|
||||
log_message "Grafana URL: $GRAFANA_URL"
|
||||
|
||||
response=$(curl -s -w "\n%{http_code}" -X GET \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer $BEARER_TOKEN" \
|
||||
"$GRAFANA_URL/api/datasources/")
|
||||
|
||||
http_code=$(echo "$response" | tail -n1)
|
||||
response_body=$(echo "$response" | sed '$d')
|
||||
|
||||
if [[ "$http_code" -ge 200 && "$http_code" -lt 300 ]]; then
|
||||
log_message "Successfully fetched data sources"
|
||||
get_summary_stats "$response_body"
|
||||
update_data_source_type "$response_body"
|
||||
log_message "Migration process completed"
|
||||
else
|
||||
log_message "error fetching data sources: HTTP $http_code - $response_body"
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to initialize log file
|
||||
initialize_log() {
|
||||
echo "=== Grafana Azure Prometheus Migration Log ===" > "$LOG_FILE"
|
||||
echo "Started at: $(date)" >> "$LOG_FILE"
|
||||
echo "=============================================" >> "$LOG_FILE"
|
||||
echo "" >> "$LOG_FILE"
|
||||
}
|
||||
|
||||
# Check if jq is installed
|
||||
if ! command -v jq &> /dev/null; then
|
||||
echo "Error: jq is required but not installed. Please install jq to run this script."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if required variables are set
|
||||
if [[ -z "$GRAFANA_URL" || -z "$BEARER_TOKEN" ]]; then
|
||||
echo "Error: Please set GRAFANA_URL and BEARER_TOKEN variables at the top of the script."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Initialize log file
|
||||
initialize_log
|
||||
|
||||
# Execute main function
|
||||
log_message "Script started"
|
||||
remove_prometheus_type_migration
|
||||
log_message "Script completed"
|
||||
|
||||
# Final log message
|
||||
echo ""
|
||||
echo "Migration completed. Full log available at: $LOG_FILE"
|
||||
```
|
||||
|
||||
If you continue to experience issues, check the Grafana server logs for detailed error messages and contact [Grafana Support](https://grafana.com/help/) with your troubleshooting results.
|
|
@ -688,7 +688,7 @@
|
|||
},
|
||||
"languages": {
|
||||
"type": "array",
|
||||
"description": "The list of languages supported by the plugin. Each entry should be a locale identifier in the format `language-COUNTRY` (for example `en-US`, `fr-FR`, `es-ES`).",
|
||||
"description": "The list of languages supported by the plugin. Each entry should be a locale identifier in the format `language-COUNTRY` (for example `en-US`, `es-ES`, `de-DE`).",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ After you localize the latency problem to a few exemplar traces, you can combine
|
|||
|
||||
Support for exemplars is available for the Prometheus data source only.
|
||||
After you enable the functionality, exemplar data is available by default.
|
||||
For more information on exemplar configuration and how to enable exemplars, refer to [configuring exemplars in the Prometheus data source](../../datasources/prometheus/configure-prometheus-data-source/#exemplars).
|
||||
For more information on exemplar configuration and how to enable exemplars, refer to the Exemplars section in [Prometheus configuration options](https://grafana.com/docs/grafana/latest/datasources/prometheus/configure/#configuration-options).
|
||||
|
||||
Grafana shows exemplars alongside a metric in the Explore view and in dashboards.
|
||||
Each exemplar displays as a highlighted star.
|
||||
|
|
|
@ -37,7 +37,7 @@ For an integrated, UI-driven Git workflow focused on dashboards, explore Git Syn
|
|||
- Connect folders or entire Grafana instances directly to a GitHub repository to synchronize dashboard definitions, enabling version control, branching, and pull requests directly from Grafana.
|
||||
- Git Sync offers a simple, out-of-the-box approach for managing dashboards as code.
|
||||
{{< admonition type="note" >}}
|
||||
Git Sync is an **experimental feature** in Grafana 12, available in Grafana OSS and Enterprise [nightly releases](https://grafana.com/grafana/download/nightly). It is not yet available in Grafana Cloud.
|
||||
Git Sync is available in **private preview** for Grafana Cloud, and it's an **experimental feature** in Grafana 12, available in Grafana OSS and Enterprise [nightly releases](https://grafana.com/grafana/download/nightly).
|
||||
{{< /admonition >}}
|
||||
|
||||
Refer to the [Git Sync documentation](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/observability-as-code/provision-resources/intro-git-sync/) to learn more.
|
||||
|
|
|
@ -18,16 +18,18 @@ weight: 300
|
|||
# Provision resources and sync dashboards
|
||||
|
||||
{{< admonition type="caution" >}}
|
||||
Provisioning is an [experimental feature](https://grafana.com/docs/release-life-cycle/) introduced in Grafana v12 for open source and Enterprise editions. Engineering and on-call support is not available. Documentation is either limited or not provided outside of code comments. No SLA is provided. This feature is not publicly available in Grafana Cloud yet. Only the cloud-hosted version of GitHub (GitHub.com) is supported at this time. GitHub Enterprise is not yet compatible.
|
||||
|
||||
Sign up for Grafana Cloud Git Sync early access using [this form](https://forms.gle/WKkR3EVMcbqsNnkD9).
|
||||
Git Sync is available in [private preview](https://grafana.com/docs/release-life-cycle/) for Grafana Cloud. Support and documentation is available but might be limited to enablement, configuration, and some troubleshooting. No SLAs are provided. You can sign up to the private preview using the [Git Sync early access form](https://forms.gle/WKkR3EVMcbqsNnkD9).
|
||||
|
||||
Git Sync and local file provisioning are [experimental features](https://grafana.com/docs/release-life-cycle/) introduced in Grafana v12 for open source and Enterprise editions. Engineering and on-call support is not available. Documentation is either limited or not provided outside of code comments. No SLA is provided.
|
||||
|
||||
{{< /admonition >}}
|
||||
|
||||
Provisioning is an experimental feature that allows you to configure how to store your dashboard JSONs and other files in GitHub repositories using either Git Sync or a local path.
|
||||
Provisioning allows you to configure how to store your dashboard JSON and other files in GitHub repositories using either Git Sync or a local path.
|
||||
|
||||
Of the two options, **Git Sync** is the favorited method for provisioning your dashboards. You can synchronize any new dashboards and changes to existing dashboards from the UI to your configured GitHub repository. If you push a change in the repository, those changes are mirrored in your Grafana instance. See [Git Sync workflow](#git-sync-workflow).
|
||||
Of the two options, **Git Sync** is the favorited method for provisioning your dashboards. You can synchronize any new dashboards and changes to existing dashboards from the UI to your configured GitHub repository. If you push a change in the repository, those changes are mirrored in your Grafana instance. Refer to [Git Sync workflow](#git-sync-workflow) for more information.
|
||||
|
||||
Alternatively, **local file provisioning** allows you to include in your Grafana instance resources (such as folders and dashboard JSON files) that are stored in a local file system. See [Local file workflow](local-file-workflow).
|
||||
Alternatively, **local file provisioning** allows you to include in your Grafana instance resources (such as folders and dashboard JSON files) that are stored in a local file system. Refer to [Local file workflow](#local-file-workflow) for more information.
|
||||
|
||||
## Provisioned folders and connections
|
||||
|
||||
|
@ -40,8 +42,7 @@ You can set a single folder, or multiple folders to a different repository, with
|
|||
In the Git Sync workflow:
|
||||
|
||||
- When you provision resources with Git Sync you can modify them from within the Grafana UI or within the GitHub repository. Changes made in either the repository or the Grafana UI are bidirectional.
|
||||
- Any changes made in the provisioned files stored in the GitHub repository are reflected in the Grafana database. By default, Grafana polls GitHub every 60 seconds.
|
||||
- The Grafana UI reads from the database and updates the UI to reflect these changes.
|
||||
- Any changes made in the provisioned files stored in the GitHub repository are reflected in the Grafana database. By default, Grafana polls GitHub every 60 seconds. The Grafana UI reads from the database and updates the UI to reflect these changes.
|
||||
|
||||
For example, if you update a dashboard within the Grafana UI and click **Save** to preserve the changes, you'll be notified that the dashboard is provisioned in a GitHub repository. Next you'll be prompted to choose how to preserve the changes: either directly to a branch, or pushed to a new branch using a pull request in GitHub.
|
||||
|
||||
|
@ -52,8 +53,7 @@ For more information, see [Introduction to Git Sync](https://grafana.com/docs/gr
|
|||
In the local file workflow:
|
||||
|
||||
- All provisioned resources are changed in the local files.
|
||||
- Any changes made in the provisioned files are reflected in the Grafana database.
|
||||
- The Grafana UI reads the database and updates the UI to reflect these changes.
|
||||
- Any changes made in the provisioned files are reflected in the Grafana database. The Grafana UI reads the database and updates the UI to reflect these changes.
|
||||
- You can't use the Grafana UI to edit or delete provisioned resources.
|
||||
|
||||
Learn more in [Set up file provisioning](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/observability-as-code/provision-resources/file-path-setup/).
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue