From 6e3ded8432b919ac220a30ad315d394f49dc7127 Mon Sep 17 00:00:00 2001 From: Matheus Macabu Date: Tue, 26 Aug 2025 13:30:15 +0200 Subject: [PATCH 01/24] Preferences: Add module to Go workspace (#110148) --- Dockerfile | 1 + apps/preferences/go.mod | 60 +++++++++++++ apps/preferences/go.sum | 183 ++++++++++++++++++++++++++++++++++++++++ go.work | 1 + 4 files changed, 245 insertions(+) diff --git a/Dockerfile b/Dockerfile index 618779f7305..4cdab806589 100644 --- a/Dockerfile +++ b/Dockerfile @@ -93,6 +93,7 @@ COPY pkg/aggregator pkg/aggregator COPY apps/playlist apps/playlist COPY apps/plugins apps/plugins COPY apps/shorturl apps/shorturl +COPY apps/preferences apps/preferences COPY apps/provisioning apps/provisioning COPY apps/secret apps/secret COPY apps/investigations apps/investigations diff --git a/apps/preferences/go.mod b/apps/preferences/go.mod index fa02d31a234..63300e95d39 100644 --- a/apps/preferences/go.mod +++ b/apps/preferences/go.mod @@ -1,3 +1,63 @@ module github.com/grafana/grafana/apps/preferences go 1.24.6 + +require ( + github.com/grafana/grafana-app-sdk v0.40.3 + k8s.io/apimachinery v0.33.3 +) + +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.1 // indirect + github.com/fxamacker/cbor/v2 v2.7.0 // indirect + github.com/getkin/kin-openapi v0.132.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/go-test/deep v1.1.1 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/google/gnostic-models v0.6.9 // indirect + github.com/grafana/grafana-app-sdk/logging v0.40.3 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/mailru/easyjson v0.9.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 // indirect + github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 // indirect + github.com/perimeterx/marshmallow v1.1.5 // indirect + github.com/prometheus/client_golang v1.23.0 // indirect + github.com/prometheus/client_model v0.6.2 // indirect + github.com/prometheus/common v0.65.0 // indirect + github.com/prometheus/procfs v0.16.1 // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect + github.com/x448/float16 v0.8.4 // indirect + go.opentelemetry.io/otel v1.37.0 // indirect + go.opentelemetry.io/otel/trace v1.37.0 // indirect + go.yaml.in/yaml/v2 v2.4.2 // indirect + golang.org/x/net v0.43.0 // indirect + golang.org/x/oauth2 v0.30.0 // indirect + golang.org/x/sys v0.35.0 // indirect + golang.org/x/term v0.34.0 // indirect + golang.org/x/text v0.28.0 // indirect + golang.org/x/time v0.11.0 // indirect + google.golang.org/protobuf v1.36.6 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + k8s.io/client-go v0.33.3 // indirect + k8s.io/klog/v2 v2.130.1 // indirect + k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect + k8s.io/utils v0.0.0-20241210054802-24370beab758 // 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/v4 v4.7.0 // indirect + sigs.k8s.io/yaml v1.5.0 // indirect +) diff --git a/apps/preferences/go.sum b/apps/preferences/go.sum index e69de29bb2d..9ea34217f40 100644 --- a/apps/preferences/go.sum +++ b/apps/preferences/go.sum @@ -0,0 +1,183 @@ +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emicklei/go-restful/v3 v3.12.1 h1:PJMDIM/ak7btuL8Ex0iYET9hxM3CI2sjZtzpL63nKAU= +github.com/emicklei/go-restful/v3 v3.12.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= +github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= +github.com/getkin/kin-openapi v0.132.0 h1:3ISeLMsQzcb5v26yeJrBcdTCEQTag36ZjaGk7MIRUwk= +github.com/getkin/kin-openapi v0.132.0/go.mod h1:3OlG51PCYNsPByuiMB0t4fjnNlIDnaEDsjiKUV8nL58= +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/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U= +github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw= +github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grafana/grafana-app-sdk v0.40.3 h1:JFo7uAfbAJUfZ9neD7/4sODKm1xgu9zhckclH/N4DYU= +github.com/grafana/grafana-app-sdk v0.40.3/go.mod h1:j0KzHo3Sa6kd+lnwSScBNoV9Vobkg/YY9HtEjxpyPrk= +github.com/grafana/grafana-app-sdk/logging v0.40.3 h1:2VXsXXEQiqAavRP8wusRDB6rDqf5lufP7A6NfjELqPE= +github.com/grafana/grafana-app-sdk/logging v0.40.3/go.mod h1:otUD9XpJD7A5sCLb8mcs9hIXGdeV6lnhzVwe747g4RU= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/mailru/easyjson v0.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 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 h1:G7ERwszslrBzRxj//JalHPu/3yz+De2J+4aLtSRlHiY= +github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037/go.mod h1:2bpvgLBZEtENV5scfDFEtB/5+1M4hkQhDQrccEJ/qGw= +github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 h1:bQx3WeLcUWy+RletIKwUIt4x3t8n2SxavmoclizMb8c= +github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90/go.mod h1:y5+oSEHCPT/DGrS++Wc/479ERge0zTFxaF8PbGKcg2o= +github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= +github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc= +github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE= +github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= +github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= +github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE= +github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8= +github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= +github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= +github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M= +github.com/spf13/pflag v1.0.7/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.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= +github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ= +go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I= +go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4= +go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.yaml.in/yaml/v2 v2.4.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.3 h1:bXOww4E/J3f66rav3pX3m8w6jDE4knZjGOw8b5Y6iNE= +go.yaml.in/yaml/v3 v3.0.3/go.mod h1:tBHosrYAkRZjRAOREWbDnBXUf08JOwYq++0QNwQiWzI= +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.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= +golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= +golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= +golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= +golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4= +golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw= +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.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= +golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= +golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= +golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= +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.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= +google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= +gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +k8s.io/api v0.33.3 h1:SRd5t//hhkI1buzxb288fy2xvjubstenEKL9K51KBI8= +k8s.io/api v0.33.3/go.mod h1:01Y/iLUjNBM3TAvypct7DIj0M0NIZc+PzAHCIo0CYGE= +k8s.io/apimachinery v0.33.3 h1:4ZSrmNa0c/ZpZJhAgRdcsFcZOw1PQU1bALVQ0B3I5LA= +k8s.io/apimachinery v0.33.3/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM= +k8s.io/client-go v0.33.3 h1:M5AfDnKfYmVJif92ngN532gFqakcGi6RvaOF16efrpA= +k8s.io/client-go v0.33.3/go.mod h1:luqKBQggEf3shbxHY4uVENAxrDISLOarxpTKMiUuujg= +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-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4= +k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8= +k8s.io/utils v0.0.0-20241210054802-24370beab758 h1:sdbE21q2nlQtFh65saZY+rRM6x6aJJI8IUa1AmH/qa0= +k8s.io/utils v0.0.0-20241210054802-24370beab758/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 v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +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/v4 v4.7.0 h1:qPeWmscJcXP0snki5IYF79Z8xrl8ETFxgMd7wez1XkI= +sigs.k8s.io/structured-merge-diff/v4 v4.7.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= +sigs.k8s.io/yaml v1.5.0 h1:M10b2U7aEUY6hRtU870n2VTPgR5RZiL/I6Lcc2F4NUQ= +sigs.k8s.io/yaml v1.5.0/go.mod h1:wZs27Rbxoai4C0f8/9urLZtZtF3avA3gKvGyPdDqTO4= diff --git a/go.work b/go.work index 737d3363e91..d39d2cfac5b 100644 --- a/go.work +++ b/go.work @@ -13,6 +13,7 @@ use ( ./apps/investigations ./apps/playlist ./apps/plugins + ./apps/preferences ./apps/provisioning ./apps/secret ./apps/shorturl From a7e4447d0e9a431d00761870811e64295e3c96a1 Mon Sep 17 00:00:00 2001 From: Ivan Ortega Alba Date: Tue, 26 Aug 2025 14:20:22 +0200 Subject: [PATCH 02/24] DashboardMigration: V16 was removing panels when repeatInteration is null (#110151) --- .../pkg/migration/schemaversion/v16.go | 8 +- .../pkg/migration/schemaversion/v16_test.go | 57 + .../testdata/input/v14.broken-dash-min.json | 118 + .../testdata/input/v14.broken-dash.json | 3369 ++++++++++++++ .../testdata/input/v14.broken-dash1.json | 3968 ++++++++++++++++ .../testdata/input/v14.broken-dash2.json | 1951 ++++++++ .../testdata/output/v14.broken-dash-min.json | 159 + .../testdata/output/v14.broken-dash.json | 3989 ++++++++++++++++ .../testdata/output/v14.broken-dash1.json | 3991 +++++++++++++++++ .../testdata/output/v14.broken-dash2.json | 2343 ++++++++++ 10 files changed, 19951 insertions(+), 2 deletions(-) create mode 100644 apps/dashboard/pkg/migration/testdata/input/v14.broken-dash-min.json create mode 100644 apps/dashboard/pkg/migration/testdata/input/v14.broken-dash.json create mode 100644 apps/dashboard/pkg/migration/testdata/input/v14.broken-dash1.json create mode 100644 apps/dashboard/pkg/migration/testdata/input/v14.broken-dash2.json create mode 100644 apps/dashboard/pkg/migration/testdata/output/v14.broken-dash-min.json create mode 100644 apps/dashboard/pkg/migration/testdata/output/v14.broken-dash.json create mode 100644 apps/dashboard/pkg/migration/testdata/output/v14.broken-dash1.json create mode 100644 apps/dashboard/pkg/migration/testdata/output/v14.broken-dash2.json diff --git a/apps/dashboard/pkg/migration/schemaversion/v16.go b/apps/dashboard/pkg/migration/schemaversion/v16.go index cbe5854ab47..ff87598ac0d 100644 --- a/apps/dashboard/pkg/migration/schemaversion/v16.go +++ b/apps/dashboard/pkg/migration/schemaversion/v16.go @@ -66,7 +66,7 @@ func upgradeToGridLayout(dashboard map[string]interface{}) { } // Skip repeated rows (line 1031-1033 in TS) - if _, hasRepeatIteration := row["repeatIteration"]; hasRepeatIteration { + if repeatIteration, hasRepeatIteration := row["repeatIteration"]; hasRepeatIteration && repeatIteration != nil { continue } @@ -283,7 +283,11 @@ func getMaxPanelID(rows []interface{}) int { func shouldShowRows(rows []interface{}) bool { for _, rowInterface := range rows { if row, ok := rowInterface.(map[string]interface{}); ok { - if GetBoolValue(row, "collapse") || GetBoolValue(row, "showTitle") || GetStringValue(row, "repeat") != "" { + collapse := GetBoolValue(row, "collapse") + showTitle := GetBoolValue(row, "showTitle") + repeat := GetStringValue(row, "repeat") + + if collapse || showTitle || repeat != "" { return true } } diff --git a/apps/dashboard/pkg/migration/schemaversion/v16_test.go b/apps/dashboard/pkg/migration/schemaversion/v16_test.go index 201a27b88f2..e8b6032e944 100644 --- a/apps/dashboard/pkg/migration/schemaversion/v16_test.go +++ b/apps/dashboard/pkg/migration/schemaversion/v16_test.go @@ -8,6 +8,63 @@ import ( func TestV16(t *testing.T) { tests := []migrationTestCase{ + { + name: "should handle repeatIteration null", + input: map[string]interface{}{ + "schemaVersion": 15, + "rows": []interface{}{ + map[string]interface{}{ + "collapse": false, + "showTitle": true, + "title": "Overview", + "type": "row", + "repeat": nil, + "repeatIteration": nil, + "repeatRowId": nil, + "panels": []interface{}{ + map[string]interface{}{ + "id": 2, + "type": "stat", + "span": 12, + "title": "Customer Stats", + }, + }, + }, + }, + }, + expected: map[string]interface{}{ + "schemaVersion": 16, + "panels": []interface{}{ + // The stat panel should be processed and added + map[string]interface{}{ + "id": 2, + "type": "stat", + "title": "Customer Stats", + "gridPos": map[string]interface{}{ + "x": 0, + "y": 1, + "w": 24, + "h": 7, // default height + }, + }, + // The row panel should be created because showTitle is true + map[string]interface{}{ + "id": 3, // Next ID after max panel ID (2) + "type": "row", + "title": "Overview", + "collapsed": false, + "repeat": "", + "panels": []interface{}{}, + "gridPos": map[string]interface{}{ + "x": 0, + "y": 0, + "w": 24, + "h": 7, // default height + }, + }, + }, + }, + }, { name: "should create proper grid", input: map[string]interface{}{ diff --git a/apps/dashboard/pkg/migration/testdata/input/v14.broken-dash-min.json b/apps/dashboard/pkg/migration/testdata/input/v14.broken-dash-min.json new file mode 100644 index 00000000000..832fc00b21a --- /dev/null +++ b/apps/dashboard/pkg/migration/testdata/input/v14.broken-dash-min.json @@ -0,0 +1,118 @@ +{ + "annotations": { + "list": [ + + ] + }, + "editable": false, + "gnetId": null, + "graphTooltip": 1, + "hideControls": false, + "id": 22815, + "links": [ + + ], + "refresh": "1m", + "rows": [ + { + "collapse": false, + "collapsed": false, + "panels": [ + { + "datasource": "$datasource", + "fieldConfig": { + "defaults": { + "links": [], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [{ "color": "green", "value": 0 }] + }, + "unit": "none" + } + }, + "gridPos": { "h": 9, "w": 12, "x": 0, "y": 0 }, + "id": 2, + "links": [], + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": ["mean"], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "7", + "targets": [ + { + "expr": "sum(rate(http_requests_total{environment=\"$env\", region=\"$region\", service=\"webapp\"}[5m]))", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Requests/sec", + "refId": "A" + }, + { + "expr": "avg(memory_usage_bytes{environment=\"$env\", region=\"$region\", service=\"webapp\"}) / 1024 / 1024", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Memory (MB)", + "refId": "B" + }, + { + "expr": "histogram_quantile(0.95, rate(response_time_bucket{environment=\"$env\", region=\"$region\", service=\"webapp\"}[5m]))", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "95th percentile latency", + "refId": "C" + } + ], + "title": "Service Stats", + "transparent": false, + "type": "stat" + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Overview", + "titleSize": "h6", + "type": "row" + } + ], + "schemaVersion": 14, + "style": "dark", + "tags": ["example-service", "as-code"], + "templating": { + "list": [ + + ] + }, + "time": { "from": "now-2h", "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": "browser", + "title": "Service Overview Dashboard", + "uid": "ba9b93e998e09d254266b11192894a8d", + "version": 1 + } diff --git a/apps/dashboard/pkg/migration/testdata/input/v14.broken-dash.json b/apps/dashboard/pkg/migration/testdata/input/v14.broken-dash.json new file mode 100644 index 00000000000..803f5ca8042 --- /dev/null +++ b/apps/dashboard/pkg/migration/testdata/input/v14.broken-dash.json @@ -0,0 +1,3369 @@ +{ + "annotations": { + "list": [ + { + "datasource": "$datasourcelogs", + "enable": true, + "expr": "{cluster=\"$cluster\", component=\"logger\"} | json | namespace_extracted=~\"example-service.*\" | name_extracted=\"example-api\"", + "iconColor": "rgba(0, 211, 255, 1)", + "instant": false, + "name": "Deployments", + "titleFormat": "{{cluster}}/{{namespace}}" + } + ] + }, + "editable": false, + "gnetId": null, + "graphTooltip": 1, + "hideControls": false, + "id": 22815, + "links": [ + { + "asDropdown": true, + "icon": "external link", + "includeVars": true, + "keepTime": true, + "tags": ["cache-overview"], + "targetBlank": false, + "title": "Cache Dashboard", + "type": "dashboards", + "url": "" + }, + { + "asDropdown": true, + "icon": "external link", + "includeVars": true, + "keepTime": true, + "tags": ["database-monitoring"], + "targetBlank": false, + "title": "Database Dashboard", + "type": "dashboards", + "url": "" + }, + { + "asDropdown": true, + "icon": "external link", + "includeVars": true, + "keepTime": true, + "tags": ["Database", "monitoring"], + "targetBlank": false, + "title": "Database Metrics Dashboard", + "type": "dashboards", + "url": "" + } + ], + "refresh": "1m", + "rows": [ + { + "collapse": false, + "collapsed": false, + "panels": [ + { + "datasource": "$datasource", + "fieldConfig": { + "defaults": { + "links": [], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [{ "color": "green", "value": 0 }] + }, + "unit": "none" + } + }, + "gridPos": { "h": 9, "w": 12, "x": 0, "y": 0 }, + "id": 2, + "links": [], + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": ["mean"], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "7", + "targets": [ + { + "expr": "sum(app_metric_counter_a{env=\"$environment\", region=\"$region\", service=\"api\"}) - 1", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Tenants", + "refId": "A" + }, + { + "expr": "sum(app_metric_counter_b{env=\"$environment\", region=\"$region\", service=\"api\"}) - 1", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Collectors", + "refId": "B" + }, + { + "expr": "sum(app_metric_counter_c{env=\"$environment\", region=\"$region\", service=\"api\"}) - 1", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Pipelines", + "refId": "C" + } + ], + "title": "Customer Stats", + "transparent": false, + "type": "stat" + }, + { + "datasource": "$datasource", + "fieldConfig": { + "defaults": { + "links": [], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { "color": "blue", "value": 0 }, + { "color": "green", "value": 40 }, + { "color": "yellow", "value": 60 }, + { "color": "orange", "value": 80 }, + { "color": "red", "value": 100 } + ] + }, + "unit": "percent" + } + }, + "gridPos": { "h": 9, "w": 12, "x": 12, "y": 0 }, + "id": 3, + "links": [], + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": ["mean"], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "7", + "targets": [ + { + "expr": "avg(sum by (pod) (app_memory_usage_bytes{env=\"$environment\", region=\"$region\", instance=~\"example-api.*\"}) * 100) / min(app_resource_quota{env=\"$environment\", region=\"$region\", resource=\"memory\", instance=~\"example-api.*\"})", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "API Mem", + "refId": "A" + }, + { + "expr": "avg(sum by (pod) (app_memory_usage_bytes{env=\"$environment\", region=\"$region\", instance=~\"cache.*\", component=\"cache-server\"}) * 100) / min(app_resource_quota{env=\"$environment\", region=\"$region\", resource=\"memory\", instance=~\"cache.*\", component=\"cache-server\"})", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Cache Server Mem", + "refId": "B" + }, + { + "expr": "avg(sum by (pod) (app_memory_usage_bytes{env=\"$environment\", region=\"$region\", instance=~\"gateway.*\"}) * 100) / min(app_resource_quota{env=\"$environment\", region=\"$region\", resource=\"memory\", instance=~\"gateway.*\"})", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Gateway Mem ", + "refId": "C" + }, + { + "expr": "avg(sum by (pod) (rate(app_cpu_usage_ratio{env=\"$environment\", region=\"$region\", instance=~\"example-api.*\"}[1m]))) * 100 / min(app_resource_quota{env=\"$environment\", region=\"$region\", resource=\"cpu\", instance=~\"example-api.*\"})", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "API CPU", + "refId": "D" + }, + { + "expr": "avg(sum by (pod) (rate(app_cpu_usage_ratio{env=\"$environment\", region=\"$region\", instance=~\"cache.*\", component=\"cache-server\"}[1m]))) * 100 / min(app_resource_quota{env=\"$environment\", region=\"$region\", resource=\"cpu\", instance=~\"cache.*\", component=\"cache-server\"})", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Cache Server CPU", + "refId": "E" + }, + { + "expr": "avg(sum by (pod) (rate(app_cpu_usage_ratio{env=\"$environment\", region=\"$region\", instance=~\"gateway.*\"}[1m]))) * 100 / min(app_resource_quota{env=\"$environment\", region=\"$region\", resource=\"cpu\", instance=~\"gateway.*\"})", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Gateway CPU", + "refId": "F" + } + ], + "title": "Requested Resource Usage", + "transparent": false, + "type": "stat" + }, + { + "datasource": "$datasource", + "fieldConfig": { + "defaults": { + "links": [], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [{ "color": "green", "value": 0 }] + }, + "unit": "reqps" + } + }, + "gridPos": { "h": 9, "w": 12, "x": 0, "y": 9 }, + "id": 4, + "links": [], + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": ["mean"], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "7", + "targets": [ + { + "expr": "sum by () (\n label_replace(\n rate(http_request_duration_seconds_count{env=\"$environment\", region=\"$region\", service=\"api\", route!~\"health|status\"}[$__rate_interval]),\n \"status\",\n \"${1}xx\",\n \"\",\n \"([0-9])..\")\n )\n", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{}}", + "refId": "A" + } + ], + "title": "Api Requests", + "transparent": false, + "type": "stat" + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Overview", + "titleSize": "h6", + "type": "row" + }, + { + "collapse": false, + "collapsed": false, + "panels": [ + { + "aliasColors": { "Limit": "#E24D42", "Requested": "#Ffa500" }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "fill": 0, + "fillGradient": 0, + "gridPos": { "h": 9, "w": 8, "x": 0, "y": 0 }, + "id": 5, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum by (pod) (app_memory_usage_bytes{env=\"$environment\", region=\"$region\", instance=~\"example-api.*\", component=~\".*\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{pod}}", + "refId": "A" + }, + { + "expr": "min(app_resource_quota{env=\"$environment\", region=\"$region\", resource=\"memory\", instance=~\"example-api.*\", component=~\".*\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Requested", + "refId": "B" + }, + { + "expr": "min(app_resource_limit{env=\"$environment\", region=\"$region\", resource=\"memory\", instance=~\"example-api.*\", component=~\".*\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Limit", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Memory Usage", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": { "Limit": "#E24D42", "Requested": "#Ffa500" }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "fill": 0, + "fillGradient": 0, + "gridPos": { "h": 9, "w": 8, "x": 8, "y": 0 }, + "id": 6, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum by (pod) (rate(app_cpu_usage_ratio{env=\"$environment\", region=\"$region\", instance=~\"example-api.*\", component=~\".*\"}[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{pod}}", + "refId": "A" + }, + { + "expr": "min(app_resource_quota{env=\"$environment\", region=\"$region\", resource=\"cpu\", instance=~\"example-api.*\", component=~\".*\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Requested", + "refId": "B" + }, + { + "expr": "min(app_resource_limit{env=\"$environment\", region=\"$region\", resource=\"cpu\", instance=~\"example-api.*\", component=~\".*\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Limit", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "CPU Usage", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": { + "1xx": "#EAB839", + "2xx": "#7EB26D", + "3xx": "#6ED0E0", + "4xx": "#EF843C", + "5xx": "#E24D42" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "decimals": 2, + "fill": 1, + "fillGradient": 0, + "gridPos": { "h": 9, "w": 8, "x": 16, "y": 0 }, + "id": 7, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum by (status_code) (\n label_replace(\n rate(http_request_duration_seconds_count{env=\"$environment\", region=\"$region\", service=\"api\", route!~\"health|status\"}[$__rate_interval]),\n \"status\",\n \"${1}xx\",\n \"status_code\",\n \"([0-9])..\")\n )\n", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{status_code}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Throughput (requests/sec)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "decimals": 2, + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "decimals": 2, + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "fill": 1, + "fillGradient": 0, + "gridPos": { "h": 9, "w": 8, "x": 0, "y": 9 }, + "id": 8, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(http_request_duration_seconds_bucket{env=\"$environment\", region=\"$region\", service=\"api\", route!~\"health|status\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "p99", + "refId": "A" + }, + { + "expr": "histogram_quantile(0.90, sum(rate(http_request_duration_seconds_bucket{env=\"$environment\", region=\"$region\", service=\"api\", route!~\"health|status\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "p90", + "refId": "B" + }, + { + "expr": "histogram_quantile(0.50, sum(rate(http_request_duration_seconds_bucket{env=\"$environment\", region=\"$region\", service=\"api\", route!~\"health|status\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "p50", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Latency", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "fill": 1, + "fillGradient": 0, + "gridPos": { "h": 9, "w": 8, "x": 8, "y": 9 }, + "id": 9, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "(rate(http_request_duration_seconds_count{env=\"$environment\", region=\"$region\", service=\"api\", route!~\"health|status\", status_code=~\"5..\"}[$__rate_interval]) / rate(http_request_duration_seconds_count{env=\"$environment\", region=\"$region\", service=\"api\", route!~\"health|status\"}[$__rate_interval])) OR on() vector(0)", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Error rate", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Error rate", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": 1, + "min": 0, + "show": true + }, + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": 1, + "min": 0, + "show": true + } + ] + }, + { + "datasource": "$datasourcelogs", + "gridPos": { "h": 9, "w": 24, "x": 0, "y": 0 }, + "id": 10, + "options": { + "showLabels": false, + "showTime": true, + "sortOrder": "Descending", + "wrapLogMessage": true + }, + "span": 12, + "targets": [ + { + "expr": "{env=\"$environment\", region=\"$region\", service=\"api\"}", + "legendFormat": "", + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Logs", + "type": "logs" + }, + { + "datasource": "$datasourcelogs", + "gridPos": { "h": 9, "w": 24, "x": 0, "y": 9 }, + "id": 11, + "options": { + "showLabels": false, + "showTime": true, + "sortOrder": "Descending", + "wrapLogMessage": true + }, + "span": 12, + "targets": [ + { + "expr": "{env=\"$environment\", region=\"$region\", service=\"api\"} | logfmt | level=\"error\"", + "legendFormat": "", + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Errors", + "type": "logs" + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "API", + "titleSize": "h6", + "type": "row" + }, + { + "collapse": false, + "collapsed": false, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "fill": 1, + "fillGradient": 0, + "gridPos": { "h": 9, "w": 8, "x": 0, "y": 0 }, + "id": 12, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(database_query_duration_seconds_bucket{env=\"$environment\", region=\"$region\", service=\"api\"}[$__rate_interval])) by (le, method)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{method}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Client Latency p99", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "fill": 1, + "fillGradient": 0, + "gridPos": { "h": 9, "w": 8, "x": 8, "y": 0 }, + "id": 13, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.90, sum(rate(database_query_duration_seconds_bucket{env=\"$environment\", region=\"$region\", service=\"api\"}[$__rate_interval])) by (le, method)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{method}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Client Latency p90", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "fill": 1, + "fillGradient": 0, + "gridPos": { "h": 9, "w": 8, "x": 16, "y": 0 }, + "id": 14, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.50, sum(rate(database_query_duration_seconds_bucket{env=\"$environment\", region=\"$region\", service=\"api\"}[$__rate_interval])) by (le, method)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{method}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Client Latency p50", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "fill": 1, + "fillGradient": 0, + "gridPos": { "h": 9, "w": 8, "x": 0, "y": 9 }, + "id": 15, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(database_query_duration_seconds_count{env=\"$environment\", region=\"$region\", service=\"api\"}[1m]))", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Total", + "refId": "A" + }, + { + "expr": "sum(rate(database_query_duration_seconds_count{env=\"$environment\", region=\"$region\", service=\"api\", method=~\"GetConfig.*\"}[1m]))", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "GetConfig", + "refId": "B" + }, + { + "expr": "sum(rate(database_query_duration_seconds_count{env=\"$environment\", region=\"$region\", service=\"api\", method!~\"GetConfig.*\"}[1m]))", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Other", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Throughput (requests/sec)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "fill": 0, + "fillGradient": 0, + "gridPos": { "h": 9, "w": 8, "x": 8, "y": 9 }, + "id": 16, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum by (pod) (db_pool_in_use_connections {env=\"$environment\", region=\"$region\", service=\"api\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{pod}}/Active", + "refId": "A" + }, + { + "expr": "sum by (pod) (db_pool_idle_connections {env=\"$environment\", region=\"$region\", service=\"api\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{pod}}/Idle", + "refId": "B" + }, + { + "expr": "sum by (pod) (db_pool_max_open_connections {env=\"$environment\", region=\"$region\", service=\"api\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{pod}}/Max", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Connections", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "fill": 0, + "fillGradient": 0, + "gridPos": { "h": 9, "w": 8, "x": 16, "y": 9 }, + "id": 17, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum by (pod) (rate(db_pool_wait_duration_seconds_total{env=\"$environment\", region=\"$region\", service=\"api\"}[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{pod}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Connection Wait Duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Database", + "titleSize": "h6", + "type": "row" + }, + { + "collapse": false, + "collapsed": false, + "panels": [ + { + "aliasColors": { "Limit": "#E24D42", "Requested": "#Ffa500" }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "fill": 0, + "fillGradient": 0, + "gridPos": { "h": 9, "w": 12, "x": 0, "y": 0 }, + "id": 18, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum by (pod) (app_memory_usage_bytes{env=\"$environment\", region=\"$region\", instance=~\"cache.*\", component=~\"cache-server\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{pod}}", + "refId": "A" + }, + { + "expr": "min(app_resource_quota{env=\"$environment\", region=\"$region\", resource=\"memory\", instance=~\"cache.*\", component=~\"cache-server\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Requested", + "refId": "B" + }, + { + "expr": "min(app_resource_limit{env=\"$environment\", region=\"$region\", resource=\"memory\", instance=~\"cache.*\", component=~\"cache-server\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Limit", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Memory Usage", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": { "Limit": "#E24D42", "Requested": "#Ffa500" }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "fill": 0, + "fillGradient": 0, + "gridPos": { "h": 9, "w": 12, "x": 12, "y": 0 }, + "id": 19, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum by (pod) (rate(app_cpu_usage_ratio{env=\"$environment\", region=\"$region\", instance=~\"cache.*\", component=~\"cache-server\"}[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{pod}}", + "refId": "A" + }, + { + "expr": "min(app_resource_quota{env=\"$environment\", region=\"$region\", resource=\"cpu\", instance=~\"cache.*\", component=~\"cache-server\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Requested", + "refId": "B" + }, + { + "expr": "min(app_resource_limit{env=\"$environment\", region=\"$region\", resource=\"cpu\", instance=~\"cache.*\", component=~\"cache-server\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Limit", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "CPU Usage", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "fill": 1, + "fillGradient": 0, + "gridPos": { "h": 9, "w": 12, "x": 0, "y": 9 }, + "id": 20, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum by (pod) (rate(cache_set_total{env=\"$environment\", region=\"$region\", service=\"api\"}[1m]))", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{pod}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Client Sets", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "fill": 1, + "fillGradient": 0, + "gridPos": { "h": 9, "w": 12, "x": 12, "y": 9 }, + "id": 21, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum by (pod) (rate(cache_request_total{env=\"$environment\", region=\"$region\", service=\"api\"}[1m]))", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{pod}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Client Requests", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "fill": 1, + "fillGradient": 0, + "gridPos": { "h": 9, "w": 12, "x": 0, "y": 18 }, + "id": 22, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum by (pod) (rate(cache_request_hit_total{env=\"$environment\", region=\"$region\", service=\"api\"}[$__rate_interval]) / rate(cache_request_total{env=\"$environment\", region=\"$region\", service=\"api\"}[$__rate_interval]))", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{pod}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Client Cache Request Hit Rate", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "fill": 0, + "fillGradient": 0, + "gridPos": { "h": 9, "w": 12, "x": 12, "y": 18 }, + "id": 23, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(cache_request_duration_seconds_bucket{env=\"$environment\", region=\"$region\", service=\"api\"}[$__rate_interval])) by (le, method)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{method}}/request", + "refId": "A" + }, + { + "expr": "histogram_quantile(0.99, sum(rate(cache_set_duration_seconds_bucket{env=\"$environment\", region=\"$region\", service=\"api\"}[$__rate_interval])) by (le, method)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{method}}/set", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Client Latency p99", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "fill": 0, + "fillGradient": 0, + "gridPos": { "h": 9, "w": 12, "x": 0, "y": 27 }, + "id": 24, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.90, sum(rate(cache_request_duration_seconds_bucket{env=\"$environment\", region=\"$region\", service=\"api\"}[$__rate_interval])) by (le, method)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{method}}/request", + "refId": "A" + }, + { + "expr": "histogram_quantile(0.90, sum(rate(cache_set_duration_seconds_bucket{env=\"$environment\", region=\"$region\", service=\"api\"}[$__rate_interval])) by (le, method)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{method}}/set", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Client Latency p90", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "fill": 0, + "fillGradient": 0, + "gridPos": { "h": 9, "w": 12, "x": 12, "y": 27 }, + "id": 25, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.50, sum(rate(cache_request_duration_seconds_bucket{env=\"$environment\", region=\"$region\", service=\"api\"}[$__rate_interval])) by (le, method)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{method}}/request", + "refId": "A" + }, + { + "expr": "histogram_quantile(0.50, sum(rate(cache_set_duration_seconds_bucket{env=\"$environment\", region=\"$region\", service=\"api\"}[$__rate_interval])) by (le, method)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{method}}/set", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Client Latency p50", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "fill": 1, + "fillGradient": 0, + "gridPos": { "h": 9, "w": 12, "x": 0, "y": 36 }, + "id": 26, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(cache_request_duration_seconds_count{env=\"$environment\", region=\"$region\", service=\"api\"}[1m]))", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Total", + "refId": "A" + }, + { + "expr": "sum(rate(cache_request_duration_seconds_count{env=\"$environment\", region=\"$region\", service=\"api\", method=~\".*Config.*\"}[1m]))", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Config", + "refId": "B" + }, + { + "expr": "sum(rate(cache_request_duration_seconds_count{env=\"$environment\", region=\"$region\", service=\"api\", method!~\".*Config.*\"}[1m]))", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Other", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Request Throughput (requests/sec)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "fill": 1, + "fillGradient": 0, + "gridPos": { "h": 9, "w": 12, "x": 12, "y": 36 }, + "id": 27, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(cache_set_duration_seconds_count{env=\"$environment\", region=\"$region\", service=\"api\"}[1m]))", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Total", + "refId": "A" + }, + { + "expr": "sum(rate(cache_set_duration_seconds_count{env=\"$environment\", region=\"$region\", service=\"api\", method=~\".*Config.*\"}[1m]))", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Config", + "refId": "B" + }, + { + "expr": "sum(rate(cache_set_duration_seconds_count{env=\"$environment\", region=\"$region\", service=\"api\", method!~\".*Config.*\"}[1m]))", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Other", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Set Throughput (requests/sec)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Cache", + "titleSize": "h6", + "type": "row" + }, + { + "collapse": false, + "collapsed": false, + "panels": [ + { + "aliasColors": { "Limit": "#E24D42", "Requested": "#Ffa500" }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "fill": 0, + "fillGradient": 0, + "gridPos": { "h": 9, "w": 8, "x": 0, "y": 0 }, + "id": 28, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum by (pod) (app_memory_usage_bytes{env=\"$environment\", region=\"$region\", instance=~\"gateway.*\", component=~\".*\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{pod}}", + "refId": "A" + }, + { + "expr": "min(app_resource_quota{env=\"$environment\", region=\"$region\", resource=\"memory\", instance=~\"gateway.*\", component=~\".*\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Requested", + "refId": "B" + }, + { + "expr": "min(app_resource_limit{env=\"$environment\", region=\"$region\", resource=\"memory\", instance=~\"gateway.*\", component=~\".*\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Limit", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Memory Usage", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": { "Limit": "#E24D42", "Requested": "#Ffa500" }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "fill": 0, + "fillGradient": 0, + "gridPos": { "h": 9, "w": 8, "x": 8, "y": 0 }, + "id": 29, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum by (pod) (rate(app_cpu_usage_ratio{env=\"$environment\", region=\"$region\", instance=~\"gateway.*\", component=~\".*\"}[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{pod}}", + "refId": "A" + }, + { + "expr": "min(app_resource_quota{env=\"$environment\", region=\"$region\", resource=\"cpu\", instance=~\"gateway.*\", component=~\".*\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Requested", + "refId": "B" + }, + { + "expr": "min(app_resource_limit{env=\"$environment\", region=\"$region\", resource=\"cpu\", instance=~\"gateway.*\", component=~\".*\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Limit", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "CPU Usage", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "fill": 1, + "fillGradient": 0, + "gridPos": { "h": 9, "w": 8, "x": 16, "y": 0 }, + "id": 30, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(agent_management_gateway_grafanacloud_authenticator_authentication_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "p99", + "refId": "A" + }, + { + "expr": "histogram_quantile(0.90, sum(rate(agent_management_gateway_grafanacloud_authenticator_authentication_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "p90", + "refId": "B" + }, + { + "expr": "histogram_quantile(0.50, sum(rate(agent_management_gateway_grafanacloud_authenticator_authentication_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "p50", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Authenticator latency", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "fill": 1, + "fillGradient": 0, + "gridPos": { "h": 9, "w": 8, "x": 0, "y": 9 }, + "id": 31, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(agent_management_gateway_grafanacloud_cachedclient_get_instance_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "p99", + "refId": "A" + }, + { + "expr": "histogram_quantile(0.95, sum(rate(agent_management_gateway_grafanacloud_cachedclient_get_instance_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "p95", + "refId": "B" + }, + { + "expr": "histogram_quantile(0.9, sum(rate(agent_management_gateway_grafanacloud_cachedclient_get_instance_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "p90", + "refId": "C" + }, + { + "expr": "histogram_quantile(0.5, sum(rate(agent_management_gateway_grafanacloud_cachedclient_get_instance_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "p50", + "refId": "D" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "GCOM cached client latency", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "fill": 1, + "fillGradient": 0, + "gridPos": { "h": 9, "w": 8, "x": 8, "y": 9 }, + "id": 32, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(agent_management_request_downstream_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "p99", + "refId": "A" + }, + { + "expr": "histogram_quantile(0.90, sum(rate(agent_management_request_downstream_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "p90", + "refId": "B" + }, + { + "expr": "histogram_quantile(0.50, sum(rate(agent_management_request_downstream_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "p50", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Handler latency", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": { + "1xx": "#EAB839", + "2xx": "#7EB26D", + "3xx": "#6ED0E0", + "4xx": "#EF843C", + "5xx": "#E24D42" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "decimals": 2, + "fill": 1, + "fillGradient": 0, + "gridPos": { "h": 9, "w": 8, "x": 16, "y": 9 }, + "id": 33, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum by (code) (\n label_replace(\n rate(grafanacom_api_request_duration_seconds_count{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval]),\n \"status\",\n \"${1}xx\",\n \"code\",\n \"([0-9])..\")\n )\n", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{code}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Grafana.com HTTP Client - Throughput (requests/sec)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "decimals": 2, + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "decimals": 2, + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "fill": 1, + "fillGradient": 0, + "gridPos": { "h": 9, "w": 8, "x": 0, "y": 18 }, + "id": 34, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(grafanacom_api_request_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "5m", + "intervalFactor": 2, + "legendFormat": "p99", + "refId": "A" + }, + { + "expr": "histogram_quantile(0.90, sum(rate(grafanacom_api_request_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "5m", + "intervalFactor": 2, + "legendFormat": "p90", + "refId": "B" + }, + { + "expr": "histogram_quantile(0.50, sum(rate(grafanacom_api_request_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "5m", + "intervalFactor": 2, + "legendFormat": "p50", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Grafana.com HTTP Client - Latency", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "fill": 1, + "fillGradient": 0, + "gridPos": { "h": 9, "w": 8, "x": 8, "y": 18 }, + "id": 35, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "(rate(grafanacom_api_request_duration_seconds_count{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\", code=~\"5..|error\"}[$__rate_interval]) / rate(grafanacom_api_request_duration_seconds_count{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) OR on() vector(0)", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Error rate", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Grafana.com HTTP Client - Error rate", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": 1, + "min": 0, + "show": true + }, + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": 1, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": { + "1xx": "#EAB839", + "2xx": "#7EB26D", + "3xx": "#6ED0E0", + "4xx": "#EF843C", + "5xx": "#E24D42" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "decimals": 2, + "fill": 1, + "fillGradient": 0, + "gridPos": { "h": 9, "w": 8, "x": 16, "y": 18 }, + "id": 36, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum by (status_code) (\n label_replace(\n rate(auth_client_request_duration_seconds_count{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval]),\n \"status\",\n \"${1}xx\",\n \"status_code\",\n \"([0-9])..\")\n )\n", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{status_code}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Auth API HTTP Client - Throughput (requests/sec)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "decimals": 2, + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "decimals": 2, + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "fill": 1, + "fillGradient": 0, + "gridPos": { "h": 9, "w": 8, "x": 0, "y": 27 }, + "id": 37, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(auth_client_request_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "5m", + "intervalFactor": 2, + "legendFormat": "p99", + "refId": "A" + }, + { + "expr": "histogram_quantile(0.90, sum(rate(auth_client_request_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "5m", + "intervalFactor": 2, + "legendFormat": "p90", + "refId": "B" + }, + { + "expr": "histogram_quantile(0.50, sum(rate(auth_client_request_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "5m", + "intervalFactor": 2, + "legendFormat": "p50", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Auth API HTTP Client - Latency", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "fill": 1, + "fillGradient": 0, + "gridPos": { "h": 9, "w": 8, "x": 8, "y": 27 }, + "id": 38, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "(rate(auth_client_request_duration_seconds_count{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\", status_code=~\"5..|error\"}[$__rate_interval]) / rate(auth_client_request_duration_seconds_count{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) OR on() vector(0)", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Error rate", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Auth API HTTP Client - Error rate", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": 1, + "min": 0, + "show": true + }, + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": 1, + "min": 0, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Gateway", + "titleSize": "h6", + "type": "row" + } + ], + "schemaVersion": 14, + "style": "dark", + "tags": ["example-service", "as-code"], + "templating": { + "list": [ + { + "current": { "value": "prometheus-datasource" }, + "hide": 0, + "label": null, + "name": "datasource", + "options": [], + "query": "prometheus", + "refresh": 1, + "regex": "", + "type": "datasource" + }, + { + "current": { "value": "logs-datasource" }, + "hide": 0, + "label": null, + "name": "datasourcelogs", + "options": [], + "query": "loki", + "refresh": 1, + "regex": "", + "type": "datasource" + }, + { + "allValue": null, + "current": { + "text": "cluster-1", + "value": "cluster-1" + }, + "datasource": "$datasource", + "hide": 0, + "includeAll": false, + "label": null, + "multi": false, + "name": "cluster", + "options": [], + "query": "label_values(example_service_inflight_requests{}, cluster)", + "refresh": 2, + "regex": "", + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allValue": null, + "current": { + "text": "cluster-1", + "value": "cluster-1" + }, + "datasource": "$datasource", + "hide": 0, + "includeAll": false, + "label": null, + "multi": false, + "name": "namespace", + "options": [], + "query": "label_values(example_service_inflight_requests{cluster=\"$cluster\"}, namespace)", + "refresh": 2, + "regex": "example-service.*", + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + } + ] + }, + "time": { "from": "now-2h", "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": "browser", + "title": "Service Overview Dashboard", + "uid": "ba9b93e998e09d254266b11192894a8d", + "version": 1 + } diff --git a/apps/dashboard/pkg/migration/testdata/input/v14.broken-dash1.json b/apps/dashboard/pkg/migration/testdata/input/v14.broken-dash1.json new file mode 100644 index 00000000000..3e9c91b6ffd --- /dev/null +++ b/apps/dashboard/pkg/migration/testdata/input/v14.broken-dash1.json @@ -0,0 +1,3968 @@ + { + "__inputs": [ + + ], + "__requires": [ + + ], + "annotations": { + "list": [ + { + "datasource": "$datasourcelogs", + "enable": true, + "expr": "{cluster=\"$cluster\", component=\"kube-diff-logger\"} | json | namespace_extracted=~\"example-service.*\" | name_extracted=\"example-api\"", + "iconColor": "rgba(0, 211, 255, 1)", + "instant": false, + "name": "Deployments", + "titleFormat": "{{cluster}}/{{namespace}}" + } + ] + }, + "editable": false, + "gnetId": null, + "graphTooltip": 1, + "hideControls": false, + "id": null, + "links": [ + { + "asDropdown": true, + "icon": "external link", + "includeVars": true, + "keepTime": true, + "tags": [ + "cache-overview" + ], + "targetBlank": false, + "title": "Cache Dashboard", + "type": "dashboards", + "url": "" + }, + { + "asDropdown": true, + "icon": "external link", + "includeVars": true, + "keepTime": true, + "tags": [ + "cloudsql-stackdriver" + ], + "targetBlank": false, + "title": "CloudSQL Dashboard", + "type": "dashboards", + "url": "" + }, + { + "asDropdown": true, + "icon": "external link", + "includeVars": true, + "keepTime": true, + "tags": [ + "Database", + "percona" + ], + "targetBlank": false, + "title": "Database Dashboard", + "type": "dashboards", + "url": "" + } + ], + "refresh": "1m", + "rows": [ + { + "collapse": false, + "collapsed": false, + "panels": [ + { + "datasource": "$datasource", + "fieldConfig": { + "defaults": { + "links": [ + + ], + "mappings": [ + + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + } + ] + }, + "unit": "none" + } + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 2, + "links": [ + + ], + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "mean" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "7", + "targets": [ + { + "expr": "sum(app_metric_counter_a{env=\"$environment\", region=\"$region\", service=\"api\"}) - 1", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Tenants", + "refId": "A" + }, + { + "expr": "sum(app_metric_counter_b{env=\"$environment\", region=\"$region\", service=\"api\"}) - 1", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Collectors", + "refId": "B" + }, + { + "expr": "sum(app_metric_counter_c{env=\"$environment\", region=\"$region\", service=\"api\"}) - 1", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Pipelines", + "refId": "C" + } + ], + "title": "Customer Stats", + "transparent": false, + "type": "stat" + }, + { + "datasource": "$datasource", + "fieldConfig": { + "defaults": { + "links": [ + + ], + "mappings": [ + + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "blue", + "value": 0 + }, + { + "color": "green", + "value": 40 + }, + { + "color": "yellow", + "value": 60 + }, + { + "color": "orange", + "value": 80 + }, + { + "color": "red", + "value": 100 + } + ] + }, + "unit": "percent" + } + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 0 + }, + "id": 3, + "links": [ + + ], + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "mean" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "7", + "targets": [ + { + "expr": "avg(sum by (pod) (app_memory_usage_bytes{env=\"$environment\", region=\"$region\", instance=~\"example-api.*\"}) * 100) / min(app_resource_quota{env=\"$environment\", region=\"$region\", resource=\"memory\", instance=~\"example-api.*\"})", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "API Mem", + "refId": "A" + }, + { + "expr": "avg(sum by (pod) (app_memory_usage_bytes{env=\"$environment\", region=\"$region\", instance=~\"cache.*\", component=\"cache-server\"}) * 100) / min(app_resource_quota{env=\"$environment\", region=\"$region\", resource=\"memory\", instance=~\"cache.*\", component=\"cache-server\"})", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Cache Server Mem", + "refId": "B" + }, + { + "expr": "avg(sum by (pod) (app_memory_usage_bytes{env=\"$environment\", region=\"$region\", instance=~\"gateway.*\"}) * 100) / min(app_resource_quota{env=\"$environment\", region=\"$region\", resource=\"memory\", instance=~\"gateway.*\"})", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Gateway Mem ", + "refId": "C" + }, + { + "expr": "avg(sum by (pod) (rate(app_cpu_usage_ratio{env=\"$environment\", region=\"$region\", instance=~\"example-api.*\"}[1m]))) * 100 / min(app_resource_quota{env=\"$environment\", region=\"$region\", resource=\"cpu\", instance=~\"example-api.*\"})", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "API CPU", + "refId": "D" + }, + { + "expr": "avg(sum by (pod) (rate(app_cpu_usage_ratio{env=\"$environment\", region=\"$region\", instance=~\"cache.*\", component=\"cache-server\"}[1m]))) * 100 / min(app_resource_quota{env=\"$environment\", region=\"$region\", resource=\"cpu\", instance=~\"cache.*\", component=\"cache-server\"})", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Cache Server CPU", + "refId": "E" + }, + { + "expr": "avg(sum by (pod) (rate(app_cpu_usage_ratio{env=\"$environment\", region=\"$region\", instance=~\"gateway.*\"}[1m]))) * 100 / min(app_resource_quota{env=\"$environment\", region=\"$region\", resource=\"cpu\", instance=~\"gateway.*\"})", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Gateway CPU", + "refId": "F" + } + ], + "title": "Requested Resource Usage", + "transparent": false, + "type": "stat" + }, + { + "datasource": "$datasource", + "fieldConfig": { + "defaults": { + "links": [ + + ], + "mappings": [ + + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + } + ] + }, + "unit": "reqps" + } + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 9 + }, + "id": 4, + "links": [ + + ], + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "mean" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "7", + "targets": [ + { + "expr": "sum by () (\n label_replace(\n rate(example_service_request_duration_seconds_count{env=\"$environment\", region=\"$region\", service=\"api\", route!~\"|root|other|ready|metrics|debug_pprof\"}[$__rate_interval]),\n \"status\",\n \"${1}xx\",\n \"\",\n \"([0-9])..\")\n )\n", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{}}", + "refId": "A" + } + ], + "title": "Api Requests", + "transparent": false, + "type": "stat" + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Overview", + "titleSize": "h6", + "type": "row" + }, + { + "collapse": false, + "collapsed": false, + "panels": [ + { + "aliasColors": { + "Limit": "#E24D42", + "Requested": "#Ffa500" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "fill": 0, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 8, + "x": 0, + "y": 0 + }, + "id": 5, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [ + + ], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [ + + ], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum by (pod) (app_memory_usage_bytes{env=\"$environment\", region=\"$region\", instance=~\"example-api.*\", component=~\".*\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{pod}}", + "refId": "A" + }, + { + "expr": "min(app_resource_quota{env=\"$environment\", region=\"$region\", resource=\"memory\", instance=~\"example-api.*\", component=~\".*\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Requested", + "refId": "B" + }, + { + "expr": "min(app_resource_limit{env=\"$environment\", region=\"$region\", resource=\"memory\", instance=~\"example-api.*\", component=~\".*\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Limit", + "refId": "C" + } + ], + "thresholds": [ + + ], + "timeFrom": null, + "timeShift": null, + "title": "Memory Usage", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [ + + ] + }, + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": { + "Limit": "#E24D42", + "Requested": "#Ffa500" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "fill": 0, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 8, + "x": 8, + "y": 0 + }, + "id": 6, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [ + + ], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [ + + ], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum by (pod) (rate(app_cpu_usage_ratio{env=\"$environment\", region=\"$region\", instance=~\"example-api.*\", component=~\".*\"}[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{pod}}", + "refId": "A" + }, + { + "expr": "min(app_resource_quota{env=\"$environment\", region=\"$region\", resource=\"cpu\", instance=~\"example-api.*\", component=~\".*\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Requested", + "refId": "B" + }, + { + "expr": "min(app_resource_limit{env=\"$environment\", region=\"$region\", resource=\"cpu\", instance=~\"example-api.*\", component=~\".*\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Limit", + "refId": "C" + } + ], + "thresholds": [ + + ], + "timeFrom": null, + "timeShift": null, + "title": "CPU Usage", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [ + + ] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": { + "1xx": "#EAB839", + "2xx": "#7EB26D", + "3xx": "#6ED0E0", + "4xx": "#EF843C", + "5xx": "#E24D42" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "decimals": 2, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 8, + "x": 16, + "y": 0 + }, + "id": 7, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [ + + ], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [ + + ], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum by (status_code) (\n label_replace(\n rate(example_service_request_duration_seconds_count{env=\"$environment\", region=\"$region\", service=\"api\", route!~\"|root|other|ready|metrics|debug_pprof\"}[$__rate_interval]),\n \"status\",\n \"${1}xx\",\n \"status_code\",\n \"([0-9])..\")\n )\n", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{status_code}}", + "refId": "A" + } + ], + "thresholds": [ + + ], + "timeFrom": null, + "timeShift": null, + "title": "Throughput (requests/sec)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [ + + ] + }, + "yaxes": [ + { + "decimals": 2, + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "decimals": 2, + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": { + + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 8, + "x": 0, + "y": 9 + }, + "id": 8, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [ + + ], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [ + + ], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(example_service_request_duration_seconds_bucket{env=\"$environment\", region=\"$region\", service=\"api\", route!~\"|root|other|ready|metrics|debug_pprof\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "p99", + "refId": "A" + }, + { + "expr": "histogram_quantile(0.90, sum(rate(example_service_request_duration_seconds_bucket{env=\"$environment\", region=\"$region\", service=\"api\", route!~\"|root|other|ready|metrics|debug_pprof\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "p90", + "refId": "B" + }, + { + "expr": "histogram_quantile(0.50, sum(rate(example_service_request_duration_seconds_bucket{env=\"$environment\", region=\"$region\", service=\"api\", route!~\"|root|other|ready|metrics|debug_pprof\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "p50", + "refId": "C" + } + ], + "thresholds": [ + + ], + "timeFrom": null, + "timeShift": null, + "title": "Latency", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [ + + ] + }, + "yaxes": [ + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": { + + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 8, + "x": 8, + "y": 9 + }, + "id": 9, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [ + + ], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [ + + ], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "(rate(example_service_request_duration_seconds_count{env=\"$environment\", region=\"$region\", service=\"api\", route!~\"|root|other|ready|metrics|debug_pprof\", status_code=~\"5..\"}[$__rate_interval]) / rate(example_service_request_duration_seconds_count{env=\"$environment\", region=\"$region\", service=\"api\", route!~\"|root|other|ready|metrics|debug_pprof\"}[$__rate_interval])) OR on() vector(0)", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Error rate", + "refId": "A" + } + ], + "thresholds": [ + + ], + "timeFrom": null, + "timeShift": null, + "title": "Error rate", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [ + + ] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": 1, + "min": 0, + "show": true + }, + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": 1, + "min": 0, + "show": true + } + ] + }, + { + "datasource": "$datasourcelogs", + "gridPos": { + "h": 9, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 10, + "options": { + "showLabels": false, + "showTime": true, + "sortOrder": "Descending", + "wrapLogMessage": true + }, + "span": 12, + "targets": [ + { + "expr": "{env=\"$environment\", region=\"$region\", service=\"api\"}", + "legendFormat": "", + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Logs", + "type": "logs" + }, + { + "datasource": "$datasourcelogs", + "gridPos": { + "h": 9, + "w": 24, + "x": 0, + "y": 9 + }, + "id": 11, + "options": { + "showLabels": false, + "showTime": true, + "sortOrder": "Descending", + "wrapLogMessage": true + }, + "span": 12, + "targets": [ + { + "expr": "{env=\"$environment\", region=\"$region\", service=\"api\"} | logfmt | level=\"error\"", + "legendFormat": "", + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Errors", + "type": "logs" + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "API", + "titleSize": "h6", + "type": "row" + }, + { + "collapse": false, + "collapsed": false, + "panels": [ + { + "aliasColors": { + + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 8, + "x": 0, + "y": 0 + }, + "id": 12, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [ + + ], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [ + + ], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(example_service_database_request_duration_seconds_bucket{env=\"$environment\", region=\"$region\", service=\"api\"}[$__rate_interval])) by (le, method)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{method}}", + "refId": "A" + } + ], + "thresholds": [ + + ], + "timeFrom": null, + "timeShift": null, + "title": "Client Latency p99", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [ + + ] + }, + "yaxes": [ + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": { + + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 8, + "x": 8, + "y": 0 + }, + "id": 13, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [ + + ], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [ + + ], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.90, sum(rate(example_service_database_request_duration_seconds_bucket{env=\"$environment\", region=\"$region\", service=\"api\"}[$__rate_interval])) by (le, method)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{method}}", + "refId": "A" + } + ], + "thresholds": [ + + ], + "timeFrom": null, + "timeShift": null, + "title": "Client Latency p90", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [ + + ] + }, + "yaxes": [ + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": { + + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 8, + "x": 16, + "y": 0 + }, + "id": 14, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [ + + ], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [ + + ], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.50, sum(rate(example_service_database_request_duration_seconds_bucket{env=\"$environment\", region=\"$region\", service=\"api\"}[$__rate_interval])) by (le, method)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{method}}", + "refId": "A" + } + ], + "thresholds": [ + + ], + "timeFrom": null, + "timeShift": null, + "title": "Client Latency p50", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [ + + ] + }, + "yaxes": [ + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": { + + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 8, + "x": 0, + "y": 9 + }, + "id": 15, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [ + + ], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [ + + ], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(example_service_database_request_duration_seconds_count{env=\"$environment\", region=\"$region\", service=\"api\"}[1m]))", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Total", + "refId": "A" + }, + { + "expr": "sum(rate(example_service_database_request_duration_seconds_count{env=\"$environment\", region=\"$region\", service=\"api\", method=~\"GetConfig.*\"}[1m]))", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "GetConfig", + "refId": "B" + }, + { + "expr": "sum(rate(example_service_database_request_duration_seconds_count{env=\"$environment\", region=\"$region\", service=\"api\", method!~\"GetConfig.*\"}[1m]))", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Other", + "refId": "C" + } + ], + "thresholds": [ + + ], + "timeFrom": null, + "timeShift": null, + "title": "Throughput (requests/sec)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [ + + ] + }, + "yaxes": [ + { + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": { + + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "fill": 0, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 8, + "x": 8, + "y": 9 + }, + "id": 16, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [ + + ], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [ + + ], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum by (pod) (go_sql_in_use_connections {env=\"$environment\", region=\"$region\", service=\"api\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{pod}}/Active", + "refId": "A" + }, + { + "expr": "sum by (pod) (go_sql_idle_connections {env=\"$environment\", region=\"$region\", service=\"api\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{pod}}/Idle", + "refId": "B" + }, + { + "expr": "sum by (pod) (go_sql_max_open_connections {env=\"$environment\", region=\"$region\", service=\"api\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{pod}}/Max", + "refId": "C" + } + ], + "thresholds": [ + + ], + "timeFrom": null, + "timeShift": null, + "title": "Connections", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [ + + ] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": { + + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "fill": 0, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 8, + "x": 16, + "y": 9 + }, + "id": 17, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [ + + ], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [ + + ], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum by (pod) (rate(go_sql_wait_duration_seconds_total{env=\"$environment\", region=\"$region\", service=\"api\"}[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{pod}}", + "refId": "A" + } + ], + "thresholds": [ + + ], + "timeFrom": null, + "timeShift": null, + "title": "Connection Wait Duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [ + + ] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Database", + "titleSize": "h6", + "type": "row" + }, + { + "collapse": false, + "collapsed": false, + "panels": [ + { + "aliasColors": { + "Limit": "#E24D42", + "Requested": "#Ffa500" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "fill": 0, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 18, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [ + + ], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [ + + ], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum by (pod) (app_memory_usage_bytes{env=\"$environment\", region=\"$region\", instance=~\"cache.*\", component=~\"cache-server\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{pod}}", + "refId": "A" + }, + { + "expr": "min(app_resource_quota{env=\"$environment\", region=\"$region\", resource=\"memory\", instance=~\"cache.*\", component=~\"cache-server\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Requested", + "refId": "B" + }, + { + "expr": "min(app_resource_limit{env=\"$environment\", region=\"$region\", resource=\"memory\", instance=~\"cache.*\", component=~\"cache-server\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Limit", + "refId": "C" + } + ], + "thresholds": [ + + ], + "timeFrom": null, + "timeShift": null, + "title": "Memory Usage", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [ + + ] + }, + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": { + "Limit": "#E24D42", + "Requested": "#Ffa500" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "fill": 0, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 0 + }, + "id": 19, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [ + + ], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [ + + ], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum by (pod) (rate(app_cpu_usage_ratio{env=\"$environment\", region=\"$region\", instance=~\"cache.*\", component=~\"cache-server\"}[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{pod}}", + "refId": "A" + }, + { + "expr": "min(app_resource_quota{env=\"$environment\", region=\"$region\", resource=\"cpu\", instance=~\"cache.*\", component=~\"cache-server\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Requested", + "refId": "B" + }, + { + "expr": "min(app_resource_limit{env=\"$environment\", region=\"$region\", resource=\"cpu\", instance=~\"cache.*\", component=~\"cache-server\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Limit", + "refId": "C" + } + ], + "thresholds": [ + + ], + "timeFrom": null, + "timeShift": null, + "title": "CPU Usage", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [ + + ] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": { + + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 9 + }, + "id": 20, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [ + + ], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [ + + ], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum by (pod) (rate(example_service_cache_set_total{env=\"$environment\", region=\"$region\", service=\"api\"}[1m]))", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{pod}}", + "refId": "A" + } + ], + "thresholds": [ + + ], + "timeFrom": null, + "timeShift": null, + "title": "Client Sets", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [ + + ] + }, + "yaxes": [ + { + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": { + + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 9 + }, + "id": 21, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [ + + ], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [ + + ], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum by (pod) (rate(example_service_cache_request_total{env=\"$environment\", region=\"$region\", service=\"api\"}[1m]))", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{pod}}", + "refId": "A" + } + ], + "thresholds": [ + + ], + "timeFrom": null, + "timeShift": null, + "title": "Client Requests", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [ + + ] + }, + "yaxes": [ + { + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": { + + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 18 + }, + "id": 22, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [ + + ], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [ + + ], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum by (pod) (rate(example_service_cache_request_hit_total{env=\"$environment\", region=\"$region\", service=\"api\"}[$__rate_interval]) / rate(example_service_cache_request_total{env=\"$environment\", region=\"$region\", service=\"api\"}[$__rate_interval]))", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{pod}}", + "refId": "A" + } + ], + "thresholds": [ + + ], + "timeFrom": null, + "timeShift": null, + "title": "Client Cache Request Hit Rate", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [ + + ] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": { + + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "fill": 0, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 18 + }, + "id": 23, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [ + + ], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [ + + ], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(example_service_cache_request_duration_seconds_bucket{env=\"$environment\", region=\"$region\", service=\"api\"}[$__rate_interval])) by (le, method)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{method}}/request", + "refId": "A" + }, + { + "expr": "histogram_quantile(0.99, sum(rate(example_service_cache_set_duration_seconds_bucket{env=\"$environment\", region=\"$region\", service=\"api\"}[$__rate_interval])) by (le, method)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{method}}/set", + "refId": "B" + } + ], + "thresholds": [ + + ], + "timeFrom": null, + "timeShift": null, + "title": "Client Latency p99", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [ + + ] + }, + "yaxes": [ + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": { + + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "fill": 0, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 27 + }, + "id": 24, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [ + + ], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [ + + ], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.90, sum(rate(example_service_cache_request_duration_seconds_bucket{env=\"$environment\", region=\"$region\", service=\"api\"}[$__rate_interval])) by (le, method)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{method}}/request", + "refId": "A" + }, + { + "expr": "histogram_quantile(0.90, sum(rate(example_service_cache_set_duration_seconds_bucket{env=\"$environment\", region=\"$region\", service=\"api\"}[$__rate_interval])) by (le, method)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{method}}/set", + "refId": "B" + } + ], + "thresholds": [ + + ], + "timeFrom": null, + "timeShift": null, + "title": "Client Latency p90", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [ + + ] + }, + "yaxes": [ + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": { + + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "fill": 0, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 27 + }, + "id": 25, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [ + + ], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [ + + ], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.50, sum(rate(example_service_cache_request_duration_seconds_bucket{env=\"$environment\", region=\"$region\", service=\"api\"}[$__rate_interval])) by (le, method)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{method}}/request", + "refId": "A" + }, + { + "expr": "histogram_quantile(0.50, sum(rate(example_service_cache_set_duration_seconds_bucket{env=\"$environment\", region=\"$region\", service=\"api\"}[$__rate_interval])) by (le, method)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{method}}/set", + "refId": "B" + } + ], + "thresholds": [ + + ], + "timeFrom": null, + "timeShift": null, + "title": "Client Latency p50", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [ + + ] + }, + "yaxes": [ + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": { + + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 36 + }, + "id": 26, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [ + + ], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [ + + ], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(example_service_cache_request_duration_seconds_count{env=\"$environment\", region=\"$region\", service=\"api\"}[1m]))", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Total", + "refId": "A" + }, + { + "expr": "sum(rate(example_service_cache_request_duration_seconds_count{env=\"$environment\", region=\"$region\", service=\"api\", method=~\".*Config.*\"}[1m]))", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Config", + "refId": "B" + }, + { + "expr": "sum(rate(example_service_cache_request_duration_seconds_count{env=\"$environment\", region=\"$region\", service=\"api\", method!~\".*Config.*\"}[1m]))", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Other", + "refId": "C" + } + ], + "thresholds": [ + + ], + "timeFrom": null, + "timeShift": null, + "title": "Request Throughput (requests/sec)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [ + + ] + }, + "yaxes": [ + { + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": { + + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 36 + }, + "id": 27, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [ + + ], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [ + + ], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(example_service_cache_set_duration_seconds_count{env=\"$environment\", region=\"$region\", service=\"api\"}[1m]))", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Total", + "refId": "A" + }, + { + "expr": "sum(rate(example_service_cache_set_duration_seconds_count{env=\"$environment\", region=\"$region\", service=\"api\", method=~\".*Config.*\"}[1m]))", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Config", + "refId": "B" + }, + { + "expr": "sum(rate(example_service_cache_set_duration_seconds_count{env=\"$environment\", region=\"$region\", service=\"api\", method!~\".*Config.*\"}[1m]))", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Other", + "refId": "C" + } + ], + "thresholds": [ + + ], + "timeFrom": null, + "timeShift": null, + "title": "Set Throughput (requests/sec)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [ + + ] + }, + "yaxes": [ + { + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Cache", + "titleSize": "h6", + "type": "row" + }, + { + "collapse": false, + "collapsed": false, + "panels": [ + { + "aliasColors": { + "Limit": "#E24D42", + "Requested": "#Ffa500" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "fill": 0, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 8, + "x": 0, + "y": 0 + }, + "id": 28, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [ + + ], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [ + + ], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum by (pod) (app_memory_usage_bytes{env=\"$environment\", region=\"$region\", instance=~\"gateway.*\", component=~\".*\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{pod}}", + "refId": "A" + }, + { + "expr": "min(app_resource_quota{env=\"$environment\", region=\"$region\", resource=\"memory\", instance=~\"gateway.*\", component=~\".*\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Requested", + "refId": "B" + }, + { + "expr": "min(app_resource_limit{env=\"$environment\", region=\"$region\", resource=\"memory\", instance=~\"gateway.*\", component=~\".*\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Limit", + "refId": "C" + } + ], + "thresholds": [ + + ], + "timeFrom": null, + "timeShift": null, + "title": "Memory Usage", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [ + + ] + }, + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": { + "Limit": "#E24D42", + "Requested": "#Ffa500" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "fill": 0, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 8, + "x": 8, + "y": 0 + }, + "id": 29, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [ + + ], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [ + + ], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum by (pod) (rate(app_cpu_usage_ratio{env=\"$environment\", region=\"$region\", instance=~\"gateway.*\", component=~\".*\"}[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{pod}}", + "refId": "A" + }, + { + "expr": "min(app_resource_quota{env=\"$environment\", region=\"$region\", resource=\"cpu\", instance=~\"gateway.*\", component=~\".*\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Requested", + "refId": "B" + }, + { + "expr": "min(app_resource_limit{env=\"$environment\", region=\"$region\", resource=\"cpu\", instance=~\"gateway.*\", component=~\".*\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Limit", + "refId": "C" + } + ], + "thresholds": [ + + ], + "timeFrom": null, + "timeShift": null, + "title": "CPU Usage", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [ + + ] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": { + + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 8, + "x": 16, + "y": 0 + }, + "id": 30, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [ + + ], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [ + + ], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(agent_management_gateway_grafanacloud_authenticator_authentication_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "p99", + "refId": "A" + }, + { + "expr": "histogram_quantile(0.90, sum(rate(agent_management_gateway_grafanacloud_authenticator_authentication_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "p90", + "refId": "B" + }, + { + "expr": "histogram_quantile(0.50, sum(rate(agent_management_gateway_grafanacloud_authenticator_authentication_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "p50", + "refId": "C" + } + ], + "thresholds": [ + + ], + "timeFrom": null, + "timeShift": null, + "title": "Authenticator latency", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [ + + ] + }, + "yaxes": [ + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": { + + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 8, + "x": 0, + "y": 9 + }, + "id": 31, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [ + + ], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [ + + ], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(agent_management_gateway_grafanacloud_cachedclient_get_instance_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "p99", + "refId": "A" + }, + { + "expr": "histogram_quantile(0.95, sum(rate(agent_management_gateway_grafanacloud_cachedclient_get_instance_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "p95", + "refId": "B" + }, + { + "expr": "histogram_quantile(0.9, sum(rate(agent_management_gateway_grafanacloud_cachedclient_get_instance_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "p90", + "refId": "C" + }, + { + "expr": "histogram_quantile(0.5, sum(rate(agent_management_gateway_grafanacloud_cachedclient_get_instance_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "p50", + "refId": "D" + } + ], + "thresholds": [ + + ], + "timeFrom": null, + "timeShift": null, + "title": "GCOM cached client latency", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [ + + ] + }, + "yaxes": [ + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": { + + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 8, + "x": 8, + "y": 9 + }, + "id": 32, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [ + + ], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [ + + ], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(agent_management_request_downstream_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "p99", + "refId": "A" + }, + { + "expr": "histogram_quantile(0.90, sum(rate(agent_management_request_downstream_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "p90", + "refId": "B" + }, + { + "expr": "histogram_quantile(0.50, sum(rate(agent_management_request_downstream_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "p50", + "refId": "C" + } + ], + "thresholds": [ + + ], + "timeFrom": null, + "timeShift": null, + "title": "Handler latency", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [ + + ] + }, + "yaxes": [ + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": { + "1xx": "#EAB839", + "2xx": "#7EB26D", + "3xx": "#6ED0E0", + "4xx": "#EF843C", + "5xx": "#E24D42" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "decimals": 2, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 8, + "x": 16, + "y": 9 + }, + "id": 33, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [ + + ], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [ + + ], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum by (code) (\n label_replace(\n rate(grafanacom_api_request_duration_seconds_count{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval]),\n \"status\",\n \"${1}xx\",\n \"code\",\n \"([0-9])..\")\n )\n", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{code}}", + "refId": "A" + } + ], + "thresholds": [ + + ], + "timeFrom": null, + "timeShift": null, + "title": "Grafana.com HTTP Client - Throughput (requests/sec)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [ + + ] + }, + "yaxes": [ + { + "decimals": 2, + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "decimals": 2, + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": { + + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 8, + "x": 0, + "y": 18 + }, + "id": 34, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [ + + ], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [ + + ], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(grafanacom_api_request_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "5m", + "intervalFactor": 2, + "legendFormat": "p99", + "refId": "A" + }, + { + "expr": "histogram_quantile(0.90, sum(rate(grafanacom_api_request_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "5m", + "intervalFactor": 2, + "legendFormat": "p90", + "refId": "B" + }, + { + "expr": "histogram_quantile(0.50, sum(rate(grafanacom_api_request_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "5m", + "intervalFactor": 2, + "legendFormat": "p50", + "refId": "C" + } + ], + "thresholds": [ + + ], + "timeFrom": null, + "timeShift": null, + "title": "Grafana.com HTTP Client - Latency", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [ + + ] + }, + "yaxes": [ + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": { + + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 8, + "x": 8, + "y": 18 + }, + "id": 35, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [ + + ], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [ + + ], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "(rate(grafanacom_api_request_duration_seconds_count{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\", code=~\"5..|error\"}[$__rate_interval]) / rate(grafanacom_api_request_duration_seconds_count{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) OR on() vector(0)", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Error rate", + "refId": "A" + } + ], + "thresholds": [ + + ], + "timeFrom": null, + "timeShift": null, + "title": "Grafana.com HTTP Client - Error rate", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [ + + ] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": 1, + "min": 0, + "show": true + }, + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": 1, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": { + "1xx": "#EAB839", + "2xx": "#7EB26D", + "3xx": "#6ED0E0", + "4xx": "#EF843C", + "5xx": "#E24D42" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "decimals": 2, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 8, + "x": 16, + "y": 18 + }, + "id": 36, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [ + + ], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [ + + ], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum by (status_code) (\n label_replace(\n rate(auth_client_request_duration_seconds_count{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval]),\n \"status\",\n \"${1}xx\",\n \"status_code\",\n \"([0-9])..\")\n )\n", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{status_code}}", + "refId": "A" + } + ], + "thresholds": [ + + ], + "timeFrom": null, + "timeShift": null, + "title": "Auth API HTTP Client - Throughput (requests/sec)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [ + + ] + }, + "yaxes": [ + { + "decimals": 2, + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "decimals": 2, + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": { + + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 8, + "x": 0, + "y": 27 + }, + "id": 37, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [ + + ], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [ + + ], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(auth_client_request_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "5m", + "intervalFactor": 2, + "legendFormat": "p99", + "refId": "A" + }, + { + "expr": "histogram_quantile(0.90, sum(rate(auth_client_request_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "5m", + "intervalFactor": 2, + "legendFormat": "p90", + "refId": "B" + }, + { + "expr": "histogram_quantile(0.50, sum(rate(auth_client_request_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "5m", + "intervalFactor": 2, + "legendFormat": "p50", + "refId": "C" + } + ], + "thresholds": [ + + ], + "timeFrom": null, + "timeShift": null, + "title": "Auth API HTTP Client - Latency", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [ + + ] + }, + "yaxes": [ + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": { + + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 8, + "x": 8, + "y": 27 + }, + "id": 38, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [ + + ], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [ + + ], + "spaceLength": 10, + "span": 4, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "(rate(auth_client_request_duration_seconds_count{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\", status_code=~\"5..|error\"}[$__rate_interval]) / rate(auth_client_request_duration_seconds_count{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) OR on() vector(0)", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Error rate", + "refId": "A" + } + ], + "thresholds": [ + + ], + "timeFrom": null, + "timeShift": null, + "title": "Auth API HTTP Client - Error rate", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [ + + ] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": 1, + "min": 0, + "show": true + }, + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": 1, + "min": 0, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Gateway", + "titleSize": "h6", + "type": "row" + } + ], + "schemaVersion": 14, + "style": "dark", + "tags": [ + "example-service", + "as-code" + ], + "templating": { + "list": [ + { + "current": { + "value": "prometheus-datasource" + }, + "hide": 0, + "label": null, + "name": "datasource", + "options": [ + + ], + "query": "prometheus", + "refresh": 1, + "regex": "", + "type": "datasource" + }, + { + "current": { + "value": "logs-datasource" + }, + "hide": 0, + "label": null, + "name": "datasourcelogs", + "options": [ + + ], + "query": "loki", + "refresh": 1, + "regex": "", + "type": "datasource" + }, + { + "allValue": null, + "current": { + "text": "cluster-1", + "value": "cluster-1" + }, + "datasource": "$datasource", + "hide": 0, + "includeAll": false, + "label": null, + "multi": false, + "name": "cluster", + "options": [ + + ], + "query": "label_values(example_service_inflight_requests{}, cluster)", + "refresh": 2, + "regex": "", + "sort": 0, + "tagValuesQuery": "", + "tags": [ + + ], + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allValue": null, + "current": { + "text": "cluster-1", + "value": "cluster-1" + }, + "datasource": "$datasource", + "hide": 0, + "includeAll": false, + "label": null, + "multi": false, + "name": "namespace", + "options": [ + + ], + "query": "label_values(example_service_inflight_requests{cluster=\"$cluster\"}, namespace)", + "refresh": 2, + "regex": "example-service.*", + "sort": 0, + "tagValuesQuery": "", + "tags": [ + + ], + "tagsQuery": "", + "type": "query", + "useTags": false + } + ] + }, + "time": { + "from": "now-2h", + "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": "browser", + "title": "Service Overview Dashboard", + "uid": "ba9b93e998e09d254266b11192894a8d", + "version": 0 + } diff --git a/apps/dashboard/pkg/migration/testdata/input/v14.broken-dash2.json b/apps/dashboard/pkg/migration/testdata/input/v14.broken-dash2.json new file mode 100644 index 00000000000..ce0f5f3ef05 --- /dev/null +++ b/apps/dashboard/pkg/migration/testdata/input/v14.broken-dash2.json @@ -0,0 +1,1951 @@ +{ + "__requires": [ + { + "id": "grafana", + "name": "Grafana", + "type": "grafana", + "version": "8.0.0" + } + ], + "annotations": { + "list": [ + { + "datasource": "$datasource", + "enable": true, + "expr": "ALERTS{alertname=\"MetricsRolloutUnderway\", region=~\"$region\", env=~\"$environment\", alertstate=\"firing\"}", + "hide": false, + "iconColor": "rgba(255, 96, 96, 1)", + "name": "rollouts", + "showIn": 0, + "tags": [], + "titleFormat": "Rollout was underway in {{cluster}}/{{namespace}}", + "type": "tags" + } + ] + }, + "editable": false, + "gnetId": null, + "graphTooltip": 1, + "hideControls": false, + "links": [], + "refresh": "5m", + "rows": [ + { + "collapse": false, + "height": "250px", + "panels": [ + { + "content": "The 'Status' panel shows an overview on the cluster health over the time.\nTo investigate failures, see a specific dashboard:\n\n- Writes\n- Reads\n- Rule evaluations\n- Alerting notifications\n- Object storage\n", + "datasource": null, + "description": "", + "id": 1, + "mode": "markdown", + "span": 3, + "title": "", + "transparent": true, + "type": "text" + }, + { + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "#7EB26D", + "value": null + }, + { + "color": "#EAB839", + "value": 0.01 + }, + { + "color": "#E24D42", + "value": 0.05 + } + ] + } + } + }, + "id": 2, + "options": { + "showValue": "never" + }, + "span": 6, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "exemplar": false, + "expr": "((\n # gRPC errors are not tracked as 5xx but \"error\".\n sum(histogram_count(rate(http_request_duration_seconds{service=~\"gateway\", route=~\"api_v1_write\",status_code=~\"5.*|error\"}[$__rate_interval])))\n or\n # Handle the case no failure has been tracked yet.\n vector(0)\n)\n/\nsum(histogram_count(rate(http_request_duration_seconds{service=~\"gateway\", route=~\"api_v1_write\"}[$__rate_interval])))\n) and on() (vector($latency_metrics) == -1)", + "instant": false, + "legendFormat": "Writes", + "range": true + }, + { + "datasource": { + "uid": "$datasource" + }, + "exemplar": false, + "expr": "((\n # gRPC errors are not tracked as 5xx but \"error\".\n sum(rate(http_request_duration_seconds_count{service=~\"gateway\", route=~\"api_v1_write\",status_code=~\"5.*|error\"}[$__rate_interval]))\n or\n # Handle the case no failure has been tracked yet.\n vector(0)\n)\n/\nsum(rate(http_request_duration_seconds_count{service=~\"gateway\", route=~\"api_v1_write\"}[$__rate_interval]))\n) and on() (vector($latency_metrics) == 1)", + "instant": false, + "legendFormat": "Writes", + "range": true + }, + { + "datasource": { + "uid": "$datasource" + }, + "exemplar": false, + "expr": "((\n # gRPC errors are not tracked as 5xx but \"error\".\n sum(histogram_count(rate(http_request_duration_seconds{service=~\"gateway\", route=~\"api_v1_query\",status_code=~\"5.*|error\"}[$__rate_interval])))\n or\n # Handle the case no failure has been tracked yet.\n vector(0)\n)\n/\nsum(histogram_count(rate(http_request_duration_seconds{service=~\"gateway\", route=~\"api_v1_query\"}[$__rate_interval])))\n) and on() (vector($latency_metrics) == -1)", + "instant": false, + "legendFormat": "Reads", + "range": true + }, + { + "datasource": { + "uid": "$datasource" + }, + "exemplar": false, + "expr": "((\n # gRPC errors are not tracked as 5xx but \"error\".\n sum(rate(http_request_duration_seconds_count{service=~\"gateway\", route=~\"api_v1_query\",status_code=~\"5.*|error\"}[$__rate_interval]))\n or\n # Handle the case no failure has been tracked yet.\n vector(0)\n)\n/\nsum(rate(http_request_duration_seconds_count{service=~\"gateway\", route=~\"api_v1_query\"}[$__rate_interval]))\n) and on() (vector($latency_metrics) == 1)", + "instant": false, + "legendFormat": "Reads", + "range": true + }, + { + "datasource": { + "uid": "$datasource" + }, + "exemplar": false, + "expr": "(\n (\n sum(rate(monitoring_rule_evaluation_failures_total{service=~\"rule-engine\"}[$__rate_interval]))\n +\n # Consider missed evaluations as failures.\n sum(rate(monitoring_rule_group_iterations_missed_total{service=~\"rule-engine\"}[$__rate_interval]))\n )\n or\n # Handle the case no failure has been tracked yet.\n vector(0)\n)\n/\nsum(rate(monitoring_rule_evaluations_total{service=~\"rule-engine\"}[$__rate_interval]))\n", + "instant": false, + "legendFormat": "Rule evaluations", + "range": true + }, + { + "datasource": { + "uid": "$datasource" + }, + "exemplar": false, + "expr": "(\n # Failed notifications from ruler to Alertmanager (handling the case the ruler metrics are missing).\n ((sum(rate(alert_notifications_errors_total{service=~\"rule-engine\"}[$__rate_interval]))\n) or vector(0))\n +\n # Failed notifications from Alertmanager to receivers (handling the case the alertmanager metrics are missing).\n ((sum(service_integration:alerting_service_notifications_failed_total:rate5m{service=~\"alerting\"}) or vector(0)\n) or vector(0))\n)\n/\n(\n # Total notifications from ruler to Alertmanager (handling the case the ruler metrics are missing).\n ((sum(rate(alert_notifications_sent_total{service=~\"rule-engine\"}[$__rate_interval]))\n) or vector(0))\n +\n # Total notifications from Alertmanager to receivers (handling the case the alertmanager metrics are missing).\n ((sum(service_integration:alerting_service_notifications_total:rate5m{service=~\"alerting\"}) or vector(0)\n) or vector(0))\n)\n", + "instant": false, + "legendFormat": "Alerting notifications", + "range": true + }, + { + "datasource": { + "uid": "$datasource" + }, + "exemplar": false, + "expr": "sum(rate(storage_operation_failures_total{env!=\"\"}[$__rate_interval]))\n/\nsum(rate(storage_operations_total{env!=\"\"}[$__rate_interval]))\n", + "instant": false, + "legendFormat": "Object storage", + "range": true + } + ], + "title": "Status", + "type": "state-timeline" + }, + { + "id": 3, + "options": { + "alertInstanceLabelFilter": "env!=\"\"", + "alertName": "Metrics", + "dashboardAlerts": false, + "maxItems": 100, + "sortOrder": 3, + "stateFilter": { + "error": true, + "firing": true, + "noData": false, + "normal": false, + "pending": false + } + }, + "span": 3, + "title": "Firing alerts", + "type": "alertlist" + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Service cluster health", + "titleSize": "h6" + }, + { + "collapse": false, + "height": "250px", + "panels": [ + { + "content": "These panels show an overview on the write path. Requests rate and latency is measured on the gateway.\nTo examine the write path in detail, see a specific dashboard:\n\n- Writes\n- Writes resources\n- Writes networking\n- Overview resources\n- Overview networking\n", + "datasource": null, + "description": "", + "id": 4, + "mode": "markdown", + "span": 3, + "title": "", + "transparent": true, + "type": "text" + }, + { + "datasource": "$datasource", + "fieldConfig": { + "defaults": { + "custom": { + "drawStyle": "line", + "fillOpacity": 100, + "lineWidth": 0, + "pointSize": 5, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "normal" + } + }, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [] + }, + "unit": "reqps" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "1xx" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#EAB839", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "2xx" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#7EB26D", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "3xx" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#6ED0E0", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "4xx" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#EF843C", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "5xx" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#E24D42", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "OK" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#7EB26D", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "cancel" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#A9A9A9", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "error" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#E24D42", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "success" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#7EB26D", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "1xx" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#EAB839", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "2xx" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#7EB26D", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "3xx" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#6ED0E0", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "4xx" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#EF843C", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "5xx" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#E24D42", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Canceled" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#A9A9A9", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "OK" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#7EB26D", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Success" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#7EB26D", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "cancel" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#A9A9A9", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "error" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#E24D42", + "mode": "fixed" + } + } + ] + } + ] + }, + "id": 5, + "links": [], + "options": { + "legend": { + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "span": 3, + "targets": [ + { + "expr": "(sum by (status) (\n label_replace(label_replace(rate(http_request_duration_seconds_count{service=~\"gateway\", route=~\"api_v1_write\"}[$__rate_interval]),\n \"status\", \"${1}xx\", \"status_code\", \"([0-9])..\"),\n \"status\", \"${1}\", \"status_code\", \"([a-zA-Z]+)\"))\n) and on() (vector($latency_metrics) == 1)", + "format": "time_series", + "legendFormat": "{{status}}", + "refId": "A_classic" + }, + { + "expr": "(sum by (status) (\n label_replace(label_replace(histogram_count(rate(http_request_duration_seconds{service=~\"gateway\", route=~\"api_v1_write\"}[$__rate_interval])),\n \"status\", \"${1}xx\", \"status_code\", \"([0-9])..\"),\n \"status\", \"${1}\", \"status_code\", \"([a-zA-Z]+)\"))\n) and on() (vector($latency_metrics) == -1)", + "format": "time_series", + "legendFormat": "{{status}}", + "refId": "A" + } + ], + "title": "Write requests / sec (gateway)", + "type": "timeseries" + }, + { + "datasource": "$datasource", + "fieldConfig": { + "defaults": { + "custom": { + "drawStyle": "line", + "fillOpacity": 1, + "lineWidth": 1, + "pointSize": 5, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + } + }, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [] + }, + "unit": "ms" + }, + "overrides": [] + }, + "id": 6, + "links": [], + "nullPointMode": "null as zero", + "options": { + "legend": { + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "span": 3, + "targets": [ + { + "expr": "(histogram_quantile(0.99, sum by (le) (service_route:http_request_duration_seconds_bucket:sum_rate{job=~\"(.*)((gateway|service-gw.*))\", route=~\"api_v1_write\"})) * 1e3) and on() (vector($latency_metrics) == 1)", + "format": "time_series", + "legendFormat": "99th percentile", + "refId": "A_classic" + }, + { + "expr": "(histogram_quantile(0.99, sum (service_route:http_request_duration_seconds:sum_rate{job=~\"(.*)((gateway|service-gw.*))\", route=~\"api_v1_write\"})) * 1e3) and on() (vector($latency_metrics) == -1)", + "format": "time_series", + "legendFormat": "99th percentile", + "refId": "A_native" + }, + { + "expr": "(histogram_quantile(0.50, sum by (le) (service_route:http_request_duration_seconds_bucket:sum_rate{job=~\"(.*)((gateway|service-gw.*))\", route=~\"api_v1_write\"})) * 1e3) and on() (vector($latency_metrics) == 1)", + "format": "time_series", + "legendFormat": "50th percentile", + "refId": "B_classic" + }, + { + "expr": "(histogram_quantile(0.50, sum (service_route:http_request_duration_seconds:sum_rate{job=~\"(.*)((gateway|service-gw.*))\", route=~\"api_v1_write\"})) * 1e3) and on() (vector($latency_metrics) == -1)", + "format": "time_series", + "legendFormat": "50th percentile", + "refId": "B_native" + }, + { + "expr": "(1e3 * sum(service_route:http_request_duration_seconds_sum:sum_rate{job=~\"(.*)((gateway|service-gw.*))\", route=~\"api_v1_write\"}) /\nsum(service_route:http_request_duration_seconds_count:sum_rate{job=~\"(.*)((gateway|service-gw.*))\", route=~\"api_v1_write\"})\n) and on() (vector($latency_metrics) == 1)", + "format": "time_series", + "legendFormat": "Average", + "refId": "C_classic" + }, + { + "expr": "(1e3 * sum(histogram_sum(service_route:http_request_duration_seconds:sum_rate{job=~\"(.*)((gateway|service-gw.*))\", route=~\"api_v1_write\"})) /\nsum(histogram_count(service_route:http_request_duration_seconds:sum_rate{job=~\"(.*)((gateway|service-gw.*))\", route=~\"api_v1_write\"}))\n) and on() (vector($latency_metrics) == -1)", + "format": "time_series", + "legendFormat": "Average", + "refId": "C_native" + } + ], + "title": "Write latency (gateway)", + "type": "timeseries" + }, + { + "datasource": "$datasource", + "fieldConfig": { + "defaults": { + "custom": { + "drawStyle": "line", + "fillOpacity": 100, + "lineWidth": 0, + "pointSize": 5, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "normal" + } + }, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [] + }, + "unit": "cps" + }, + "overrides": [] + }, + "id": 7, + "links": [], + "options": { + "legend": { + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "span": 3, + "targets": [ + { + "expr": "sum(cluster_namespace_job:ingestion_service_received_samples:rate5m{job=~\"(.*)/(distributor.*|service|metrics)\"})", + "format": "time_series", + "legendFormat": "samples / sec", + "legendLink": null + }, + { + "expr": "sum(cluster_namespace_job:ingestion_service_received_exemplars:rate5m{job=~\"(.*)/(distributor.*|service|metrics)\"})", + "format": "time_series", + "legendFormat": "exemplars / sec", + "legendLink": null + } + ], + "title": "Ingestion / sec", + "type": "timeseries" + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Writes", + "titleSize": "h6" + }, + { + "collapse": false, + "height": "250px", + "panels": [ + { + "content": "These panels show an overview on the read path. Requests rate and latency is measured on the gateway.\nTo examine the read path in detail, see a specific dashboard:\n\n- Reads\n- Reads resources\n- Reads networking\n- Overview resources\n- Overview networking\n- Queries\n- Compactor\n", + "datasource": null, + "description": "", + "id": 8, + "mode": "markdown", + "span": 3, + "title": "", + "transparent": true, + "type": "text" + }, + { + "datasource": "$datasource", + "fieldConfig": { + "defaults": { + "custom": { + "drawStyle": "line", + "fillOpacity": 100, + "lineWidth": 0, + "pointSize": 5, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "normal" + } + }, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [] + }, + "unit": "reqps" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "1xx" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#EAB839", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "2xx" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#7EB26D", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "3xx" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#6ED0E0", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "4xx" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#EF843C", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "5xx" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#E24D42", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "OK" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#7EB26D", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "cancel" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#A9A9A9", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "error" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#E24D42", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "success" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#7EB26D", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "1xx" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#EAB839", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "2xx" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#7EB26D", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "3xx" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#6ED0E0", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "4xx" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#EF843C", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "5xx" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#E24D42", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Canceled" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#A9A9A9", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "OK" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#7EB26D", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Success" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#7EB26D", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "cancel" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#A9A9A9", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "error" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#E24D42", + "mode": "fixed" + } + } + ] + } + ] + }, + "id": 9, + "links": [], + "options": { + "legend": { + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "span": 3, + "targets": [ + { + "expr": "(sum by (status) (\n label_replace(label_replace(rate(http_request_duration_seconds_count{service=~\"gateway\", route=~\"api_v1_query\"}[$__rate_interval]),\n \"status\", \"${1}xx\", \"status_code\", \"([0-9])..\"),\n \"status\", \"${1}\", \"status_code\", \"([a-zA-Z]+)\"))\n) and on() (vector($latency_metrics) == 1)", + "format": "time_series", + "legendFormat": "{{status}}", + "refId": "A_classic" + }, + { + "expr": "(sum by (status) (\n label_replace(label_replace(histogram_count(rate(http_request_duration_seconds{service=~\"gateway\", route=~\"api_v1_query\"}[$__rate_interval])),\n \"status\", \"${1}xx\", \"status_code\", \"([0-9])..\"),\n \"status\", \"${1}\", \"status_code\", \"([a-zA-Z]+)\"))\n) and on() (vector($latency_metrics) == -1)", + "format": "time_series", + "legendFormat": "{{status}}", + "refId": "A" + } + ], + "title": "Read requests / sec (gateway)", + "type": "timeseries" + }, + { + "datasource": "$datasource", + "fieldConfig": { + "defaults": { + "custom": { + "drawStyle": "line", + "fillOpacity": 1, + "lineWidth": 1, + "pointSize": 5, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + } + }, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [] + }, + "unit": "ms" + }, + "overrides": [] + }, + "id": 10, + "links": [], + "nullPointMode": "null as zero", + "options": { + "legend": { + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "span": 3, + "targets": [ + { + "expr": "(histogram_quantile(0.99, sum by (le) (service_route:http_request_duration_seconds_bucket:sum_rate{job=~\"(.*)((gateway|service-gw.*))\", route=~\"api_v1_query\"})) * 1e3) and on() (vector($latency_metrics) == 1)", + "format": "time_series", + "legendFormat": "99th percentile", + "refId": "A_classic" + }, + { + "expr": "(histogram_quantile(0.99, sum (service_route:http_request_duration_seconds:sum_rate{job=~\"(.*)((gateway|service-gw.*))\", route=~\"api_v1_query\"})) * 1e3) and on() (vector($latency_metrics) == -1)", + "format": "time_series", + "legendFormat": "99th percentile", + "refId": "A_native" + }, + { + "expr": "(histogram_quantile(0.50, sum by (le) (service_route:http_request_duration_seconds_bucket:sum_rate{job=~\"(.*)((gateway|service-gw.*))\", route=~\"api_v1_query\"})) * 1e3) and on() (vector($latency_metrics) == 1)", + "format": "time_series", + "legendFormat": "50th percentile", + "refId": "B_classic" + }, + { + "expr": "(histogram_quantile(0.50, sum (service_route:http_request_duration_seconds:sum_rate{job=~\"(.*)((gateway|service-gw.*))\", route=~\"api_v1_query\"})) * 1e3) and on() (vector($latency_metrics) == -1)", + "format": "time_series", + "legendFormat": "50th percentile", + "refId": "B_native" + }, + { + "expr": "(1e3 * sum(service_route:http_request_duration_seconds_sum:sum_rate{job=~\"(.*)((gateway|service-gw.*))\", route=~\"api_v1_query\"}) /\nsum(service_route:http_request_duration_seconds_count:sum_rate{job=~\"(.*)((gateway|service-gw.*))\", route=~\"api_v1_query\"})\n) and on() (vector($latency_metrics) == 1)", + "format": "time_series", + "legendFormat": "Average", + "refId": "C_classic" + }, + { + "expr": "(1e3 * sum(histogram_sum(service_route:http_request_duration_seconds:sum_rate{job=~\"(.*)((gateway|service-gw.*))\", route=~\"api_v1_query\"})) /\nsum(histogram_count(service_route:http_request_duration_seconds:sum_rate{job=~\"(.*)((gateway|service-gw.*))\", route=~\"api_v1_query\"}))\n) and on() (vector($latency_metrics) == -1)", + "format": "time_series", + "legendFormat": "Average", + "refId": "C_native" + } + ], + "title": "Read latency (gateway)", + "type": "timeseries" + }, + { + "datasource": "$datasource", + "fieldConfig": { + "defaults": { + "custom": { + "drawStyle": "line", + "fillOpacity": 100, + "lineWidth": 0, + "pointSize": 5, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "normal" + } + }, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [] + }, + "unit": "reqps" + }, + "overrides": [ + { + "matcher": { + "id": "byRegexp", + "options": "/.*_api_v1_query($|[^_])/" + }, + "properties": [ + { + "id": "displayName", + "value": "instant queries" + }, + { + "id": "color", + "value": { + "fixedColor": "#429D48", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byRegexp", + "options": "/.*_api_v1_query_range($|[^_])/" + }, + "properties": [ + { + "id": "displayName", + "value": "range queries" + }, + { + "id": "color", + "value": { + "fixedColor": "#F1C731", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byRegexp", + "options": "/.*_api_v1_labels($|[^_])/" + }, + "properties": [ + { + "id": "displayName", + "value": "\"label names\" queries" + }, + { + "id": "color", + "value": { + "fixedColor": "#2A66CF", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byRegexp", + "options": "/.*_api_v1_label_name_values($|[^_])/" + }, + "properties": [ + { + "id": "displayName", + "value": "\"label values\" queries" + }, + { + "id": "color", + "value": { + "fixedColor": "#9E44C1", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byRegexp", + "options": "/.*_api_v1_series($|[^_])/" + }, + "properties": [ + { + "id": "displayName", + "value": "series queries" + }, + { + "id": "color", + "value": { + "fixedColor": "#FFAB57", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byRegexp", + "options": "/.*_api_v1_read($|[^_])/" + }, + "properties": [ + { + "id": "displayName", + "value": "remote read queries" + }, + { + "id": "color", + "value": { + "fixedColor": "#C79424", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byRegexp", + "options": "/.*_api_v1_metadata($|[^_])/" + }, + "properties": [ + { + "id": "displayName", + "value": "metadata queries" + }, + { + "id": "color", + "value": { + "fixedColor": "#84D586", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byRegexp", + "options": "/.*_api_v1_query_exemplars($|[^_])/" + }, + "properties": [ + { + "id": "displayName", + "value": "exemplar queries" + }, + { + "id": "color", + "value": { + "fixedColor": "#A1C4FC", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byRegexp", + "options": "/.*_api_v1_cardinality_active_series($|[^_])/" + }, + "properties": [ + { + "id": "displayName", + "value": "\"active series\" queries" + }, + { + "id": "color", + "value": { + "fixedColor": "#C788DE", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byRegexp", + "options": "/.*_api_v1_cardinality_label_names($|[^_])/" + }, + "properties": [ + { + "id": "displayName", + "value": "\"label name cardinality\" queries" + }, + { + "id": "color", + "value": { + "fixedColor": "#3F6833", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byRegexp", + "options": "/.*_api_v1_cardinality_label_values($|[^_])/" + }, + "properties": [ + { + "id": "displayName", + "value": "\"label value cardinality\" queries" + }, + { + "id": "color", + "value": { + "fixedColor": "#447EBC", + "mode": "fixed" + } + } + ] + } + ] + }, + "id": 11, + "links": [], + "options": { + "legend": { + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "span": 3, + "targets": [ + { + "expr": "(sum by (route) (rate(http_request_duration_seconds_count{job=~\"(.*)/(query-frontend.*|service|metrics)\",route=~\"(prometheus|api_prom)(_api_v1_query|_api_v1_query_range|_api_v1_labels|_api_v1_label_name_values|_api_v1_series|_api_v1_read|_api_v1_metadata|_api_v1_query_exemplars|_api_v1_cardinality_active_series|_api_v1_cardinality_label_names|_api_v1_cardinality_label_values)\"}[$__rate_interval]))) and on() (vector($latency_metrics) == 1)", + "format": "time_series", + "legendLink": null + }, + { + "expr": "(sum by (route) (histogram_count(rate(http_request_duration_seconds{job=~\"(.*)/(query-frontend.*|service|metrics)\",route=~\"(prometheus|api_prom)(_api_v1_query|_api_v1_query_range|_api_v1_labels|_api_v1_label_name_values|_api_v1_series|_api_v1_read|_api_v1_metadata|_api_v1_query_exemplars|_api_v1_cardinality_active_series|_api_v1_cardinality_label_names|_api_v1_cardinality_label_values)\"}[$__rate_interval])))) and on() (vector($latency_metrics) == -1)", + "format": "time_series", + "legendLink": null + }, + { + "expr": "(sum (rate(http_request_duration_seconds_count{job=~\"(.*)/(query-frontend.*|service|metrics)\",route=~\"(prometheus|api_prom)_api_v1_.*\",route!~\"(prometheus|api_prom)(_api_v1_query|_api_v1_query_range|_api_v1_labels|_api_v1_label_name_values|_api_v1_series|_api_v1_read|_api_v1_metadata|_api_v1_query_exemplars|_api_v1_cardinality_active_series|_api_v1_cardinality_label_names|_api_v1_cardinality_label_values)\"}[$__rate_interval]))) and on() (vector($latency_metrics) == 1)", + "format": "time_series", + "legendFormat": "other", + "legendLink": null + }, + { + "expr": "(sum (histogram_count(rate(http_request_duration_seconds{job=~\"(.*)/(query-frontend.*|service|metrics)\",route=~\"(prometheus|api_prom)_api_v1_.*\",route!~\"(prometheus|api_prom)(_api_v1_query|_api_v1_query_range|_api_v1_labels|_api_v1_label_name_values|_api_v1_series|_api_v1_read|_api_v1_metadata|_api_v1_query_exemplars|_api_v1_cardinality_active_series|_api_v1_cardinality_label_names|_api_v1_cardinality_label_values)\"}[$__rate_interval])))) and on() (vector($latency_metrics) == -1)", + "format": "time_series", + "legendFormat": "other", + "legendLink": null + } + ], + "title": "Queries / sec", + "type": "timeseries" + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Reads", + "titleSize": "h6" + }, + { + "collapse": false, + "height": "250px", + "panels": [ + { + "content": "These panels show an overview on the recording and alerting rules evaluation.\nTo examine the rules evaluation and alerts notifications in detail, see a specific dashboard:\n\n- Ruler\n- Alertmanager\n- Alertmanager resources\n- Overview resources\n- Overview networking\n", + "datasource": null, + "description": "", + "id": 12, + "mode": "markdown", + "span": 3, + "title": "", + "transparent": true, + "type": "text" + }, + { + "datasource": "$datasource", + "fieldConfig": { + "defaults": { + "custom": { + "drawStyle": "line", + "fillOpacity": 1, + "lineWidth": 1, + "pointSize": 5, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + } + }, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [] + }, + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "failed" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#E24D42", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "success" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#7EB26D", + "mode": "fixed" + } + } + ] + } + ] + }, + "id": 13, + "links": [], + "options": { + "legend": { + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "span": 3, + "targets": [ + { + "expr": "sum(rate(monitoring_rule_evaluations_total{service=~\"rule-engine\"}[$__rate_interval]))\n-\nsum(rate(monitoring_rule_evaluation_failures_total{service=~\"rule-engine\"}[$__rate_interval]))\n", + "format": "time_series", + "legendFormat": "success", + "legendLink": null + }, + { + "expr": "sum(rate(monitoring_rule_evaluation_failures_total{service=~\"rule-engine\"}[$__rate_interval]))", + "format": "time_series", + "legendFormat": "failed", + "legendLink": null + }, + { + "expr": "sum(rate(monitoring_rule_group_iterations_missed_total{service=~\"rule-engine\"}[$__rate_interval]))", + "format": "time_series", + "legendFormat": "missed", + "legendLink": null + } + ], + "title": "Rule evaluations / sec", + "type": "timeseries" + }, + { + "datasource": "$datasource", + "fieldConfig": { + "defaults": { + "custom": { + "drawStyle": "line", + "fillOpacity": 1, + "lineWidth": 1, + "pointSize": 5, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + } + }, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [] + }, + "unit": "s" + }, + "overrides": [] + }, + "id": 14, + "links": [], + "options": { + "legend": { + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "span": 3, + "targets": [ + { + "expr": "sum (rate(monitoring_rule_evaluation_duration_seconds_sum{service=~\"rule-engine\"}[$__rate_interval]))\n /\nsum (rate(monitoring_rule_evaluation_duration_seconds_count{service=~\"rule-engine\"}[$__rate_interval]))\n", + "format": "time_series", + "legendFormat": "average", + "legendLink": null + } + ], + "title": "Rule evaluations latency", + "type": "timeseries" + }, + { + "datasource": "$datasource", + "fieldConfig": { + "defaults": { + "custom": { + "drawStyle": "line", + "fillOpacity": 100, + "lineWidth": 0, + "pointSize": 5, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "normal" + } + }, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [] + }, + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "failed" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#E24D42", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "successful" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#7EB26D", + "mode": "fixed" + } + } + ] + } + ] + }, + "id": 15, + "links": [], + "options": { + "legend": { + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "span": 3, + "targets": [ + { + "expr": "sum(rate(alert_notifications_sent_total{service=~\"rule-engine\"}[$__rate_interval]))\n -\nsum(rate(alert_notifications_errors_total{service=~\"rule-engine\"}[$__rate_interval]))\n", + "format": "time_series", + "legendFormat": "successful", + "legendLink": null + }, + { + "expr": "sum(rate(alert_notifications_errors_total{service=~\"rule-engine\"}[$__rate_interval]))\n", + "format": "time_series", + "legendFormat": "failed", + "legendLink": null + } + ], + "title": "Alerting notifications sent to Alertmanager / sec", + "type": "timeseries" + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Recording and alerting rules", + "titleSize": "h6" + }, + { + "collapse": false, + "height": "250px", + "panels": [ + { + "content": "These panels show an overview on the long-term storage (object storage).\nTo examine the storage in detail, see a specific dashboard:\n\n- Object store\n- Compactor\n", + "datasource": null, + "description": "", + "id": 16, + "mode": "markdown", + "span": 3, + "title": "", + "transparent": true, + "type": "text" + }, + { + "datasource": "$datasource", + "fieldConfig": { + "defaults": { + "custom": { + "drawStyle": "line", + "fillOpacity": 100, + "lineWidth": 0, + "pointSize": 5, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "normal" + } + }, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [] + }, + "unit": "reqps" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "failed" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#E24D42", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "successful" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#7EB26D", + "mode": "fixed" + } + } + ] + } + ] + }, + "id": 17, + "links": [], + "options": { + "legend": { + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "span": 3, + "targets": [ + { + "expr": "sum(rate(storage_operations_total{env!=\"\"}[$__rate_interval]))\n-\nsum(rate(storage_operation_failures_total{env!=\"\"}[$__rate_interval]))\n", + "format": "time_series", + "legendFormat": "successful", + "legendLink": null + }, + { + "expr": "sum(rate(storage_operation_failures_total{env!=\"\"}[$__rate_interval]))\n", + "format": "time_series", + "legendFormat": "failed", + "legendLink": null + } + ], + "title": "Requests / sec", + "type": "timeseries" + }, + { + "datasource": "$datasource", + "fieldConfig": { + "defaults": { + "custom": { + "drawStyle": "line", + "fillOpacity": 100, + "lineWidth": 0, + "pointSize": 5, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "normal" + } + }, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [] + }, + "unit": "reqps" + }, + "overrides": [] + }, + "id": 18, + "links": [], + "options": { + "legend": { + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "seriesOverrides": [ + { + "alias": "attributes", + "color": "#429D48" + }, + { + "alias": "delete", + "color": "#F1C731" + }, + { + "alias": "exists", + "color": "#2A66CF" + }, + { + "alias": "get", + "color": "#9E44C1" + }, + { + "alias": "get_range", + "color": "#FFAB57" + }, + { + "alias": "iter", + "color": "#C79424" + }, + { + "alias": "upload", + "color": "#84D586" + } + ], + "span": 3, + "targets": [ + { + "expr": "sum by(operation) (rate(storage_operations_total{env!=\"\"}[$__rate_interval]))", + "format": "time_series", + "legendFormat": "{{operation}}", + "legendLink": null + } + ], + "title": "Operations / sec", + "type": "timeseries" + }, + { + "datasource": "$datasource", + "fieldConfig": { + "defaults": { + "custom": { + "drawStyle": "line", + "fillOpacity": 1, + "lineWidth": 1, + "pointSize": 5, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + } + }, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [] + }, + "unit": "short" + }, + "overrides": [] + }, + "id": 19, + "links": [], + "options": { + "legend": { + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "span": 3, + "targets": [ + { + "expr": "sum(max by(user) (max_over_time(storage_blocks_count{job=~\"(.*)/(compactor.*|service|metrics)\"}[15m])))", + "format": "time_series", + "legendFormat": "blocks", + "legendLink": null + } + ], + "title": "Total number of blocks in the storage", + "type": "timeseries" + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Long-term storage (object storage)", + "titleSize": "h6" + } + ], + "schemaVersion": 14, + "style": "dark", + "tags": ["metrics", "scoped", "as-code"], + "templating": { + "list": [ + { + "name": "datasource", + "options": [], + "query": "prometheus", + "refresh": 1, + "regex": "/(dev-prometheus|ops-prometheus|service-prometheus|critical-prometheus)/", + "type": "datasource" + }, + { + "current": { + "selected": true, + "text": "classic", + "value": "1" + }, + "description": "Choose between showing latencies based on low precision classic or high precision native histogram metrics.", + "hide": 0, + "includeAll": false, + "label": "Latency metrics", + "multi": false, + "name": "latency_metrics", + "options": [ + { + "selected": false, + "text": "native", + "value": "-1" + }, + { + "selected": true, + "text": "classic", + "value": "1" + } + ], + "query": "native : -1,classic : 1", + "skipUrlSync": false, + "type": "custom", + "useTags": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "filters": [], + "name": "filters", + "type": "adhoc" + } + ] + }, + "time": { + "from": "now-1h", + "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": "Service Overview Dashboard", + "uid": "e540f8b0ce5b02335ec443a769d1a74e", + "version": 0 +} diff --git a/apps/dashboard/pkg/migration/testdata/output/v14.broken-dash-min.json b/apps/dashboard/pkg/migration/testdata/output/v14.broken-dash-min.json new file mode 100644 index 00000000000..216889cd4a1 --- /dev/null +++ b/apps/dashboard/pkg/migration/testdata/output/v14.broken-dash-min.json @@ -0,0 +1,159 @@ +{ + "annotations": { + "list": [] + }, + "editable": false, + "gnetId": null, + "graphTooltip": 1, + "hideControls": false, + "id": 22815, + "links": [], + "panels": [ + { + "datasource": { + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "links": [], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + } + ] + }, + "unit": "none" + } + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 1 + }, + "id": 2, + "links": [], + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "mean" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "7", + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum(rate(http_requests_total{environment=\"$env\", region=\"$region\", service=\"webapp\"}[5m]))", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Requests/sec", + "refId": "A" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "avg(memory_usage_bytes{environment=\"$env\", region=\"$region\", service=\"webapp\"}) / 1024 / 1024", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Memory (MB)", + "refId": "B" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "histogram_quantile(0.95, rate(response_time_bucket{environment=\"$env\", region=\"$region\", service=\"webapp\"}[5m]))", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "95th percentile latency", + "refId": "C" + } + ], + "title": "Service Stats", + "transparent": false, + "type": "stat" + }, + { + "collapsed": false, + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "gridPos": { + "h": 7, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 3, + "panels": [], + "repeat": "", + "targets": [ + { + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "refId": "A" + } + ], + "title": "Overview", + "type": "row" + } + ], + "refresh": "1m", + "schemaVersion": 41, + "style": "dark", + "tags": [ + "example-service", + "as-code" + ], + "templating": { + "list": [] + }, + "time": { + "from": "now-2h", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ] + }, + "timezone": "browser", + "title": "Service Overview Dashboard", + "uid": "ba9b93e998e09d254266b11192894a8d", + "version": 1 +} \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/testdata/output/v14.broken-dash.json b/apps/dashboard/pkg/migration/testdata/output/v14.broken-dash.json new file mode 100644 index 00000000000..ed2a893cbeb --- /dev/null +++ b/apps/dashboard/pkg/migration/testdata/output/v14.broken-dash.json @@ -0,0 +1,3989 @@ +{ + "annotations": { + "list": [ + { + "datasource": { + "uid": "$datasourcelogs" + }, + "enable": true, + "expr": "{cluster=\"$cluster\", component=\"logger\"} | json | namespace_extracted=~\"example-service.*\" | name_extracted=\"example-api\"", + "iconColor": "rgba(0, 211, 255, 1)", + "instant": false, + "name": "Deployments", + "titleFormat": "{{cluster}}/{{namespace}}" + } + ] + }, + "editable": false, + "gnetId": null, + "graphTooltip": 1, + "hideControls": false, + "id": 22815, + "links": [ + { + "asDropdown": true, + "icon": "external link", + "includeVars": true, + "keepTime": true, + "tags": [ + "cache-overview" + ], + "targetBlank": false, + "title": "Cache Dashboard", + "type": "dashboards", + "url": "" + }, + { + "asDropdown": true, + "icon": "external link", + "includeVars": true, + "keepTime": true, + "tags": [ + "database-monitoring" + ], + "targetBlank": false, + "title": "Database Dashboard", + "type": "dashboards", + "url": "" + }, + { + "asDropdown": true, + "icon": "external link", + "includeVars": true, + "keepTime": true, + "tags": [ + "Database", + "monitoring" + ], + "targetBlank": false, + "title": "Database Metrics Dashboard", + "type": "dashboards", + "url": "" + } + ], + "panels": [ + { + "datasource": { + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "links": [], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + } + ] + }, + "unit": "none" + } + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 1 + }, + "id": 2, + "links": [], + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "mean" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "7", + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum(app_metric_counter_a{env=\"$environment\", region=\"$region\", service=\"api\"}) - 1", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Tenants", + "refId": "A" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum(app_metric_counter_b{env=\"$environment\", region=\"$region\", service=\"api\"}) - 1", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Collectors", + "refId": "B" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum(app_metric_counter_c{env=\"$environment\", region=\"$region\", service=\"api\"}) - 1", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Pipelines", + "refId": "C" + } + ], + "title": "Customer Stats", + "transparent": false, + "type": "stat" + }, + { + "datasource": { + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "links": [], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "blue", + "value": 0 + }, + { + "color": "green", + "value": 40 + }, + { + "color": "yellow", + "value": 60 + }, + { + "color": "orange", + "value": 80 + }, + { + "color": "red", + "value": 100 + } + ] + }, + "unit": "percent" + } + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 1 + }, + "id": 3, + "links": [], + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "mean" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "7", + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "avg(sum by (pod) (app_memory_usage_bytes{env=\"$environment\", region=\"$region\", instance=~\"example-api.*\"}) * 100) / min(app_resource_quota{env=\"$environment\", region=\"$region\", resource=\"memory\", instance=~\"example-api.*\"})", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "API Mem", + "refId": "A" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "avg(sum by (pod) (app_memory_usage_bytes{env=\"$environment\", region=\"$region\", instance=~\"cache.*\", component=\"cache-server\"}) * 100) / min(app_resource_quota{env=\"$environment\", region=\"$region\", resource=\"memory\", instance=~\"cache.*\", component=\"cache-server\"})", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Cache Server Mem", + "refId": "B" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "avg(sum by (pod) (app_memory_usage_bytes{env=\"$environment\", region=\"$region\", instance=~\"gateway.*\"}) * 100) / min(app_resource_quota{env=\"$environment\", region=\"$region\", resource=\"memory\", instance=~\"gateway.*\"})", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Gateway Mem ", + "refId": "C" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "avg(sum by (pod) (rate(app_cpu_usage_ratio{env=\"$environment\", region=\"$region\", instance=~\"example-api.*\"}[1m]))) * 100 / min(app_resource_quota{env=\"$environment\", region=\"$region\", resource=\"cpu\", instance=~\"example-api.*\"})", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "API CPU", + "refId": "D" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "avg(sum by (pod) (rate(app_cpu_usage_ratio{env=\"$environment\", region=\"$region\", instance=~\"cache.*\", component=\"cache-server\"}[1m]))) * 100 / min(app_resource_quota{env=\"$environment\", region=\"$region\", resource=\"cpu\", instance=~\"cache.*\", component=\"cache-server\"})", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Cache Server CPU", + "refId": "E" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "avg(sum by (pod) (rate(app_cpu_usage_ratio{env=\"$environment\", region=\"$region\", instance=~\"gateway.*\"}[1m]))) * 100 / min(app_resource_quota{env=\"$environment\", region=\"$region\", resource=\"cpu\", instance=~\"gateway.*\"})", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Gateway CPU", + "refId": "F" + } + ], + "title": "Requested Resource Usage", + "transparent": false, + "type": "stat" + }, + { + "datasource": { + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "links": [], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + } + ] + }, + "unit": "reqps" + } + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 1 + }, + "id": 4, + "links": [], + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "mean" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "7", + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum by () (\n label_replace(\n rate(http_request_duration_seconds_count{env=\"$environment\", region=\"$region\", service=\"api\", route!~\"health|status\"}[$__rate_interval]),\n \"status\",\n \"${1}xx\",\n \"\",\n \"([0-9])..\")\n )\n", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{}}", + "refId": "A" + } + ], + "title": "Api Requests", + "transparent": false, + "type": "stat" + }, + { + "collapsed": false, + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "gridPos": { + "h": 7, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 39, + "panels": [], + "repeat": "", + "targets": [ + { + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "refId": "A" + } + ], + "title": "Overview", + "type": "row" + }, + { + "aliasColors": { + "Limit": "#E24D42", + "Requested": "#Ffa500" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "fill": 0, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 9 + }, + "id": 5, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum by (pod) (app_memory_usage_bytes{env=\"$environment\", region=\"$region\", instance=~\"example-api.*\", component=~\".*\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{pod}}", + "refId": "A" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "min(app_resource_quota{env=\"$environment\", region=\"$region\", resource=\"memory\", instance=~\"example-api.*\", component=~\".*\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Requested", + "refId": "B" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "min(app_resource_limit{env=\"$environment\", region=\"$region\", resource=\"memory\", instance=~\"example-api.*\", component=~\".*\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Limit", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Memory Usage", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": { + "Limit": "#E24D42", + "Requested": "#Ffa500" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "fill": 0, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 9 + }, + "id": 6, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum by (pod) (rate(app_cpu_usage_ratio{env=\"$environment\", region=\"$region\", instance=~\"example-api.*\", component=~\".*\"}[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{pod}}", + "refId": "A" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "min(app_resource_quota{env=\"$environment\", region=\"$region\", resource=\"cpu\", instance=~\"example-api.*\", component=~\".*\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Requested", + "refId": "B" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "min(app_resource_limit{env=\"$environment\", region=\"$region\", resource=\"cpu\", instance=~\"example-api.*\", component=~\".*\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Limit", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "CPU Usage", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": { + "1xx": "#EAB839", + "2xx": "#7EB26D", + "3xx": "#6ED0E0", + "4xx": "#EF843C", + "5xx": "#E24D42" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "decimals": 2, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 16 + }, + "id": 7, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum by (status_code) (\n label_replace(\n rate(http_request_duration_seconds_count{env=\"$environment\", region=\"$region\", service=\"api\", route!~\"health|status\"}[$__rate_interval]),\n \"status\",\n \"${1}xx\",\n \"status_code\",\n \"([0-9])..\")\n )\n", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{status_code}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Throughput (requests/sec)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "decimals": 2, + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "decimals": 2, + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 16 + }, + "id": 8, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "histogram_quantile(0.99, sum(rate(http_request_duration_seconds_bucket{env=\"$environment\", region=\"$region\", service=\"api\", route!~\"health|status\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "p99", + "refId": "A" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "histogram_quantile(0.90, sum(rate(http_request_duration_seconds_bucket{env=\"$environment\", region=\"$region\", service=\"api\", route!~\"health|status\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "p90", + "refId": "B" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "histogram_quantile(0.50, sum(rate(http_request_duration_seconds_bucket{env=\"$environment\", region=\"$region\", service=\"api\", route!~\"health|status\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "p50", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Latency", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 16 + }, + "id": 9, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "(rate(http_request_duration_seconds_count{env=\"$environment\", region=\"$region\", service=\"api\", route!~\"health|status\", status_code=~\"5..\"}[$__rate_interval]) / rate(http_request_duration_seconds_count{env=\"$environment\", region=\"$region\", service=\"api\", route!~\"health|status\"}[$__rate_interval])) OR on() vector(0)", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Error rate", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Error rate", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": 1, + "min": 0, + "show": true + }, + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": 1, + "min": 0, + "show": true + } + ] + }, + { + "datasource": { + "uid": "$datasourcelogs" + }, + "gridPos": { + "h": 7, + "w": 24, + "x": 0, + "y": 23 + }, + "id": 10, + "options": { + "showLabels": false, + "showTime": true, + "sortOrder": "Descending", + "wrapLogMessage": true + }, + "targets": [ + { + "datasource": { + "uid": "$datasourcelogs" + }, + "expr": "{env=\"$environment\", region=\"$region\", service=\"api\"}", + "legendFormat": "", + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Logs", + "type": "logs" + }, + { + "datasource": { + "uid": "$datasourcelogs" + }, + "gridPos": { + "h": 7, + "w": 24, + "x": 0, + "y": 30 + }, + "id": 11, + "options": { + "showLabels": false, + "showTime": true, + "sortOrder": "Descending", + "wrapLogMessage": true + }, + "targets": [ + { + "datasource": { + "uid": "$datasourcelogs" + }, + "expr": "{env=\"$environment\", region=\"$region\", service=\"api\"} | logfmt | level=\"error\"", + "legendFormat": "", + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Errors", + "type": "logs" + }, + { + "collapsed": false, + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "gridPos": { + "h": 7, + "w": 24, + "x": 0, + "y": 8 + }, + "id": 40, + "panels": [], + "repeat": "", + "targets": [ + { + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "refId": "A" + } + ], + "title": "API", + "type": "row" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 38 + }, + "id": 12, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "histogram_quantile(0.99, sum(rate(database_query_duration_seconds_bucket{env=\"$environment\", region=\"$region\", service=\"api\"}[$__rate_interval])) by (le, method)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{method}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Client Latency p99", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 38 + }, + "id": 13, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "histogram_quantile(0.90, sum(rate(database_query_duration_seconds_bucket{env=\"$environment\", region=\"$region\", service=\"api\"}[$__rate_interval])) by (le, method)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{method}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Client Latency p90", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 38 + }, + "id": 14, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "histogram_quantile(0.50, sum(rate(database_query_duration_seconds_bucket{env=\"$environment\", region=\"$region\", service=\"api\"}[$__rate_interval])) by (le, method)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{method}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Client Latency p50", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 45 + }, + "id": 15, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum(rate(database_query_duration_seconds_count{env=\"$environment\", region=\"$region\", service=\"api\"}[1m]))", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Total", + "refId": "A" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum(rate(database_query_duration_seconds_count{env=\"$environment\", region=\"$region\", service=\"api\", method=~\"GetConfig.*\"}[1m]))", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "GetConfig", + "refId": "B" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum(rate(database_query_duration_seconds_count{env=\"$environment\", region=\"$region\", service=\"api\", method!~\"GetConfig.*\"}[1m]))", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Other", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Throughput (requests/sec)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "fill": 0, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 45 + }, + "id": 16, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum by (pod) (db_pool_in_use_connections {env=\"$environment\", region=\"$region\", service=\"api\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{pod}}/Active", + "refId": "A" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum by (pod) (db_pool_idle_connections {env=\"$environment\", region=\"$region\", service=\"api\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{pod}}/Idle", + "refId": "B" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum by (pod) (db_pool_max_open_connections {env=\"$environment\", region=\"$region\", service=\"api\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{pod}}/Max", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Connections", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "fill": 0, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 45 + }, + "id": 17, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum by (pod) (rate(db_pool_wait_duration_seconds_total{env=\"$environment\", region=\"$region\", service=\"api\"}[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{pod}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Connection Wait Duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "collapsed": false, + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "gridPos": { + "h": 7, + "w": 24, + "x": 0, + "y": 37 + }, + "id": 41, + "panels": [], + "repeat": "", + "targets": [ + { + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "refId": "A" + } + ], + "title": "Database", + "type": "row" + }, + { + "aliasColors": { + "Limit": "#E24D42", + "Requested": "#Ffa500" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "fill": 0, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 53 + }, + "id": 18, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum by (pod) (app_memory_usage_bytes{env=\"$environment\", region=\"$region\", instance=~\"cache.*\", component=~\"cache-server\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{pod}}", + "refId": "A" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "min(app_resource_quota{env=\"$environment\", region=\"$region\", resource=\"memory\", instance=~\"cache.*\", component=~\"cache-server\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Requested", + "refId": "B" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "min(app_resource_limit{env=\"$environment\", region=\"$region\", resource=\"memory\", instance=~\"cache.*\", component=~\"cache-server\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Limit", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Memory Usage", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": { + "Limit": "#E24D42", + "Requested": "#Ffa500" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "fill": 0, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 53 + }, + "id": 19, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum by (pod) (rate(app_cpu_usage_ratio{env=\"$environment\", region=\"$region\", instance=~\"cache.*\", component=~\"cache-server\"}[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{pod}}", + "refId": "A" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "min(app_resource_quota{env=\"$environment\", region=\"$region\", resource=\"cpu\", instance=~\"cache.*\", component=~\"cache-server\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Requested", + "refId": "B" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "min(app_resource_limit{env=\"$environment\", region=\"$region\", resource=\"cpu\", instance=~\"cache.*\", component=~\"cache-server\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Limit", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "CPU Usage", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 60 + }, + "id": 20, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum by (pod) (rate(cache_set_total{env=\"$environment\", region=\"$region\", service=\"api\"}[1m]))", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{pod}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Client Sets", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 60 + }, + "id": 21, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum by (pod) (rate(cache_request_total{env=\"$environment\", region=\"$region\", service=\"api\"}[1m]))", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{pod}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Client Requests", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 60 + }, + "id": 22, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum by (pod) (rate(cache_request_hit_total{env=\"$environment\", region=\"$region\", service=\"api\"}[$__rate_interval]) / rate(cache_request_total{env=\"$environment\", region=\"$region\", service=\"api\"}[$__rate_interval]))", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{pod}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Client Cache Request Hit Rate", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "fill": 0, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 67 + }, + "id": 23, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "histogram_quantile(0.99, sum(rate(cache_request_duration_seconds_bucket{env=\"$environment\", region=\"$region\", service=\"api\"}[$__rate_interval])) by (le, method)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{method}}/request", + "refId": "A" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "histogram_quantile(0.99, sum(rate(cache_set_duration_seconds_bucket{env=\"$environment\", region=\"$region\", service=\"api\"}[$__rate_interval])) by (le, method)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{method}}/set", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Client Latency p99", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "fill": 0, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 67 + }, + "id": 24, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "histogram_quantile(0.90, sum(rate(cache_request_duration_seconds_bucket{env=\"$environment\", region=\"$region\", service=\"api\"}[$__rate_interval])) by (le, method)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{method}}/request", + "refId": "A" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "histogram_quantile(0.90, sum(rate(cache_set_duration_seconds_bucket{env=\"$environment\", region=\"$region\", service=\"api\"}[$__rate_interval])) by (le, method)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{method}}/set", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Client Latency p90", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "fill": 0, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 67 + }, + "id": 25, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "histogram_quantile(0.50, sum(rate(cache_request_duration_seconds_bucket{env=\"$environment\", region=\"$region\", service=\"api\"}[$__rate_interval])) by (le, method)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{method}}/request", + "refId": "A" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "histogram_quantile(0.50, sum(rate(cache_set_duration_seconds_bucket{env=\"$environment\", region=\"$region\", service=\"api\"}[$__rate_interval])) by (le, method)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{method}}/set", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Client Latency p50", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 74 + }, + "id": 26, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum(rate(cache_request_duration_seconds_count{env=\"$environment\", region=\"$region\", service=\"api\"}[1m]))", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Total", + "refId": "A" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum(rate(cache_request_duration_seconds_count{env=\"$environment\", region=\"$region\", service=\"api\", method=~\".*Config.*\"}[1m]))", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Config", + "refId": "B" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum(rate(cache_request_duration_seconds_count{env=\"$environment\", region=\"$region\", service=\"api\", method!~\".*Config.*\"}[1m]))", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Other", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Request Throughput (requests/sec)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 74 + }, + "id": 27, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum(rate(cache_set_duration_seconds_count{env=\"$environment\", region=\"$region\", service=\"api\"}[1m]))", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Total", + "refId": "A" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum(rate(cache_set_duration_seconds_count{env=\"$environment\", region=\"$region\", service=\"api\", method=~\".*Config.*\"}[1m]))", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Config", + "refId": "B" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum(rate(cache_set_duration_seconds_count{env=\"$environment\", region=\"$region\", service=\"api\", method!~\".*Config.*\"}[1m]))", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Other", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Set Throughput (requests/sec)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "collapsed": false, + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "gridPos": { + "h": 7, + "w": 24, + "x": 0, + "y": 52 + }, + "id": 42, + "panels": [], + "repeat": "", + "targets": [ + { + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "refId": "A" + } + ], + "title": "Cache", + "type": "row" + }, + { + "aliasColors": { + "Limit": "#E24D42", + "Requested": "#Ffa500" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "fill": 0, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 82 + }, + "id": 28, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum by (pod) (app_memory_usage_bytes{env=\"$environment\", region=\"$region\", instance=~\"gateway.*\", component=~\".*\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{pod}}", + "refId": "A" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "min(app_resource_quota{env=\"$environment\", region=\"$region\", resource=\"memory\", instance=~\"gateway.*\", component=~\".*\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Requested", + "refId": "B" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "min(app_resource_limit{env=\"$environment\", region=\"$region\", resource=\"memory\", instance=~\"gateway.*\", component=~\".*\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Limit", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Memory Usage", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": { + "Limit": "#E24D42", + "Requested": "#Ffa500" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "fill": 0, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 82 + }, + "id": 29, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum by (pod) (rate(app_cpu_usage_ratio{env=\"$environment\", region=\"$region\", instance=~\"gateway.*\", component=~\".*\"}[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{pod}}", + "refId": "A" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "min(app_resource_quota{env=\"$environment\", region=\"$region\", resource=\"cpu\", instance=~\"gateway.*\", component=~\".*\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Requested", + "refId": "B" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "min(app_resource_limit{env=\"$environment\", region=\"$region\", resource=\"cpu\", instance=~\"gateway.*\", component=~\".*\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Limit", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "CPU Usage", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 89 + }, + "id": 30, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "histogram_quantile(0.99, sum(rate(agent_management_gateway_grafanacloud_authenticator_authentication_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "p99", + "refId": "A" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "histogram_quantile(0.90, sum(rate(agent_management_gateway_grafanacloud_authenticator_authentication_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "p90", + "refId": "B" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "histogram_quantile(0.50, sum(rate(agent_management_gateway_grafanacloud_authenticator_authentication_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "p50", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Authenticator latency", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 89 + }, + "id": 31, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "histogram_quantile(0.99, sum(rate(agent_management_gateway_grafanacloud_cachedclient_get_instance_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "p99", + "refId": "A" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "histogram_quantile(0.95, sum(rate(agent_management_gateway_grafanacloud_cachedclient_get_instance_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "p95", + "refId": "B" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "histogram_quantile(0.9, sum(rate(agent_management_gateway_grafanacloud_cachedclient_get_instance_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "p90", + "refId": "C" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "histogram_quantile(0.5, sum(rate(agent_management_gateway_grafanacloud_cachedclient_get_instance_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "p50", + "refId": "D" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "GCOM cached client latency", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 89 + }, + "id": 32, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "histogram_quantile(0.99, sum(rate(agent_management_request_downstream_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "p99", + "refId": "A" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "histogram_quantile(0.90, sum(rate(agent_management_request_downstream_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "p90", + "refId": "B" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "histogram_quantile(0.50, sum(rate(agent_management_request_downstream_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "p50", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Handler latency", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": { + "1xx": "#EAB839", + "2xx": "#7EB26D", + "3xx": "#6ED0E0", + "4xx": "#EF843C", + "5xx": "#E24D42" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "decimals": 2, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 96 + }, + "id": 33, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum by (code) (\n label_replace(\n rate(grafanacom_api_request_duration_seconds_count{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval]),\n \"status\",\n \"${1}xx\",\n \"code\",\n \"([0-9])..\")\n )\n", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{code}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Grafana.com HTTP Client - Throughput (requests/sec)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "decimals": 2, + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "decimals": 2, + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 96 + }, + "id": 34, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "histogram_quantile(0.99, sum(rate(grafanacom_api_request_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "5m", + "intervalFactor": 2, + "legendFormat": "p99", + "refId": "A" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "histogram_quantile(0.90, sum(rate(grafanacom_api_request_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "5m", + "intervalFactor": 2, + "legendFormat": "p90", + "refId": "B" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "histogram_quantile(0.50, sum(rate(grafanacom_api_request_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "5m", + "intervalFactor": 2, + "legendFormat": "p50", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Grafana.com HTTP Client - Latency", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 96 + }, + "id": 35, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "(rate(grafanacom_api_request_duration_seconds_count{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\", code=~\"5..|error\"}[$__rate_interval]) / rate(grafanacom_api_request_duration_seconds_count{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) OR on() vector(0)", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Error rate", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Grafana.com HTTP Client - Error rate", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": 1, + "min": 0, + "show": true + }, + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": 1, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": { + "1xx": "#EAB839", + "2xx": "#7EB26D", + "3xx": "#6ED0E0", + "4xx": "#EF843C", + "5xx": "#E24D42" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "decimals": 2, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 103 + }, + "id": 36, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum by (status_code) (\n label_replace(\n rate(auth_client_request_duration_seconds_count{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval]),\n \"status\",\n \"${1}xx\",\n \"status_code\",\n \"([0-9])..\")\n )\n", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{status_code}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Auth API HTTP Client - Throughput (requests/sec)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "decimals": 2, + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "decimals": 2, + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 103 + }, + "id": 37, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "histogram_quantile(0.99, sum(rate(auth_client_request_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "5m", + "intervalFactor": 2, + "legendFormat": "p99", + "refId": "A" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "histogram_quantile(0.90, sum(rate(auth_client_request_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "5m", + "intervalFactor": 2, + "legendFormat": "p90", + "refId": "B" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "histogram_quantile(0.50, sum(rate(auth_client_request_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "5m", + "intervalFactor": 2, + "legendFormat": "p50", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Auth API HTTP Client - Latency", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 103 + }, + "id": 38, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "(rate(auth_client_request_duration_seconds_count{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\", status_code=~\"5..|error\"}[$__rate_interval]) / rate(auth_client_request_duration_seconds_count{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) OR on() vector(0)", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Error rate", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Auth API HTTP Client - Error rate", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": 1, + "min": 0, + "show": true + }, + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": 1, + "min": 0, + "show": true + } + ] + }, + { + "collapsed": false, + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "gridPos": { + "h": 7, + "w": 24, + "x": 0, + "y": 81 + }, + "id": 43, + "panels": [], + "repeat": "", + "targets": [ + { + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "refId": "A" + } + ], + "title": "Gateway", + "type": "row" + } + ], + "refresh": "1m", + "schemaVersion": 41, + "style": "dark", + "tags": [ + "example-service", + "as-code" + ], + "templating": { + "list": [ + { + "current": { + "value": "prometheus-datasource" + }, + "hide": 0, + "label": null, + "name": "datasource", + "options": [], + "query": "prometheus", + "refresh": 1, + "regex": "", + "type": "datasource" + }, + { + "current": { + "value": "logs-datasource" + }, + "hide": 0, + "label": null, + "name": "datasourcelogs", + "options": [], + "query": "loki", + "refresh": 1, + "regex": "", + "type": "datasource" + }, + { + "allValue": null, + "current": { + "text": "cluster-1", + "value": "cluster-1" + }, + "datasource": { + "uid": "$datasource" + }, + "hide": 0, + "includeAll": false, + "label": null, + "multi": false, + "name": "cluster", + "options": [], + "query": "label_values(example_service_inflight_requests{}, cluster)", + "refresh": 1, + "regex": "", + "sort": 0, + "type": "query" + }, + { + "allValue": null, + "current": { + "text": "cluster-1", + "value": "cluster-1" + }, + "datasource": { + "uid": "$datasource" + }, + "hide": 0, + "includeAll": false, + "label": null, + "multi": false, + "name": "namespace", + "options": [], + "query": "label_values(example_service_inflight_requests{cluster=\"$cluster\"}, namespace)", + "refresh": 1, + "regex": "example-service.*", + "sort": 0, + "type": "query" + } + ] + }, + "time": { + "from": "now-2h", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ] + }, + "timezone": "browser", + "title": "Service Overview Dashboard", + "uid": "ba9b93e998e09d254266b11192894a8d", + "version": 1 +} \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/testdata/output/v14.broken-dash1.json b/apps/dashboard/pkg/migration/testdata/output/v14.broken-dash1.json new file mode 100644 index 00000000000..d87dbb38e61 --- /dev/null +++ b/apps/dashboard/pkg/migration/testdata/output/v14.broken-dash1.json @@ -0,0 +1,3991 @@ +{ + "__inputs": [], + "__requires": [], + "annotations": { + "list": [ + { + "datasource": { + "uid": "$datasourcelogs" + }, + "enable": true, + "expr": "{cluster=\"$cluster\", component=\"kube-diff-logger\"} | json | namespace_extracted=~\"example-service.*\" | name_extracted=\"example-api\"", + "iconColor": "rgba(0, 211, 255, 1)", + "instant": false, + "name": "Deployments", + "titleFormat": "{{cluster}}/{{namespace}}" + } + ] + }, + "editable": false, + "gnetId": null, + "graphTooltip": 1, + "hideControls": false, + "id": null, + "links": [ + { + "asDropdown": true, + "icon": "external link", + "includeVars": true, + "keepTime": true, + "tags": [ + "cache-overview" + ], + "targetBlank": false, + "title": "Cache Dashboard", + "type": "dashboards", + "url": "" + }, + { + "asDropdown": true, + "icon": "external link", + "includeVars": true, + "keepTime": true, + "tags": [ + "cloudsql-stackdriver" + ], + "targetBlank": false, + "title": "CloudSQL Dashboard", + "type": "dashboards", + "url": "" + }, + { + "asDropdown": true, + "icon": "external link", + "includeVars": true, + "keepTime": true, + "tags": [ + "Database", + "percona" + ], + "targetBlank": false, + "title": "Database Dashboard", + "type": "dashboards", + "url": "" + } + ], + "panels": [ + { + "datasource": { + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "links": [], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + } + ] + }, + "unit": "none" + } + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 1 + }, + "id": 2, + "links": [], + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "mean" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "7", + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum(app_metric_counter_a{env=\"$environment\", region=\"$region\", service=\"api\"}) - 1", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Tenants", + "refId": "A" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum(app_metric_counter_b{env=\"$environment\", region=\"$region\", service=\"api\"}) - 1", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Collectors", + "refId": "B" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum(app_metric_counter_c{env=\"$environment\", region=\"$region\", service=\"api\"}) - 1", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Pipelines", + "refId": "C" + } + ], + "title": "Customer Stats", + "transparent": false, + "type": "stat" + }, + { + "datasource": { + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "links": [], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "blue", + "value": 0 + }, + { + "color": "green", + "value": 40 + }, + { + "color": "yellow", + "value": 60 + }, + { + "color": "orange", + "value": 80 + }, + { + "color": "red", + "value": 100 + } + ] + }, + "unit": "percent" + } + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 1 + }, + "id": 3, + "links": [], + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "mean" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "7", + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "avg(sum by (pod) (app_memory_usage_bytes{env=\"$environment\", region=\"$region\", instance=~\"example-api.*\"}) * 100) / min(app_resource_quota{env=\"$environment\", region=\"$region\", resource=\"memory\", instance=~\"example-api.*\"})", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "API Mem", + "refId": "A" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "avg(sum by (pod) (app_memory_usage_bytes{env=\"$environment\", region=\"$region\", instance=~\"cache.*\", component=\"cache-server\"}) * 100) / min(app_resource_quota{env=\"$environment\", region=\"$region\", resource=\"memory\", instance=~\"cache.*\", component=\"cache-server\"})", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Cache Server Mem", + "refId": "B" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "avg(sum by (pod) (app_memory_usage_bytes{env=\"$environment\", region=\"$region\", instance=~\"gateway.*\"}) * 100) / min(app_resource_quota{env=\"$environment\", region=\"$region\", resource=\"memory\", instance=~\"gateway.*\"})", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Gateway Mem ", + "refId": "C" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "avg(sum by (pod) (rate(app_cpu_usage_ratio{env=\"$environment\", region=\"$region\", instance=~\"example-api.*\"}[1m]))) * 100 / min(app_resource_quota{env=\"$environment\", region=\"$region\", resource=\"cpu\", instance=~\"example-api.*\"})", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "API CPU", + "refId": "D" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "avg(sum by (pod) (rate(app_cpu_usage_ratio{env=\"$environment\", region=\"$region\", instance=~\"cache.*\", component=\"cache-server\"}[1m]))) * 100 / min(app_resource_quota{env=\"$environment\", region=\"$region\", resource=\"cpu\", instance=~\"cache.*\", component=\"cache-server\"})", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Cache Server CPU", + "refId": "E" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "avg(sum by (pod) (rate(app_cpu_usage_ratio{env=\"$environment\", region=\"$region\", instance=~\"gateway.*\"}[1m]))) * 100 / min(app_resource_quota{env=\"$environment\", region=\"$region\", resource=\"cpu\", instance=~\"gateway.*\"})", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Gateway CPU", + "refId": "F" + } + ], + "title": "Requested Resource Usage", + "transparent": false, + "type": "stat" + }, + { + "datasource": { + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "links": [], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + } + ] + }, + "unit": "reqps" + } + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 1 + }, + "id": 4, + "links": [], + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "mean" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "7", + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum by () (\n label_replace(\n rate(example_service_request_duration_seconds_count{env=\"$environment\", region=\"$region\", service=\"api\", route!~\"|root|other|ready|metrics|debug_pprof\"}[$__rate_interval]),\n \"status\",\n \"${1}xx\",\n \"\",\n \"([0-9])..\")\n )\n", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{}}", + "refId": "A" + } + ], + "title": "Api Requests", + "transparent": false, + "type": "stat" + }, + { + "collapsed": false, + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "gridPos": { + "h": 7, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 39, + "panels": [], + "repeat": "", + "targets": [ + { + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "refId": "A" + } + ], + "title": "Overview", + "type": "row" + }, + { + "aliasColors": { + "Limit": "#E24D42", + "Requested": "#Ffa500" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "fill": 0, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 9 + }, + "id": 5, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum by (pod) (app_memory_usage_bytes{env=\"$environment\", region=\"$region\", instance=~\"example-api.*\", component=~\".*\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{pod}}", + "refId": "A" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "min(app_resource_quota{env=\"$environment\", region=\"$region\", resource=\"memory\", instance=~\"example-api.*\", component=~\".*\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Requested", + "refId": "B" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "min(app_resource_limit{env=\"$environment\", region=\"$region\", resource=\"memory\", instance=~\"example-api.*\", component=~\".*\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Limit", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Memory Usage", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": { + "Limit": "#E24D42", + "Requested": "#Ffa500" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "fill": 0, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 9 + }, + "id": 6, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum by (pod) (rate(app_cpu_usage_ratio{env=\"$environment\", region=\"$region\", instance=~\"example-api.*\", component=~\".*\"}[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{pod}}", + "refId": "A" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "min(app_resource_quota{env=\"$environment\", region=\"$region\", resource=\"cpu\", instance=~\"example-api.*\", component=~\".*\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Requested", + "refId": "B" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "min(app_resource_limit{env=\"$environment\", region=\"$region\", resource=\"cpu\", instance=~\"example-api.*\", component=~\".*\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Limit", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "CPU Usage", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": { + "1xx": "#EAB839", + "2xx": "#7EB26D", + "3xx": "#6ED0E0", + "4xx": "#EF843C", + "5xx": "#E24D42" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "decimals": 2, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 16 + }, + "id": 7, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum by (status_code) (\n label_replace(\n rate(example_service_request_duration_seconds_count{env=\"$environment\", region=\"$region\", service=\"api\", route!~\"|root|other|ready|metrics|debug_pprof\"}[$__rate_interval]),\n \"status\",\n \"${1}xx\",\n \"status_code\",\n \"([0-9])..\")\n )\n", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{status_code}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Throughput (requests/sec)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "decimals": 2, + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "decimals": 2, + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 16 + }, + "id": 8, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "histogram_quantile(0.99, sum(rate(example_service_request_duration_seconds_bucket{env=\"$environment\", region=\"$region\", service=\"api\", route!~\"|root|other|ready|metrics|debug_pprof\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "p99", + "refId": "A" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "histogram_quantile(0.90, sum(rate(example_service_request_duration_seconds_bucket{env=\"$environment\", region=\"$region\", service=\"api\", route!~\"|root|other|ready|metrics|debug_pprof\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "p90", + "refId": "B" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "histogram_quantile(0.50, sum(rate(example_service_request_duration_seconds_bucket{env=\"$environment\", region=\"$region\", service=\"api\", route!~\"|root|other|ready|metrics|debug_pprof\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "p50", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Latency", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 16 + }, + "id": 9, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "(rate(example_service_request_duration_seconds_count{env=\"$environment\", region=\"$region\", service=\"api\", route!~\"|root|other|ready|metrics|debug_pprof\", status_code=~\"5..\"}[$__rate_interval]) / rate(example_service_request_duration_seconds_count{env=\"$environment\", region=\"$region\", service=\"api\", route!~\"|root|other|ready|metrics|debug_pprof\"}[$__rate_interval])) OR on() vector(0)", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Error rate", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Error rate", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": 1, + "min": 0, + "show": true + }, + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": 1, + "min": 0, + "show": true + } + ] + }, + { + "datasource": { + "uid": "$datasourcelogs" + }, + "gridPos": { + "h": 7, + "w": 24, + "x": 0, + "y": 23 + }, + "id": 10, + "options": { + "showLabels": false, + "showTime": true, + "sortOrder": "Descending", + "wrapLogMessage": true + }, + "targets": [ + { + "datasource": { + "uid": "$datasourcelogs" + }, + "expr": "{env=\"$environment\", region=\"$region\", service=\"api\"}", + "legendFormat": "", + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Logs", + "type": "logs" + }, + { + "datasource": { + "uid": "$datasourcelogs" + }, + "gridPos": { + "h": 7, + "w": 24, + "x": 0, + "y": 30 + }, + "id": 11, + "options": { + "showLabels": false, + "showTime": true, + "sortOrder": "Descending", + "wrapLogMessage": true + }, + "targets": [ + { + "datasource": { + "uid": "$datasourcelogs" + }, + "expr": "{env=\"$environment\", region=\"$region\", service=\"api\"} | logfmt | level=\"error\"", + "legendFormat": "", + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Errors", + "type": "logs" + }, + { + "collapsed": false, + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "gridPos": { + "h": 7, + "w": 24, + "x": 0, + "y": 8 + }, + "id": 40, + "panels": [], + "repeat": "", + "targets": [ + { + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "refId": "A" + } + ], + "title": "API", + "type": "row" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 38 + }, + "id": 12, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "histogram_quantile(0.99, sum(rate(example_service_database_request_duration_seconds_bucket{env=\"$environment\", region=\"$region\", service=\"api\"}[$__rate_interval])) by (le, method)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{method}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Client Latency p99", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 38 + }, + "id": 13, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "histogram_quantile(0.90, sum(rate(example_service_database_request_duration_seconds_bucket{env=\"$environment\", region=\"$region\", service=\"api\"}[$__rate_interval])) by (le, method)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{method}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Client Latency p90", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 38 + }, + "id": 14, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "histogram_quantile(0.50, sum(rate(example_service_database_request_duration_seconds_bucket{env=\"$environment\", region=\"$region\", service=\"api\"}[$__rate_interval])) by (le, method)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{method}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Client Latency p50", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 45 + }, + "id": 15, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum(rate(example_service_database_request_duration_seconds_count{env=\"$environment\", region=\"$region\", service=\"api\"}[1m]))", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Total", + "refId": "A" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum(rate(example_service_database_request_duration_seconds_count{env=\"$environment\", region=\"$region\", service=\"api\", method=~\"GetConfig.*\"}[1m]))", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "GetConfig", + "refId": "B" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum(rate(example_service_database_request_duration_seconds_count{env=\"$environment\", region=\"$region\", service=\"api\", method!~\"GetConfig.*\"}[1m]))", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Other", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Throughput (requests/sec)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "fill": 0, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 45 + }, + "id": 16, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum by (pod) (go_sql_in_use_connections {env=\"$environment\", region=\"$region\", service=\"api\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{pod}}/Active", + "refId": "A" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum by (pod) (go_sql_idle_connections {env=\"$environment\", region=\"$region\", service=\"api\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{pod}}/Idle", + "refId": "B" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum by (pod) (go_sql_max_open_connections {env=\"$environment\", region=\"$region\", service=\"api\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{pod}}/Max", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Connections", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "fill": 0, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 45 + }, + "id": 17, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum by (pod) (rate(go_sql_wait_duration_seconds_total{env=\"$environment\", region=\"$region\", service=\"api\"}[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{pod}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Connection Wait Duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "collapsed": false, + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "gridPos": { + "h": 7, + "w": 24, + "x": 0, + "y": 37 + }, + "id": 41, + "panels": [], + "repeat": "", + "targets": [ + { + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "refId": "A" + } + ], + "title": "Database", + "type": "row" + }, + { + "aliasColors": { + "Limit": "#E24D42", + "Requested": "#Ffa500" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "fill": 0, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 53 + }, + "id": 18, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum by (pod) (app_memory_usage_bytes{env=\"$environment\", region=\"$region\", instance=~\"cache.*\", component=~\"cache-server\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{pod}}", + "refId": "A" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "min(app_resource_quota{env=\"$environment\", region=\"$region\", resource=\"memory\", instance=~\"cache.*\", component=~\"cache-server\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Requested", + "refId": "B" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "min(app_resource_limit{env=\"$environment\", region=\"$region\", resource=\"memory\", instance=~\"cache.*\", component=~\"cache-server\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Limit", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Memory Usage", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": { + "Limit": "#E24D42", + "Requested": "#Ffa500" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "fill": 0, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 53 + }, + "id": 19, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum by (pod) (rate(app_cpu_usage_ratio{env=\"$environment\", region=\"$region\", instance=~\"cache.*\", component=~\"cache-server\"}[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{pod}}", + "refId": "A" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "min(app_resource_quota{env=\"$environment\", region=\"$region\", resource=\"cpu\", instance=~\"cache.*\", component=~\"cache-server\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Requested", + "refId": "B" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "min(app_resource_limit{env=\"$environment\", region=\"$region\", resource=\"cpu\", instance=~\"cache.*\", component=~\"cache-server\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Limit", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "CPU Usage", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 60 + }, + "id": 20, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum by (pod) (rate(example_service_cache_set_total{env=\"$environment\", region=\"$region\", service=\"api\"}[1m]))", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{pod}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Client Sets", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 60 + }, + "id": 21, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum by (pod) (rate(example_service_cache_request_total{env=\"$environment\", region=\"$region\", service=\"api\"}[1m]))", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{pod}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Client Requests", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 60 + }, + "id": 22, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum by (pod) (rate(example_service_cache_request_hit_total{env=\"$environment\", region=\"$region\", service=\"api\"}[$__rate_interval]) / rate(example_service_cache_request_total{env=\"$environment\", region=\"$region\", service=\"api\"}[$__rate_interval]))", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{pod}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Client Cache Request Hit Rate", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "fill": 0, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 67 + }, + "id": 23, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "histogram_quantile(0.99, sum(rate(example_service_cache_request_duration_seconds_bucket{env=\"$environment\", region=\"$region\", service=\"api\"}[$__rate_interval])) by (le, method)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{method}}/request", + "refId": "A" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "histogram_quantile(0.99, sum(rate(example_service_cache_set_duration_seconds_bucket{env=\"$environment\", region=\"$region\", service=\"api\"}[$__rate_interval])) by (le, method)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{method}}/set", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Client Latency p99", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "fill": 0, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 67 + }, + "id": 24, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "histogram_quantile(0.90, sum(rate(example_service_cache_request_duration_seconds_bucket{env=\"$environment\", region=\"$region\", service=\"api\"}[$__rate_interval])) by (le, method)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{method}}/request", + "refId": "A" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "histogram_quantile(0.90, sum(rate(example_service_cache_set_duration_seconds_bucket{env=\"$environment\", region=\"$region\", service=\"api\"}[$__rate_interval])) by (le, method)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{method}}/set", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Client Latency p90", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "fill": 0, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 67 + }, + "id": 25, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "histogram_quantile(0.50, sum(rate(example_service_cache_request_duration_seconds_bucket{env=\"$environment\", region=\"$region\", service=\"api\"}[$__rate_interval])) by (le, method)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{method}}/request", + "refId": "A" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "histogram_quantile(0.50, sum(rate(example_service_cache_set_duration_seconds_bucket{env=\"$environment\", region=\"$region\", service=\"api\"}[$__rate_interval])) by (le, method)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{method}}/set", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Client Latency p50", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 74 + }, + "id": 26, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum(rate(example_service_cache_request_duration_seconds_count{env=\"$environment\", region=\"$region\", service=\"api\"}[1m]))", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Total", + "refId": "A" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum(rate(example_service_cache_request_duration_seconds_count{env=\"$environment\", region=\"$region\", service=\"api\", method=~\".*Config.*\"}[1m]))", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Config", + "refId": "B" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum(rate(example_service_cache_request_duration_seconds_count{env=\"$environment\", region=\"$region\", service=\"api\", method!~\".*Config.*\"}[1m]))", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Other", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Request Throughput (requests/sec)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 74 + }, + "id": 27, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum(rate(example_service_cache_set_duration_seconds_count{env=\"$environment\", region=\"$region\", service=\"api\"}[1m]))", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Total", + "refId": "A" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum(rate(example_service_cache_set_duration_seconds_count{env=\"$environment\", region=\"$region\", service=\"api\", method=~\".*Config.*\"}[1m]))", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Config", + "refId": "B" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum(rate(example_service_cache_set_duration_seconds_count{env=\"$environment\", region=\"$region\", service=\"api\", method!~\".*Config.*\"}[1m]))", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Other", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Set Throughput (requests/sec)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "collapsed": false, + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "gridPos": { + "h": 7, + "w": 24, + "x": 0, + "y": 52 + }, + "id": 42, + "panels": [], + "repeat": "", + "targets": [ + { + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "refId": "A" + } + ], + "title": "Cache", + "type": "row" + }, + { + "aliasColors": { + "Limit": "#E24D42", + "Requested": "#Ffa500" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "fill": 0, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 82 + }, + "id": 28, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum by (pod) (app_memory_usage_bytes{env=\"$environment\", region=\"$region\", instance=~\"gateway.*\", component=~\".*\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{pod}}", + "refId": "A" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "min(app_resource_quota{env=\"$environment\", region=\"$region\", resource=\"memory\", instance=~\"gateway.*\", component=~\".*\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Requested", + "refId": "B" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "min(app_resource_limit{env=\"$environment\", region=\"$region\", resource=\"memory\", instance=~\"gateway.*\", component=~\".*\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Limit", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Memory Usage", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": { + "Limit": "#E24D42", + "Requested": "#Ffa500" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "fill": 0, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 82 + }, + "id": 29, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum by (pod) (rate(app_cpu_usage_ratio{env=\"$environment\", region=\"$region\", instance=~\"gateway.*\", component=~\".*\"}[1m]))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{pod}}", + "refId": "A" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "min(app_resource_quota{env=\"$environment\", region=\"$region\", resource=\"cpu\", instance=~\"gateway.*\", component=~\".*\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Requested", + "refId": "B" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "min(app_resource_limit{env=\"$environment\", region=\"$region\", resource=\"cpu\", instance=~\"gateway.*\", component=~\".*\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Limit", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "CPU Usage", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 89 + }, + "id": 30, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "histogram_quantile(0.99, sum(rate(agent_management_gateway_grafanacloud_authenticator_authentication_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "p99", + "refId": "A" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "histogram_quantile(0.90, sum(rate(agent_management_gateway_grafanacloud_authenticator_authentication_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "p90", + "refId": "B" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "histogram_quantile(0.50, sum(rate(agent_management_gateway_grafanacloud_authenticator_authentication_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "p50", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Authenticator latency", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 89 + }, + "id": 31, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "histogram_quantile(0.99, sum(rate(agent_management_gateway_grafanacloud_cachedclient_get_instance_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "p99", + "refId": "A" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "histogram_quantile(0.95, sum(rate(agent_management_gateway_grafanacloud_cachedclient_get_instance_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "p95", + "refId": "B" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "histogram_quantile(0.9, sum(rate(agent_management_gateway_grafanacloud_cachedclient_get_instance_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "p90", + "refId": "C" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "histogram_quantile(0.5, sum(rate(agent_management_gateway_grafanacloud_cachedclient_get_instance_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "p50", + "refId": "D" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "GCOM cached client latency", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 89 + }, + "id": 32, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "histogram_quantile(0.99, sum(rate(agent_management_request_downstream_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "p99", + "refId": "A" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "histogram_quantile(0.90, sum(rate(agent_management_request_downstream_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "p90", + "refId": "B" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "histogram_quantile(0.50, sum(rate(agent_management_request_downstream_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "p50", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Handler latency", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": { + "1xx": "#EAB839", + "2xx": "#7EB26D", + "3xx": "#6ED0E0", + "4xx": "#EF843C", + "5xx": "#E24D42" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "decimals": 2, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 96 + }, + "id": 33, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum by (code) (\n label_replace(\n rate(grafanacom_api_request_duration_seconds_count{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval]),\n \"status\",\n \"${1}xx\",\n \"code\",\n \"([0-9])..\")\n )\n", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{code}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Grafana.com HTTP Client - Throughput (requests/sec)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "decimals": 2, + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "decimals": 2, + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 96 + }, + "id": 34, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "histogram_quantile(0.99, sum(rate(grafanacom_api_request_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "5m", + "intervalFactor": 2, + "legendFormat": "p99", + "refId": "A" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "histogram_quantile(0.90, sum(rate(grafanacom_api_request_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "5m", + "intervalFactor": 2, + "legendFormat": "p90", + "refId": "B" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "histogram_quantile(0.50, sum(rate(grafanacom_api_request_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "5m", + "intervalFactor": 2, + "legendFormat": "p50", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Grafana.com HTTP Client - Latency", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 96 + }, + "id": 35, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "(rate(grafanacom_api_request_duration_seconds_count{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\", code=~\"5..|error\"}[$__rate_interval]) / rate(grafanacom_api_request_duration_seconds_count{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) OR on() vector(0)", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Error rate", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Grafana.com HTTP Client - Error rate", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": 1, + "min": 0, + "show": true + }, + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": 1, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": { + "1xx": "#EAB839", + "2xx": "#7EB26D", + "3xx": "#6ED0E0", + "4xx": "#EF843C", + "5xx": "#E24D42" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "decimals": 2, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 103 + }, + "id": 36, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum by (status_code) (\n label_replace(\n rate(auth_client_request_duration_seconds_count{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval]),\n \"status\",\n \"${1}xx\",\n \"status_code\",\n \"([0-9])..\")\n )\n", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "{{status_code}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Auth API HTTP Client - Throughput (requests/sec)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "decimals": 2, + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "decimals": 2, + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 103 + }, + "id": 37, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "histogram_quantile(0.99, sum(rate(auth_client_request_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "5m", + "intervalFactor": 2, + "legendFormat": "p99", + "refId": "A" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "histogram_quantile(0.90, sum(rate(auth_client_request_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "5m", + "intervalFactor": 2, + "legendFormat": "p90", + "refId": "B" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "histogram_quantile(0.50, sum(rate(auth_client_request_duration_seconds_bucket{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) by (le)) * 1e3", + "format": "time_series", + "interval": "5m", + "intervalFactor": 2, + "legendFormat": "p50", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Auth API HTTP Client - Latency", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "ms", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "uid": "$datasource" + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 103 + }, + "id": 38, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "(rate(auth_client_request_duration_seconds_count{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\", status_code=~\"5..|error\"}[$__rate_interval]) / rate(auth_client_request_duration_seconds_count{env=\"$environment\", region=\"$region\", job=\"$namespace/gateway\"}[$__rate_interval])) OR on() vector(0)", + "format": "time_series", + "interval": "1m", + "intervalFactor": 2, + "legendFormat": "Error rate", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Auth API HTTP Client - Error rate", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": 1, + "min": 0, + "show": true + }, + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": 1, + "min": 0, + "show": true + } + ] + }, + { + "collapsed": false, + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "gridPos": { + "h": 7, + "w": 24, + "x": 0, + "y": 81 + }, + "id": 43, + "panels": [], + "repeat": "", + "targets": [ + { + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "refId": "A" + } + ], + "title": "Gateway", + "type": "row" + } + ], + "refresh": "1m", + "schemaVersion": 41, + "style": "dark", + "tags": [ + "example-service", + "as-code" + ], + "templating": { + "list": [ + { + "current": { + "value": "prometheus-datasource" + }, + "hide": 0, + "label": null, + "name": "datasource", + "options": [], + "query": "prometheus", + "refresh": 1, + "regex": "", + "type": "datasource" + }, + { + "current": { + "value": "logs-datasource" + }, + "hide": 0, + "label": null, + "name": "datasourcelogs", + "options": [], + "query": "loki", + "refresh": 1, + "regex": "", + "type": "datasource" + }, + { + "allValue": null, + "current": { + "text": "cluster-1", + "value": "cluster-1" + }, + "datasource": { + "uid": "$datasource" + }, + "hide": 0, + "includeAll": false, + "label": null, + "multi": false, + "name": "cluster", + "options": [], + "query": "label_values(example_service_inflight_requests{}, cluster)", + "refresh": 1, + "regex": "", + "sort": 0, + "type": "query" + }, + { + "allValue": null, + "current": { + "text": "cluster-1", + "value": "cluster-1" + }, + "datasource": { + "uid": "$datasource" + }, + "hide": 0, + "includeAll": false, + "label": null, + "multi": false, + "name": "namespace", + "options": [], + "query": "label_values(example_service_inflight_requests{cluster=\"$cluster\"}, namespace)", + "refresh": 1, + "regex": "example-service.*", + "sort": 0, + "type": "query" + } + ] + }, + "time": { + "from": "now-2h", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ] + }, + "timezone": "browser", + "title": "Service Overview Dashboard", + "uid": "ba9b93e998e09d254266b11192894a8d", + "version": 0 +} \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/testdata/output/v14.broken-dash2.json b/apps/dashboard/pkg/migration/testdata/output/v14.broken-dash2.json new file mode 100644 index 00000000000..955813c4305 --- /dev/null +++ b/apps/dashboard/pkg/migration/testdata/output/v14.broken-dash2.json @@ -0,0 +1,2343 @@ +{ + "__requires": [ + { + "id": "grafana", + "name": "Grafana", + "type": "grafana", + "version": "8.0.0" + } + ], + "annotations": { + "list": [ + { + "datasource": { + "uid": "$datasource" + }, + "enable": true, + "expr": "ALERTS{alertname=\"MetricsRolloutUnderway\", region=~\"$region\", env=~\"$environment\", alertstate=\"firing\"}", + "hide": false, + "iconColor": "rgba(255, 96, 96, 1)", + "name": "rollouts", + "showIn": 0, + "tags": [], + "titleFormat": "Rollout was underway in {{cluster}}/{{namespace}}", + "type": "tags" + } + ] + }, + "editable": false, + "gnetId": null, + "graphTooltip": 1, + "hideControls": false, + "links": [], + "panels": [ + { + "content": "The 'Status' panel shows an overview on the cluster health over the time.\nTo investigate failures, see a specific dashboard:\n\n- \u003ca target=\"_blank\" href=\"./d/8280707b8f16e7b87b840fc1cc92d4c5/service-writes?${__url_time_range}\u0026${__all_variables}\"\u003eWrites\u003c/a\u003e\n- \u003ca target=\"_blank\" href=\"./d/e327503188913dc38ad571c647eef643/service-reads?${__url_time_range}\u0026${__all_variables}\"\u003eReads\u003c/a\u003e\n- \u003ca target=\"_blank\" href=\"./d/631e15d5d85afb2ca8e35d62984eeaa0/service-ruler?${__url_time_range}\u0026${__all_variables}\"\u003eRule evaluations\u003c/a\u003e\n- \u003ca target=\"_blank\" href=\"./d/b0d38d318bbddd80476246d4930f9e55/service-alertmanager?${__url_time_range}\u0026${__all_variables}\"\u003eAlerting notifications\u003c/a\u003e\n- \u003ca target=\"_blank\" href=\"./d/e1324ee2a434f4158c00a9ee279d3292/service-object-store?${__url_time_range}\u0026${__all_variables}\"\u003eObject storage\u003c/a\u003e\n", + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "description": "", + "gridPos": { + "h": 7, + "w": 6, + "x": 0, + "y": 1 + }, + "id": 1, + "mode": "markdown", + "targets": [ + { + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "refId": "A" + } + ], + "title": "", + "transparent": true, + "type": "text" + }, + { + "datasource": { + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "#7EB26D", + "value": null + }, + { + "color": "#EAB839", + "value": 0.01 + }, + { + "color": "#E24D42", + "value": 0.05 + } + ] + } + } + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 6, + "y": 1 + }, + "id": 2, + "options": { + "showValue": "never" + }, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "exemplar": false, + "expr": "((\n # gRPC errors are not tracked as 5xx but \"error\".\n sum(histogram_count(rate(http_request_duration_seconds{service=~\"gateway\", route=~\"api_v1_write\",status_code=~\"5.*|error\"}[$__rate_interval])))\n or\n # Handle the case no failure has been tracked yet.\n vector(0)\n)\n/\nsum(histogram_count(rate(http_request_duration_seconds{service=~\"gateway\", route=~\"api_v1_write\"}[$__rate_interval])))\n) and on() (vector($latency_metrics) == -1)", + "instant": false, + "legendFormat": "Writes", + "range": true + }, + { + "datasource": { + "uid": "$datasource" + }, + "exemplar": false, + "expr": "((\n # gRPC errors are not tracked as 5xx but \"error\".\n sum(rate(http_request_duration_seconds_count{service=~\"gateway\", route=~\"api_v1_write\",status_code=~\"5.*|error\"}[$__rate_interval]))\n or\n # Handle the case no failure has been tracked yet.\n vector(0)\n)\n/\nsum(rate(http_request_duration_seconds_count{service=~\"gateway\", route=~\"api_v1_write\"}[$__rate_interval]))\n) and on() (vector($latency_metrics) == 1)", + "instant": false, + "legendFormat": "Writes", + "range": true + }, + { + "datasource": { + "uid": "$datasource" + }, + "exemplar": false, + "expr": "((\n # gRPC errors are not tracked as 5xx but \"error\".\n sum(histogram_count(rate(http_request_duration_seconds{service=~\"gateway\", route=~\"api_v1_query\",status_code=~\"5.*|error\"}[$__rate_interval])))\n or\n # Handle the case no failure has been tracked yet.\n vector(0)\n)\n/\nsum(histogram_count(rate(http_request_duration_seconds{service=~\"gateway\", route=~\"api_v1_query\"}[$__rate_interval])))\n) and on() (vector($latency_metrics) == -1)", + "instant": false, + "legendFormat": "Reads", + "range": true + }, + { + "datasource": { + "uid": "$datasource" + }, + "exemplar": false, + "expr": "((\n # gRPC errors are not tracked as 5xx but \"error\".\n sum(rate(http_request_duration_seconds_count{service=~\"gateway\", route=~\"api_v1_query\",status_code=~\"5.*|error\"}[$__rate_interval]))\n or\n # Handle the case no failure has been tracked yet.\n vector(0)\n)\n/\nsum(rate(http_request_duration_seconds_count{service=~\"gateway\", route=~\"api_v1_query\"}[$__rate_interval]))\n) and on() (vector($latency_metrics) == 1)", + "instant": false, + "legendFormat": "Reads", + "range": true + }, + { + "datasource": { + "uid": "$datasource" + }, + "exemplar": false, + "expr": "(\n (\n sum(rate(monitoring_rule_evaluation_failures_total{service=~\"rule-engine\"}[$__rate_interval]))\n +\n # Consider missed evaluations as failures.\n sum(rate(monitoring_rule_group_iterations_missed_total{service=~\"rule-engine\"}[$__rate_interval]))\n )\n or\n # Handle the case no failure has been tracked yet.\n vector(0)\n)\n/\nsum(rate(monitoring_rule_evaluations_total{service=~\"rule-engine\"}[$__rate_interval]))\n", + "instant": false, + "legendFormat": "Rule evaluations", + "range": true + }, + { + "datasource": { + "uid": "$datasource" + }, + "exemplar": false, + "expr": "(\n # Failed notifications from ruler to Alertmanager (handling the case the ruler metrics are missing).\n ((sum(rate(alert_notifications_errors_total{service=~\"rule-engine\"}[$__rate_interval]))\n) or vector(0))\n +\n # Failed notifications from Alertmanager to receivers (handling the case the alertmanager metrics are missing).\n ((sum(service_integration:alerting_service_notifications_failed_total:rate5m{service=~\"alerting\"}) or vector(0)\n) or vector(0))\n)\n/\n(\n # Total notifications from ruler to Alertmanager (handling the case the ruler metrics are missing).\n ((sum(rate(alert_notifications_sent_total{service=~\"rule-engine\"}[$__rate_interval]))\n) or vector(0))\n +\n # Total notifications from Alertmanager to receivers (handling the case the alertmanager metrics are missing).\n ((sum(service_integration:alerting_service_notifications_total:rate5m{service=~\"alerting\"}) or vector(0)\n) or vector(0))\n)\n", + "instant": false, + "legendFormat": "Alerting notifications", + "range": true + }, + { + "datasource": { + "uid": "$datasource" + }, + "exemplar": false, + "expr": "sum(rate(storage_operation_failures_total{env!=\"\"}[$__rate_interval]))\n/\nsum(rate(storage_operations_total{env!=\"\"}[$__rate_interval]))\n", + "instant": false, + "legendFormat": "Object storage", + "range": true + } + ], + "title": "Status", + "type": "state-timeline" + }, + { + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 18, + "y": 1 + }, + "id": 3, + "options": { + "alertInstanceLabelFilter": "env!=\"\"", + "alertName": "Metrics", + "dashboardAlerts": false, + "maxItems": 100, + "sortOrder": 3, + "stateFilter": { + "error": true, + "firing": true, + "noData": false, + "normal": false, + "pending": false + } + }, + "targets": [ + { + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "refId": "A" + } + ], + "title": "Firing alerts", + "type": "alertlist" + }, + { + "collapsed": false, + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "gridPos": { + "h": 7, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 20, + "panels": [], + "repeat": "", + "targets": [ + { + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "refId": "A" + } + ], + "title": "Service cluster health", + "type": "row" + }, + { + "content": "These panels show an overview on the write path. Requests rate and latency is measured on the gateway.\nTo examine the write path in detail, see a specific dashboard:\n\n- \u003ca target=\"_blank\" href=\"./d/8280707b8f16e7b87b840fc1cc92d4c5/service-writes?${__url_time_range}\u0026${__all_variables}\"\u003eWrites\u003c/a\u003e\n- \u003ca target=\"_blank\" href=\"./d/bc9160e50b52e89e0e49c840fea3d379/service-writes-resources?${__url_time_range}\u0026${__all_variables}\"\u003eWrites resources\u003c/a\u003e\n- \u003ca target=\"_blank\" href=\"./d/978c1cb452585c96697a238eaac7fe2d/service-writes-networking?${__url_time_range}\u0026${__all_variables}\"\u003eWrites networking\u003c/a\u003e\n- \u003ca target=\"_blank\" href=\"./d/a9b92d3c4d1af325d872a9e9a7083d71/service-overview-resources?${__url_time_range}\u0026${__all_variables}\"\u003eOverview resources\u003c/a\u003e\n- \u003ca target=\"_blank\" href=\"./d/e15c71d372cc541367a088f10d9fcd92/service-overview-networking?${__url_time_range}\u0026${__all_variables}\"\u003eOverview networking\u003c/a\u003e\n", + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "description": "", + "gridPos": { + "h": 7, + "w": 6, + "x": 0, + "y": 9 + }, + "id": 4, + "mode": "markdown", + "targets": [ + { + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "refId": "A" + } + ], + "title": "", + "transparent": true, + "type": "text" + }, + { + "datasource": { + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "custom": { + "drawStyle": "line", + "fillOpacity": 100, + "lineWidth": 0, + "pointSize": 5, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "normal" + } + }, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [] + }, + "unit": "reqps" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "1xx" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#EAB839", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "2xx" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#7EB26D", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "3xx" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#6ED0E0", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "4xx" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#EF843C", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "5xx" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#E24D42", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "OK" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#7EB26D", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "cancel" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#A9A9A9", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "error" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#E24D42", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "success" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#7EB26D", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "1xx" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#EAB839", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "2xx" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#7EB26D", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "3xx" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#6ED0E0", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "4xx" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#EF843C", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "5xx" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#E24D42", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Canceled" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#A9A9A9", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "OK" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#7EB26D", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Success" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#7EB26D", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "cancel" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#A9A9A9", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "error" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#E24D42", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 6, + "y": 9 + }, + "id": 5, + "links": [], + "options": { + "legend": { + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "(sum by (status) (\n label_replace(label_replace(rate(http_request_duration_seconds_count{service=~\"gateway\", route=~\"api_v1_write\"}[$__rate_interval]),\n \"status\", \"${1}xx\", \"status_code\", \"([0-9])..\"),\n \"status\", \"${1}\", \"status_code\", \"([a-zA-Z]+)\"))\n) and on() (vector($latency_metrics) == 1)", + "format": "time_series", + "legendFormat": "{{status}}", + "refId": "A_classic" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "(sum by (status) (\n label_replace(label_replace(histogram_count(rate(http_request_duration_seconds{service=~\"gateway\", route=~\"api_v1_write\"}[$__rate_interval])),\n \"status\", \"${1}xx\", \"status_code\", \"([0-9])..\"),\n \"status\", \"${1}\", \"status_code\", \"([a-zA-Z]+)\"))\n) and on() (vector($latency_metrics) == -1)", + "format": "time_series", + "legendFormat": "{{status}}", + "refId": "A" + } + ], + "title": "Write requests / sec (gateway)", + "type": "timeseries" + }, + { + "datasource": { + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "custom": { + "drawStyle": "line", + "fillOpacity": 1, + "lineWidth": 1, + "pointSize": 5, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + } + }, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 12, + "y": 9 + }, + "id": 6, + "links": [], + "nullPointMode": "null as zero", + "options": { + "legend": { + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "(histogram_quantile(0.99, sum by (le) (service_route:http_request_duration_seconds_bucket:sum_rate{job=~\"(.*)((gateway|service-gw.*))\", route=~\"api_v1_write\"})) * 1e3) and on() (vector($latency_metrics) == 1)", + "format": "time_series", + "legendFormat": "99th percentile", + "refId": "A_classic" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "(histogram_quantile(0.99, sum (service_route:http_request_duration_seconds:sum_rate{job=~\"(.*)((gateway|service-gw.*))\", route=~\"api_v1_write\"})) * 1e3) and on() (vector($latency_metrics) == -1)", + "format": "time_series", + "legendFormat": "99th percentile", + "refId": "A_native" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "(histogram_quantile(0.50, sum by (le) (service_route:http_request_duration_seconds_bucket:sum_rate{job=~\"(.*)((gateway|service-gw.*))\", route=~\"api_v1_write\"})) * 1e3) and on() (vector($latency_metrics) == 1)", + "format": "time_series", + "legendFormat": "50th percentile", + "refId": "B_classic" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "(histogram_quantile(0.50, sum (service_route:http_request_duration_seconds:sum_rate{job=~\"(.*)((gateway|service-gw.*))\", route=~\"api_v1_write\"})) * 1e3) and on() (vector($latency_metrics) == -1)", + "format": "time_series", + "legendFormat": "50th percentile", + "refId": "B_native" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "(1e3 * sum(service_route:http_request_duration_seconds_sum:sum_rate{job=~\"(.*)((gateway|service-gw.*))\", route=~\"api_v1_write\"}) /\nsum(service_route:http_request_duration_seconds_count:sum_rate{job=~\"(.*)((gateway|service-gw.*))\", route=~\"api_v1_write\"})\n) and on() (vector($latency_metrics) == 1)", + "format": "time_series", + "legendFormat": "Average", + "refId": "C_classic" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "(1e3 * sum(histogram_sum(service_route:http_request_duration_seconds:sum_rate{job=~\"(.*)((gateway|service-gw.*))\", route=~\"api_v1_write\"})) /\nsum(histogram_count(service_route:http_request_duration_seconds:sum_rate{job=~\"(.*)((gateway|service-gw.*))\", route=~\"api_v1_write\"}))\n) and on() (vector($latency_metrics) == -1)", + "format": "time_series", + "legendFormat": "Average", + "refId": "C_native" + } + ], + "title": "Write latency (gateway)", + "type": "timeseries" + }, + { + "datasource": { + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "custom": { + "drawStyle": "line", + "fillOpacity": 100, + "lineWidth": 0, + "pointSize": 5, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "normal" + } + }, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [] + }, + "unit": "cps" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 18, + "y": 9 + }, + "id": 7, + "links": [], + "options": { + "legend": { + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum(cluster_namespace_job:ingestion_service_received_samples:rate5m{job=~\"(.*)/(distributor.*|service|metrics)\"})", + "format": "time_series", + "legendFormat": "samples / sec", + "legendLink": null + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum(cluster_namespace_job:ingestion_service_received_exemplars:rate5m{job=~\"(.*)/(distributor.*|service|metrics)\"})", + "format": "time_series", + "legendFormat": "exemplars / sec", + "legendLink": null + } + ], + "title": "Ingestion / sec", + "type": "timeseries" + }, + { + "collapsed": false, + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "gridPos": { + "h": 7, + "w": 24, + "x": 0, + "y": 8 + }, + "id": 21, + "panels": [], + "repeat": "", + "targets": [ + { + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "refId": "A" + } + ], + "title": "Writes", + "type": "row" + }, + { + "content": "These panels show an overview on the read path. Requests rate and latency is measured on the gateway.\nTo examine the read path in detail, see a specific dashboard:\n\n- \u003ca target=\"_blank\" href=\"./d/e327503188913dc38ad571c647eef643/service-reads?${__url_time_range}\u0026${__all_variables}\"\u003eReads\u003c/a\u003e\n- \u003ca target=\"_blank\" href=\"./d/cc86fd5aa9301c6528986572ad974db9/service-reads-resources?${__url_time_range}\u0026${__all_variables}\"\u003eReads resources\u003c/a\u003e\n- \u003ca target=\"_blank\" href=\"./d/54b2a0a4748b3bd1aefa92ce5559a1c2/service-reads-networking?${__url_time_range}\u0026${__all_variables}\"\u003eReads networking\u003c/a\u003e\n- \u003ca target=\"_blank\" href=\"./d/a9b92d3c4d1af325d872a9e9a7083d71/service-overview-resources?${__url_time_range}\u0026${__all_variables}\"\u003eOverview resources\u003c/a\u003e\n- \u003ca target=\"_blank\" href=\"./d/e15c71d372cc541367a088f10d9fcd92/service-overview-networking?${__url_time_range}\u0026${__all_variables}\"\u003eOverview networking\u003c/a\u003e\n- \u003ca target=\"_blank\" href=\"./d/b3abe8d5c040395cc36615cb4334c92d/service-queries?${__url_time_range}\u0026${__all_variables}\"\u003eQueries\u003c/a\u003e\n- \u003ca target=\"_blank\" href=\"./d/1b3443aea86db629e6efdb7d05c53823/service-compactor?${__url_time_range}\u0026${__all_variables}\"\u003eCompactor\u003c/a\u003e\n", + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "description": "", + "gridPos": { + "h": 7, + "w": 6, + "x": 0, + "y": 17 + }, + "id": 8, + "mode": "markdown", + "targets": [ + { + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "refId": "A" + } + ], + "title": "", + "transparent": true, + "type": "text" + }, + { + "datasource": { + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "custom": { + "drawStyle": "line", + "fillOpacity": 100, + "lineWidth": 0, + "pointSize": 5, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "normal" + } + }, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [] + }, + "unit": "reqps" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "1xx" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#EAB839", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "2xx" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#7EB26D", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "3xx" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#6ED0E0", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "4xx" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#EF843C", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "5xx" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#E24D42", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "OK" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#7EB26D", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "cancel" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#A9A9A9", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "error" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#E24D42", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "success" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#7EB26D", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "1xx" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#EAB839", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "2xx" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#7EB26D", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "3xx" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#6ED0E0", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "4xx" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#EF843C", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "5xx" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#E24D42", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Canceled" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#A9A9A9", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "OK" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#7EB26D", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Success" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#7EB26D", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "cancel" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#A9A9A9", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "error" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#E24D42", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 6, + "y": 17 + }, + "id": 9, + "links": [], + "options": { + "legend": { + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "(sum by (status) (\n label_replace(label_replace(rate(http_request_duration_seconds_count{service=~\"gateway\", route=~\"api_v1_query\"}[$__rate_interval]),\n \"status\", \"${1}xx\", \"status_code\", \"([0-9])..\"),\n \"status\", \"${1}\", \"status_code\", \"([a-zA-Z]+)\"))\n) and on() (vector($latency_metrics) == 1)", + "format": "time_series", + "legendFormat": "{{status}}", + "refId": "A_classic" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "(sum by (status) (\n label_replace(label_replace(histogram_count(rate(http_request_duration_seconds{service=~\"gateway\", route=~\"api_v1_query\"}[$__rate_interval])),\n \"status\", \"${1}xx\", \"status_code\", \"([0-9])..\"),\n \"status\", \"${1}\", \"status_code\", \"([a-zA-Z]+)\"))\n) and on() (vector($latency_metrics) == -1)", + "format": "time_series", + "legendFormat": "{{status}}", + "refId": "A" + } + ], + "title": "Read requests / sec (gateway)", + "type": "timeseries" + }, + { + "datasource": { + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "custom": { + "drawStyle": "line", + "fillOpacity": 1, + "lineWidth": 1, + "pointSize": 5, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + } + }, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 12, + "y": 17 + }, + "id": 10, + "links": [], + "nullPointMode": "null as zero", + "options": { + "legend": { + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "(histogram_quantile(0.99, sum by (le) (service_route:http_request_duration_seconds_bucket:sum_rate{job=~\"(.*)((gateway|service-gw.*))\", route=~\"api_v1_query\"})) * 1e3) and on() (vector($latency_metrics) == 1)", + "format": "time_series", + "legendFormat": "99th percentile", + "refId": "A_classic" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "(histogram_quantile(0.99, sum (service_route:http_request_duration_seconds:sum_rate{job=~\"(.*)((gateway|service-gw.*))\", route=~\"api_v1_query\"})) * 1e3) and on() (vector($latency_metrics) == -1)", + "format": "time_series", + "legendFormat": "99th percentile", + "refId": "A_native" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "(histogram_quantile(0.50, sum by (le) (service_route:http_request_duration_seconds_bucket:sum_rate{job=~\"(.*)((gateway|service-gw.*))\", route=~\"api_v1_query\"})) * 1e3) and on() (vector($latency_metrics) == 1)", + "format": "time_series", + "legendFormat": "50th percentile", + "refId": "B_classic" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "(histogram_quantile(0.50, sum (service_route:http_request_duration_seconds:sum_rate{job=~\"(.*)((gateway|service-gw.*))\", route=~\"api_v1_query\"})) * 1e3) and on() (vector($latency_metrics) == -1)", + "format": "time_series", + "legendFormat": "50th percentile", + "refId": "B_native" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "(1e3 * sum(service_route:http_request_duration_seconds_sum:sum_rate{job=~\"(.*)((gateway|service-gw.*))\", route=~\"api_v1_query\"}) /\nsum(service_route:http_request_duration_seconds_count:sum_rate{job=~\"(.*)((gateway|service-gw.*))\", route=~\"api_v1_query\"})\n) and on() (vector($latency_metrics) == 1)", + "format": "time_series", + "legendFormat": "Average", + "refId": "C_classic" + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "(1e3 * sum(histogram_sum(service_route:http_request_duration_seconds:sum_rate{job=~\"(.*)((gateway|service-gw.*))\", route=~\"api_v1_query\"})) /\nsum(histogram_count(service_route:http_request_duration_seconds:sum_rate{job=~\"(.*)((gateway|service-gw.*))\", route=~\"api_v1_query\"}))\n) and on() (vector($latency_metrics) == -1)", + "format": "time_series", + "legendFormat": "Average", + "refId": "C_native" + } + ], + "title": "Read latency (gateway)", + "type": "timeseries" + }, + { + "datasource": { + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "custom": { + "drawStyle": "line", + "fillOpacity": 100, + "lineWidth": 0, + "pointSize": 5, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "normal" + } + }, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [] + }, + "unit": "reqps" + }, + "overrides": [ + { + "matcher": { + "id": "byRegexp", + "options": "/.*_api_v1_query($|[^_])/" + }, + "properties": [ + { + "id": "displayName", + "value": "instant queries" + }, + { + "id": "color", + "value": { + "fixedColor": "#429D48", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byRegexp", + "options": "/.*_api_v1_query_range($|[^_])/" + }, + "properties": [ + { + "id": "displayName", + "value": "range queries" + }, + { + "id": "color", + "value": { + "fixedColor": "#F1C731", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byRegexp", + "options": "/.*_api_v1_labels($|[^_])/" + }, + "properties": [ + { + "id": "displayName", + "value": "\"label names\" queries" + }, + { + "id": "color", + "value": { + "fixedColor": "#2A66CF", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byRegexp", + "options": "/.*_api_v1_label_name_values($|[^_])/" + }, + "properties": [ + { + "id": "displayName", + "value": "\"label values\" queries" + }, + { + "id": "color", + "value": { + "fixedColor": "#9E44C1", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byRegexp", + "options": "/.*_api_v1_series($|[^_])/" + }, + "properties": [ + { + "id": "displayName", + "value": "series queries" + }, + { + "id": "color", + "value": { + "fixedColor": "#FFAB57", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byRegexp", + "options": "/.*_api_v1_read($|[^_])/" + }, + "properties": [ + { + "id": "displayName", + "value": "remote read queries" + }, + { + "id": "color", + "value": { + "fixedColor": "#C79424", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byRegexp", + "options": "/.*_api_v1_metadata($|[^_])/" + }, + "properties": [ + { + "id": "displayName", + "value": "metadata queries" + }, + { + "id": "color", + "value": { + "fixedColor": "#84D586", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byRegexp", + "options": "/.*_api_v1_query_exemplars($|[^_])/" + }, + "properties": [ + { + "id": "displayName", + "value": "exemplar queries" + }, + { + "id": "color", + "value": { + "fixedColor": "#A1C4FC", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byRegexp", + "options": "/.*_api_v1_cardinality_active_series($|[^_])/" + }, + "properties": [ + { + "id": "displayName", + "value": "\"active series\" queries" + }, + { + "id": "color", + "value": { + "fixedColor": "#C788DE", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byRegexp", + "options": "/.*_api_v1_cardinality_label_names($|[^_])/" + }, + "properties": [ + { + "id": "displayName", + "value": "\"label name cardinality\" queries" + }, + { + "id": "color", + "value": { + "fixedColor": "#3F6833", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byRegexp", + "options": "/.*_api_v1_cardinality_label_values($|[^_])/" + }, + "properties": [ + { + "id": "displayName", + "value": "\"label value cardinality\" queries" + }, + { + "id": "color", + "value": { + "fixedColor": "#447EBC", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 18, + "y": 17 + }, + "id": 11, + "links": [], + "options": { + "legend": { + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "(sum by (route) (rate(http_request_duration_seconds_count{job=~\"(.*)/(query-frontend.*|service|metrics)\",route=~\"(prometheus|api_prom)(_api_v1_query|_api_v1_query_range|_api_v1_labels|_api_v1_label_name_values|_api_v1_series|_api_v1_read|_api_v1_metadata|_api_v1_query_exemplars|_api_v1_cardinality_active_series|_api_v1_cardinality_label_names|_api_v1_cardinality_label_values)\"}[$__rate_interval]))) and on() (vector($latency_metrics) == 1)", + "format": "time_series", + "legendLink": null + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "(sum by (route) (histogram_count(rate(http_request_duration_seconds{job=~\"(.*)/(query-frontend.*|service|metrics)\",route=~\"(prometheus|api_prom)(_api_v1_query|_api_v1_query_range|_api_v1_labels|_api_v1_label_name_values|_api_v1_series|_api_v1_read|_api_v1_metadata|_api_v1_query_exemplars|_api_v1_cardinality_active_series|_api_v1_cardinality_label_names|_api_v1_cardinality_label_values)\"}[$__rate_interval])))) and on() (vector($latency_metrics) == -1)", + "format": "time_series", + "legendLink": null + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "(sum (rate(http_request_duration_seconds_count{job=~\"(.*)/(query-frontend.*|service|metrics)\",route=~\"(prometheus|api_prom)_api_v1_.*\",route!~\"(prometheus|api_prom)(_api_v1_query|_api_v1_query_range|_api_v1_labels|_api_v1_label_name_values|_api_v1_series|_api_v1_read|_api_v1_metadata|_api_v1_query_exemplars|_api_v1_cardinality_active_series|_api_v1_cardinality_label_names|_api_v1_cardinality_label_values)\"}[$__rate_interval]))) and on() (vector($latency_metrics) == 1)", + "format": "time_series", + "legendFormat": "other", + "legendLink": null + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "(sum (histogram_count(rate(http_request_duration_seconds{job=~\"(.*)/(query-frontend.*|service|metrics)\",route=~\"(prometheus|api_prom)_api_v1_.*\",route!~\"(prometheus|api_prom)(_api_v1_query|_api_v1_query_range|_api_v1_labels|_api_v1_label_name_values|_api_v1_series|_api_v1_read|_api_v1_metadata|_api_v1_query_exemplars|_api_v1_cardinality_active_series|_api_v1_cardinality_label_names|_api_v1_cardinality_label_values)\"}[$__rate_interval])))) and on() (vector($latency_metrics) == -1)", + "format": "time_series", + "legendFormat": "other", + "legendLink": null + } + ], + "title": "Queries / sec", + "type": "timeseries" + }, + { + "collapsed": false, + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "gridPos": { + "h": 7, + "w": 24, + "x": 0, + "y": 16 + }, + "id": 22, + "panels": [], + "repeat": "", + "targets": [ + { + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "refId": "A" + } + ], + "title": "Reads", + "type": "row" + }, + { + "content": "These panels show an overview on the recording and alerting rules evaluation.\nTo examine the rules evaluation and alerts notifications in detail, see a specific dashboard:\n\n- \u003ca target=\"_blank\" href=\"./d/631e15d5d85afb2ca8e35d62984eeaa0/service-ruler?${__url_time_range}\u0026${__all_variables}\"\u003eRuler\u003c/a\u003e\n- \u003ca target=\"_blank\" href=\"./d/b0d38d318bbddd80476246d4930f9e55/service-alertmanager?${__url_time_range}\u0026${__all_variables}\"\u003eAlertmanager\u003c/a\u003e\n- \u003ca target=\"_blank\" href=\"./d/a6883fb22799ac74479c7db872451092/service-alertmanager-resources?${__url_time_range}\u0026${__all_variables}\"\u003eAlertmanager resources\u003c/a\u003e\n- \u003ca target=\"_blank\" href=\"./d/a9b92d3c4d1af325d872a9e9a7083d71/service-overview-resources?${__url_time_range}\u0026${__all_variables}\"\u003eOverview resources\u003c/a\u003e\n- \u003ca target=\"_blank\" href=\"./d/e15c71d372cc541367a088f10d9fcd92/service-overview-networking?${__url_time_range}\u0026${__all_variables}\"\u003eOverview networking\u003c/a\u003e\n", + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "description": "", + "gridPos": { + "h": 7, + "w": 6, + "x": 0, + "y": 25 + }, + "id": 12, + "mode": "markdown", + "targets": [ + { + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "refId": "A" + } + ], + "title": "", + "transparent": true, + "type": "text" + }, + { + "datasource": { + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "custom": { + "drawStyle": "line", + "fillOpacity": 1, + "lineWidth": 1, + "pointSize": 5, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + } + }, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [] + }, + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "failed" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#E24D42", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "success" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#7EB26D", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 6, + "y": 25 + }, + "id": 13, + "links": [], + "options": { + "legend": { + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum(rate(monitoring_rule_evaluations_total{service=~\"rule-engine\"}[$__rate_interval]))\n-\nsum(rate(monitoring_rule_evaluation_failures_total{service=~\"rule-engine\"}[$__rate_interval]))\n", + "format": "time_series", + "legendFormat": "success", + "legendLink": null + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum(rate(monitoring_rule_evaluation_failures_total{service=~\"rule-engine\"}[$__rate_interval]))", + "format": "time_series", + "legendFormat": "failed", + "legendLink": null + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum(rate(monitoring_rule_group_iterations_missed_total{service=~\"rule-engine\"}[$__rate_interval]))", + "format": "time_series", + "legendFormat": "missed", + "legendLink": null + } + ], + "title": "Rule evaluations / sec", + "type": "timeseries" + }, + { + "datasource": { + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "custom": { + "drawStyle": "line", + "fillOpacity": 1, + "lineWidth": 1, + "pointSize": 5, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + } + }, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 12, + "y": 25 + }, + "id": 14, + "links": [], + "options": { + "legend": { + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum (rate(monitoring_rule_evaluation_duration_seconds_sum{service=~\"rule-engine\"}[$__rate_interval]))\n /\nsum (rate(monitoring_rule_evaluation_duration_seconds_count{service=~\"rule-engine\"}[$__rate_interval]))\n", + "format": "time_series", + "legendFormat": "average", + "legendLink": null + } + ], + "title": "Rule evaluations latency", + "type": "timeseries" + }, + { + "datasource": { + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "custom": { + "drawStyle": "line", + "fillOpacity": 100, + "lineWidth": 0, + "pointSize": 5, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "normal" + } + }, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [] + }, + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "failed" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#E24D42", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "successful" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#7EB26D", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 18, + "y": 25 + }, + "id": 15, + "links": [], + "options": { + "legend": { + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum(rate(alert_notifications_sent_total{service=~\"rule-engine\"}[$__rate_interval]))\n -\nsum(rate(alert_notifications_errors_total{service=~\"rule-engine\"}[$__rate_interval]))\n", + "format": "time_series", + "legendFormat": "successful", + "legendLink": null + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum(rate(alert_notifications_errors_total{service=~\"rule-engine\"}[$__rate_interval]))\n", + "format": "time_series", + "legendFormat": "failed", + "legendLink": null + } + ], + "title": "Alerting notifications sent to Alertmanager / sec", + "type": "timeseries" + }, + { + "collapsed": false, + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "gridPos": { + "h": 7, + "w": 24, + "x": 0, + "y": 24 + }, + "id": 23, + "panels": [], + "repeat": "", + "targets": [ + { + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "refId": "A" + } + ], + "title": "Recording and alerting rules", + "type": "row" + }, + { + "content": "These panels show an overview on the long-term storage (object storage).\nTo examine the storage in detail, see a specific dashboard:\n\n- \u003ca target=\"_blank\" href=\"./d/e1324ee2a434f4158c00a9ee279d3292/service-object-store?${__url_time_range}\u0026${__all_variables}\"\u003eObject store\u003c/a\u003e\n- \u003ca target=\"_blank\" href=\"./d/1b3443aea86db629e6efdb7d05c53823/service-compactor?${__url_time_range}\u0026${__all_variables}\"\u003eCompactor\u003c/a\u003e\n", + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "description": "", + "gridPos": { + "h": 7, + "w": 6, + "x": 0, + "y": 33 + }, + "id": 16, + "mode": "markdown", + "targets": [ + { + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "refId": "A" + } + ], + "title": "", + "transparent": true, + "type": "text" + }, + { + "datasource": { + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "custom": { + "drawStyle": "line", + "fillOpacity": 100, + "lineWidth": 0, + "pointSize": 5, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "normal" + } + }, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [] + }, + "unit": "reqps" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "failed" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#E24D42", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "successful" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#7EB26D", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 6, + "y": 33 + }, + "id": 17, + "links": [], + "options": { + "legend": { + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum(rate(storage_operations_total{env!=\"\"}[$__rate_interval]))\n-\nsum(rate(storage_operation_failures_total{env!=\"\"}[$__rate_interval]))\n", + "format": "time_series", + "legendFormat": "successful", + "legendLink": null + }, + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum(rate(storage_operation_failures_total{env!=\"\"}[$__rate_interval]))\n", + "format": "time_series", + "legendFormat": "failed", + "legendLink": null + } + ], + "title": "Requests / sec", + "type": "timeseries" + }, + { + "datasource": { + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "custom": { + "drawStyle": "line", + "fillOpacity": 100, + "lineWidth": 0, + "pointSize": 5, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "normal" + } + }, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [] + }, + "unit": "reqps" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 12, + "y": 33 + }, + "id": 18, + "links": [], + "options": { + "legend": { + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "seriesOverrides": [ + { + "alias": "attributes", + "color": "#429D48" + }, + { + "alias": "delete", + "color": "#F1C731" + }, + { + "alias": "exists", + "color": "#2A66CF" + }, + { + "alias": "get", + "color": "#9E44C1" + }, + { + "alias": "get_range", + "color": "#FFAB57" + }, + { + "alias": "iter", + "color": "#C79424" + }, + { + "alias": "upload", + "color": "#84D586" + } + ], + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum by(operation) (rate(storage_operations_total{env!=\"\"}[$__rate_interval]))", + "format": "time_series", + "legendFormat": "{{operation}}", + "legendLink": null + } + ], + "title": "Operations / sec", + "type": "timeseries" + }, + { + "datasource": { + "uid": "$datasource" + }, + "fieldConfig": { + "defaults": { + "custom": { + "drawStyle": "line", + "fillOpacity": 1, + "lineWidth": 1, + "pointSize": 5, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + } + }, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 18, + "y": 33 + }, + "id": 19, + "links": [], + "options": { + "legend": { + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "uid": "$datasource" + }, + "expr": "sum(max by(user) (max_over_time(storage_blocks_count{job=~\"(.*)/(compactor.*|service|metrics)\"}[15m])))", + "format": "time_series", + "legendFormat": "blocks", + "legendLink": null + } + ], + "title": "Total number of blocks in the storage", + "type": "timeseries" + }, + { + "collapsed": false, + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "gridPos": { + "h": 7, + "w": 24, + "x": 0, + "y": 32 + }, + "id": 24, + "panels": [], + "repeat": "", + "targets": [ + { + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "refId": "A" + } + ], + "title": "Long-term storage (object storage)", + "type": "row" + } + ], + "refresh": "5m", + "schemaVersion": 41, + "style": "dark", + "tags": [ + "metrics", + "scoped", + "as-code" + ], + "templating": { + "list": [ + { + "name": "datasource", + "options": [], + "query": "prometheus", + "refresh": 1, + "regex": "/(dev-prometheus|ops-prometheus|service-prometheus|critical-prometheus)/", + "type": "datasource" + }, + { + "current": { + "selected": true, + "text": "classic", + "value": "1" + }, + "description": "Choose between showing latencies based on low precision classic or high precision native histogram metrics.", + "hide": 0, + "includeAll": false, + "label": "Latency metrics", + "multi": false, + "name": "latency_metrics", + "options": [ + { + "selected": false, + "text": "native", + "value": "-1" + }, + { + "selected": true, + "text": "classic", + "value": "1" + } + ], + "query": "native : -1,classic : 1", + "skipUrlSync": false, + "type": "custom" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${datasource}" + }, + "filters": [], + "name": "filters", + "type": "adhoc" + } + ] + }, + "time": { + "from": "now-1h", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ] + }, + "timezone": "utc", + "title": "Service Overview Dashboard", + "uid": "e540f8b0ce5b02335ec443a769d1a74e", + "version": 0 +} \ No newline at end of file From 5724fae7784760a86d01345ec200be90a3a6c9e6 Mon Sep 17 00:00:00 2001 From: Moustafa Baiou Date: Tue, 26 Aug 2025 08:20:47 -0400 Subject: [PATCH 03/24] Alerting: Add Extended List Query for Alert Rules w/pagination (#109360) * Alerting: Add Extended List Query for Alert Rules w/pagination This adds an extended query which allows filtering by the kind of rule (Recording or Alerting) and supports pagination. Pagination tokens will allow us to list all rules paginated, regardless of the rule group. --------- Co-authored-by: William Wernert --- pkg/services/ngalert/models/alert_rule.go | 9 + .../ngalert/provisioning/accesscontrol.go | 1 + .../ngalert/provisioning/alert_rules.go | 61 ++++++ .../ngalert/provisioning/alert_rules_test.go | 93 +++++++++ pkg/services/ngalert/provisioning/persist.go | 1 + pkg/services/ngalert/provisioning/testing.go | 9 + pkg/services/ngalert/store/alert_rule.go | 181 ++++++++++++++---- pkg/services/ngalert/store/alert_rule_test.go | 140 ++++++++++++++ pkg/services/ngalert/store/models.go | 4 +- pkg/services/ngalert/tests/fakes/rules.go | 16 ++ 10 files changed, 474 insertions(+), 41 deletions(-) diff --git a/pkg/services/ngalert/models/alert_rule.go b/pkg/services/ngalert/models/alert_rule.go index f888a2f8467..7d31ac44090 100644 --- a/pkg/services/ngalert/models/alert_rule.go +++ b/pkg/services/ngalert/models/alert_rule.go @@ -939,6 +939,15 @@ type ListAlertRulesQuery struct { HasPrometheusRuleDefinition *bool } +type ListAlertRulesExtendedQuery struct { + ListAlertRulesQuery + + RuleType RuleTypeFilter + + Limit int64 + ContinueToken string +} + // CountAlertRulesQuery is the query for counting alert rules type CountAlertRulesQuery struct { OrgID int64 diff --git a/pkg/services/ngalert/provisioning/accesscontrol.go b/pkg/services/ngalert/provisioning/accesscontrol.go index f7daec33459..3b451b8a9b7 100644 --- a/pkg/services/ngalert/provisioning/accesscontrol.go +++ b/pkg/services/ngalert/provisioning/accesscontrol.go @@ -14,6 +14,7 @@ type RuleAccessControlService interface { AuthorizeAccessToRuleGroup(ctx context.Context, user identity.Requester, rules models.RulesGroup) error AuthorizeAccessInFolder(ctx context.Context, user identity.Requester, namespaced models.Namespaced) error AuthorizeRuleChanges(ctx context.Context, user identity.Requester, change *store.GroupDelta) error + HasAccessInFolder(ctx context.Context, user identity.Requester, folder models.Namespaced) (bool, error) } func newRuleAccessControlService(ac RuleAccessControlService) *provisioningRuleAccessControl { diff --git a/pkg/services/ngalert/provisioning/alert_rules.go b/pkg/services/ngalert/provisioning/alert_rules.go index 016fbc147f6..b4efe1bc572 100644 --- a/pkg/services/ngalert/provisioning/alert_rules.go +++ b/pkg/services/ngalert/provisioning/alert_rules.go @@ -28,6 +28,7 @@ type ruleAccessControlService interface { CanReadAllRules(ctx context.Context, user identity.Requester) (bool, error) // CanWriteAllRules returns true if the user has full access to write rules via provisioning API and bypass regular checks CanWriteAllRules(ctx context.Context, user identity.Requester) (bool, error) + HasAccessInFolder(ctx context.Context, user identity.Requester, folder models.Namespaced) (bool, error) } var errProvenanceMismatch = errutil.NewBase(errutil.StatusConflict, "alerting.provenanceMismatch").MustTemplate( @@ -80,6 +81,66 @@ func NewAlertRuleService(ruleStore RuleStore, } } +type ListAlertRulesOptions struct { + RuleType models.RuleTypeFilter + Limit int64 + ContinueToken string + // TODO: plumb more options +} + +func (service *AlertRuleService) ListAlertRules(ctx context.Context, user identity.Requester, opts ListAlertRulesOptions) (rules []*models.AlertRule, provenances map[string]models.Provenance, nextToken string, err error) { + q := models.ListAlertRulesExtendedQuery{ + ListAlertRulesQuery: models.ListAlertRulesQuery{ + OrgID: user.GetOrgID(), + }, + RuleType: opts.RuleType, + Limit: opts.Limit, + ContinueToken: opts.ContinueToken, + } + + can, err := service.authz.CanReadAllRules(ctx, user) + if err != nil { + return nil, nil, "", err + } + // If user does not have blanket privilege to read rules, filter to only folders they have rule access to + if !can { + fq := folder.GetFoldersQuery{ + OrgID: user.GetOrgID(), + SignedInUser: user, + } + folders, err := service.folderService.GetFolders(ctx, fq) + if err != nil { + return nil, nil, "", err + } + folderUIDs := make([]string, 0, len(folders)) + for _, f := range folders { + access, err := service.authz.HasAccessInFolder(ctx, user, models.Namespace(*f.ToFolderReference())) + if err != nil { + return nil, nil, "", err + } + if access { + folderUIDs = append(folderUIDs, f.UID) + } + } + q.NamespaceUIDs = folderUIDs + } + + rules, nextToken, err = service.ruleStore.ListAlertRulesPaginated(ctx, &q) + if err != nil { + return nil, nil, "", err + } + provenances = make(map[string]models.Provenance) + if len(rules) > 0 { + resourceType := rules[0].ResourceType() + provenances, err = service.provenanceStore.GetProvenances(ctx, user.GetOrgID(), resourceType) + if err != nil { + return nil, nil, "", err + } + } + + return rules, provenances, nextToken, nil +} + func (service *AlertRuleService) GetAlertRules(ctx context.Context, user identity.Requester) ([]*models.AlertRule, map[string]models.Provenance, error) { q := models.ListAlertRulesQuery{ OrgID: user.GetOrgID(), diff --git a/pkg/services/ngalert/provisioning/alert_rules_test.go b/pkg/services/ngalert/provisioning/alert_rules_test.go index ea488b83971..359a09e2e33 100644 --- a/pkg/services/ngalert/provisioning/alert_rules_test.go +++ b/pkg/services/ngalert/provisioning/alert_rules_test.go @@ -1423,6 +1423,99 @@ func TestGetRuleGroup(t *testing.T) { }) } +func TestListAlertRules(t *testing.T) { + orgID := rand.Int63() + u := &user.SignedInUser{OrgID: orgID} + groupKey1 := models.GenerateGroupKey(orgID) + groupKey2 := models.GenerateGroupKey(orgID) + gen := models.RuleGen + rules1 := gen.With(gen.WithGroupKey(groupKey1), gen.WithUniqueGroupIndex()).GenerateManyRef(3) + models.RulesGroup(rules1).SortByGroupIndex() + rules2 := gen.With(gen.WithGroupKey(groupKey2), gen.WithUniqueGroupIndex()).GenerateManyRef(4) + models.RulesGroup(rules2).SortByGroupIndex() + allRules := append(rules1, rules2...) + + fs := foldertest.NewFakeService() + fs.AddFolder(&folder.Folder{ + OrgID: orgID, + UID: groupKey1.NamespaceUID, + Title: "folder1", + }) + fs.AddFolder(&folder.Folder{ + OrgID: orgID, + UID: groupKey2.NamespaceUID, + Title: "folder2", + }) + + initServiceWithData := func(t *testing.T) (*AlertRuleService, *fakes.RuleStore, *fakes.FakeProvisioningStore, *fakeRuleAccessControlService) { + service, ruleStore, provenanceStore, ac := initService(t) + service.folderService = fs + ruleStore.Rules = map[int64][]*models.AlertRule{ + orgID: allRules, + } + ac.HasAccessInFolderFunc = func(ctx context.Context, user identity.Requester, folder models.Namespaced) (bool, error) { + return true, nil + } + + return service, ruleStore, provenanceStore, ac + } + + t.Run("when user can read all rules", func(t *testing.T) { + t.Run("should skip AuthorizeRuleGroupRead and return all rules", func(t *testing.T) { + service, _, _, ac := initServiceWithData(t) + ac.CanReadAllRulesFunc = func(ctx context.Context, user identity.Requester) (bool, error) { + return true, nil + } + + rules, _, token, err := service.ListAlertRules(context.Background(), u, ListAlertRulesOptions{}) + require.NoError(t, err) + // check that rules contain all uids from allRules + ruleUIDs := make(map[string]bool) + for _, r := range rules { + ruleUIDs[r.UID] = true + } + for _, r := range allRules { + assert.True(t, ruleUIDs[r.UID]) + } + require.Len(t, ruleUIDs, len(allRules)) + require.Empty(t, token) + + assert.Len(t, ac.Calls, 1) + assert.Equal(t, "CanReadAllRules", ac.Calls[0].Method) + }) + }) + + t.Run("when user cannot read all rules", func(t *testing.T) { + t.Run("should return only rules in accessible folders", func(t *testing.T) { + service, _, _, ac := initServiceWithData(t) + ac.CanReadAllRulesFunc = func(ctx context.Context, user identity.Requester) (bool, error) { + return false, nil + } + ac.HasAccessInFolderFunc = func(ctx context.Context, user identity.Requester, folder models.Namespaced) (bool, error) { + return folder.GetNamespaceUID() == groupKey2.NamespaceUID, nil + } + + rules, _, token, err := service.ListAlertRules(context.Background(), u, ListAlertRulesOptions{}) + require.NoError(t, err) + // check that rules contain all uids from rules1 + ruleUIDs := make(map[string]bool) + for _, r := range rules { + ruleUIDs[r.UID] = true + } + for _, r := range rules2 { + assert.True(t, ruleUIDs[r.UID]) + } + require.Len(t, ruleUIDs, len(rules2)) + require.Empty(t, token) + + assert.Len(t, ac.Calls, 3) + assert.Equal(t, "CanReadAllRules", ac.Calls[0].Method) + assert.Equal(t, "HasAccessInFolder", ac.Calls[1].Method) + assert.Equal(t, "HasAccessInFolder", ac.Calls[2].Method) + }) + }) +} + func TestGetAlertRules(t *testing.T) { orgID := rand.Int63() u := &user.SignedInUser{OrgID: orgID} diff --git a/pkg/services/ngalert/provisioning/persist.go b/pkg/services/ngalert/provisioning/persist.go index 6b3c2fb00d6..753c874cce3 100644 --- a/pkg/services/ngalert/provisioning/persist.go +++ b/pkg/services/ngalert/provisioning/persist.go @@ -32,6 +32,7 @@ type TransactionManager interface { type RuleStore interface { GetAlertRuleByUID(ctx context.Context, query *models.GetAlertRuleByUIDQuery) (*models.AlertRule, error) ListAlertRules(ctx context.Context, query *models.ListAlertRulesQuery) (models.RulesGroup, error) + ListAlertRulesPaginated(ctx context.Context, query *models.ListAlertRulesExtendedQuery) (models.RulesGroup, string, error) GetRuleGroupInterval(ctx context.Context, orgID int64, namespaceUID string, ruleGroup string) (int64, error) InsertAlertRules(ctx context.Context, user *models.UserUID, rule []models.AlertRule) ([]models.AlertRuleKeyWithId, error) UpdateAlertRules(ctx context.Context, user *models.UserUID, rule []models.UpdateRule) error diff --git a/pkg/services/ngalert/provisioning/testing.go b/pkg/services/ngalert/provisioning/testing.go index beefd0287fc..059ef542d39 100644 --- a/pkg/services/ngalert/provisioning/testing.go +++ b/pkg/services/ngalert/provisioning/testing.go @@ -113,6 +113,7 @@ type fakeRuleAccessControlService struct { AuthorizeRuleChangesFunc func(ctx context.Context, user identity.Requester, change *store.GroupDelta) error CanReadAllRulesFunc func(ctx context.Context, user identity.Requester) (bool, error) CanWriteAllRulesFunc func(ctx context.Context, user identity.Requester) (bool, error) + HasAccessInFolderFunc func(ctx context.Context, user identity.Requester, folder models.Namespaced) (bool, error) } func (s *fakeRuleAccessControlService) RecordCall(method string, args ...interface{}) { @@ -167,6 +168,14 @@ func (s *fakeRuleAccessControlService) CanWriteAllRules(ctx context.Context, use return false, nil } +func (s *fakeRuleAccessControlService) HasAccessInFolder(ctx context.Context, user identity.Requester, folder models.Namespaced) (bool, error) { + s.RecordCall("HasAccessInFolder", ctx, user, folder) + if s.HasAccessInFolderFunc != nil { + return s.HasAccessInFolderFunc(ctx, user, folder) + } + return true, nil +} + type fakeAlertRuleNotificationStore struct { Calls []call diff --git a/pkg/services/ngalert/store/alert_rule.go b/pkg/services/ngalert/store/alert_rule.go index ab8676c754d..6a19a31ff05 100644 --- a/pkg/services/ngalert/store/alert_rule.go +++ b/pkg/services/ngalert/store/alert_rule.go @@ -2,14 +2,16 @@ package store import ( "context" + "encoding/base64" "encoding/json" "errors" "fmt" + "math" + "slices" "strings" "github.com/google/uuid" "golang.org/x/exp/maps" - "golang.org/x/exp/slices" "github.com/grafana/grafana/pkg/util/xorm" @@ -762,8 +764,23 @@ func shouldIncludeRule(rule *ngmodels.AlertRule, query *ngmodels.ListAlertRulesB return true } -// ListAlertRules is a handler for retrieving alert rules of specific organisation. func (st DBstore) ListAlertRules(ctx context.Context, query *ngmodels.ListAlertRulesQuery) (result ngmodels.RulesGroup, err error) { + result, nextToken, err := st.ListAlertRulesPaginated(ctx, &ngmodels.ListAlertRulesExtendedQuery{ + ListAlertRulesQuery: *query, + ContinueToken: "", + Limit: 0, + RuleType: ngmodels.RuleTypeFilterAll, + }) + // This should never happen, as Limit is 0, which means no pagination. + if nextToken != "" { + err = fmt.Errorf("unexpected next token %q, expected empty string", nextToken) + st.Logger.Error("ListAlertRules returned a next token, but it should not have, this is a bug!", "next_token", nextToken, "query", query) + } + return result, err +} + +// ListAlertRulesPaginated is a handler for retrieving alert rules of specific organization paginated. +func (st DBstore) ListAlertRulesPaginated(ctx context.Context, query *ngmodels.ListAlertRulesExtendedQuery) (result ngmodels.RulesGroup, nextToken string, err error) { err = st.SQLStore.WithDbSession(ctx, func(sess *db.Session) error { q := sess.Table("alert_rule") @@ -819,8 +836,37 @@ func (st DBstore) ListAlertRules(ctx context.Context, query *ngmodels.ListAlertR } } + // FIXME: record is nullable but we don't save it as null when it's nil + switch query.RuleType { + case ngmodels.RuleTypeFilterAlerting: + q = q.Where("record = ''") + case ngmodels.RuleTypeFilterRecording: + q = q.Where("record != ''") + case ngmodels.RuleTypeFilterAll: + // no additional filter + default: + return fmt.Errorf("unknown rule type filter %q", query.RuleType) + } + q = q.Asc("namespace_uid", "rule_group", "rule_group_idx", "id") + if query.ContinueToken != "" { + cursor, err := decodeCursor(query.ContinueToken) + if err != nil { + return fmt.Errorf("invalid continue token: %w", err) + } + + // Build cursor condition that matches the ORDER BY clause + q = buildCursorCondition(q, cursor) + } + + if query.Limit > 0 { + // Ensure we clamp to the max int available on the platform + lim := min(query.Limit, math.MaxInt) + // Fetch one extra rule to determine if there are more results + q = q.Limit(int(lim) + 1) + } + alertRules := make([]*ngmodels.AlertRule, 0) rule := new(alertRule) rows, err := q.Rows(rule) @@ -833,50 +879,107 @@ func (st DBstore) ListAlertRules(ctx context.Context, query *ngmodels.ListAlertR // Deserialize each rule separately in case any of them contain invalid JSON. for rows.Next() { - rule := new(alertRule) - err = rows.Scan(rule) - if err != nil { - st.Logger.Error("Invalid rule found in DB store, ignoring it", "func", "ListAlertRules", "error", err) - continue + converted, ok := st.handleRuleRow(rows, query, groupsMap) + if ok { + alertRules = append(alertRules, converted) } - converted, err := alertRuleToModelsAlertRule(*rule, st.Logger) - if err != nil { - st.Logger.Error("Invalid rule found in DB store, cannot convert, ignoring it", "func", "ListAlertRules", "error", err) - continue + } + + genToken := query.Limit > 0 && len(alertRules) > int(query.Limit) + if genToken { + // Remove the extra item we fetched + alertRules = alertRules[:query.Limit] + + // Generate next continue token from the last item + lastRule := alertRules[len(alertRules)-1] + cursor := continueCursor{ + NamespaceUID: lastRule.NamespaceUID, + RuleGroup: lastRule.RuleGroup, + RuleGroupIdx: int64(lastRule.RuleGroupIndex), + ID: lastRule.ID, } - if query.ReceiverName != "" { // remove false-positive hits from the result - if !slices.ContainsFunc(converted.NotificationSettings, func(settings ngmodels.NotificationSettings) bool { - return settings.Receiver == query.ReceiverName - }) { - continue - } - } - if query.TimeIntervalName != "" { - if !slices.ContainsFunc(converted.NotificationSettings, func(settings ngmodels.NotificationSettings) bool { - return slices.Contains(settings.MuteTimeIntervals, query.TimeIntervalName) || slices.Contains(settings.ActiveTimeIntervals, query.TimeIntervalName) - }) { - continue - } - } - if query.HasPrometheusRuleDefinition != nil { // remove false-positive hits from the result - if *query.HasPrometheusRuleDefinition != converted.HasPrometheusRuleDefinition() { - continue - } - } - // MySQL (and potentially other databases) can use case-insensitive comparison. - // This code makes sure we return groups that only exactly match the filter. - if groupsMap != nil { - if _, ok := groupsMap[converted.RuleGroup]; !ok { - continue - } - } - alertRules = append(alertRules, &converted) + + nextToken = encodeCursor(cursor) } result = alertRules return nil }) - return result, err + return result, nextToken, err +} + +func (st DBstore) handleRuleRow(rows *xorm.Rows, query *ngmodels.ListAlertRulesExtendedQuery, groupsSet map[string]struct{}) (*ngmodels.AlertRule, bool) { + rule := new(alertRule) + err := rows.Scan(rule) + if err != nil { + st.Logger.Error("Invalid rule found in DB store, ignoring it", "func", "ListAlertRules", "error", err) + return nil, false + } + converted, err := alertRuleToModelsAlertRule(*rule, st.Logger) + if err != nil { + st.Logger.Error("Invalid rule found in DB store, cannot convert, ignoring it", "func", "ListAlertRules", "error", err) + return nil, false + } + if query.ReceiverName != "" { // remove false-positive hits from the result + if !slices.ContainsFunc(converted.NotificationSettings, func(settings ngmodels.NotificationSettings) bool { + return settings.Receiver == query.ReceiverName + }) { + return nil, false + } + } + if query.TimeIntervalName != "" { + if !slices.ContainsFunc(converted.NotificationSettings, func(settings ngmodels.NotificationSettings) bool { + return slices.Contains(settings.MuteTimeIntervals, query.TimeIntervalName) || slices.Contains(settings.ActiveTimeIntervals, query.TimeIntervalName) + }) { + return nil, false + } + } + if query.HasPrometheusRuleDefinition != nil { // remove false-positive hits from the result + if *query.HasPrometheusRuleDefinition != converted.HasPrometheusRuleDefinition() { + return nil, false + } + } + // MySQL (and potentially other databases) can use case-insensitive comparison. + // This code makes sure we return groups that only exactly match the filter. + if groupsSet != nil { + if _, ok := groupsSet[converted.RuleGroup]; !ok { + return nil, false + } + } + return &converted, true +} + +type continueCursor struct { + NamespaceUID string `json:"n"` + RuleGroup string `json:"g"` + RuleGroupIdx int64 `json:"i"` + ID int64 `json:"d"` +} + +func encodeCursor(c continueCursor) string { + data, _ := json.Marshal(c) + return base64.URLEncoding.EncodeToString(data) +} + +func decodeCursor(token string) (continueCursor, error) { + var c continueCursor + data, err := base64.URLEncoding.DecodeString(token) + if err != nil { + return c, fmt.Errorf("failed to decode token: %w", err) + } + + if err := json.Unmarshal(data, &c); err != nil { + return c, fmt.Errorf("failed to unmarshal cursor: %w", err) + } + + return c, nil +} + +func buildCursorCondition(sess *xorm.Session, c continueCursor) *xorm.Session { + return sess.Where("(namespace_uid > ?)", c.NamespaceUID). + Or("(namespace_uid = ? AND rule_group > ?)", c.NamespaceUID, c.RuleGroup). + Or("(namespace_uid = ? AND rule_group = ? AND rule_group_idx > ?)", c.NamespaceUID, c.RuleGroup, c.RuleGroupIdx). + Or("(namespace_uid = ? AND rule_group = ? AND rule_group_idx = ? AND id > ?)", c.NamespaceUID, c.RuleGroup, c.RuleGroupIdx, c.ID) } // Count returns either the number of the alert rules under a specific org (if orgID is not zero) diff --git a/pkg/services/ngalert/store/alert_rule_test.go b/pkg/services/ngalert/store/alert_rule_test.go index c3031528013..1e0119fe3fa 100644 --- a/pkg/services/ngalert/store/alert_rule_test.go +++ b/pkg/services/ngalert/store/alert_rule_test.go @@ -1956,6 +1956,146 @@ func TestIntegration_ListAlertRules(t *testing.T) { }) } +func TestIntegration_ListAlertRulesPaginated(t *testing.T) { + if testing.Short() { + t.Skip("skipping integration test") + } + sqlStore := db.InitTestDB(t) + cfg := setting.NewCfg() + cfg.UnifiedAlerting = setting.UnifiedAlertingSettings{ + BaseInterval: time.Duration(rand.Int64N(100)) * time.Second, + } + folderService := setupFolderService(t, sqlStore, cfg, featuremgmt.WithFeatures()) + b := &fakeBus{} + orgID := int64(1) + ruleGen := models.RuleGen + ruleGen = ruleGen.With( + ruleGen.WithIntervalMatching(cfg.UnifiedAlerting.BaseInterval), + ruleGen.WithOrgID(orgID), + ) + t.Run("filter by RuleType", func(t *testing.T) { + store := createTestStore(sqlStore, folderService, &logtest.Fake{}, cfg.UnifiedAlerting, b) + alertingGen := ruleGen + recordingGen := ruleGen.With(models.RuleMuts.WithAllRecordingRules(), models.RuleMuts.WithMetric("metric1"), models.RuleMuts.WithRecordFrom("A")) + + alertingRules := []*models.AlertRule{ + createRule(t, store, alertingGen), + createRule(t, store, alertingGen), + } + recordingRules := []*models.AlertRule{ + createRule(t, store, recordingGen), + createRule(t, store, recordingGen), + } + + t.Run("should return only alerting rules", func(t *testing.T) { + query := &models.ListAlertRulesExtendedQuery{ + ListAlertRulesQuery: models.ListAlertRulesQuery{ + OrgID: orgID, + }, + RuleType: models.RuleTypeFilterAlerting, + } + result, continueToken, err := store.ListAlertRulesPaginated(context.Background(), query) + require.NoError(t, err) + require.Empty(t, continueToken, "continue token should be empty when no pagination is applied") + require.NotEmpty(t, result) + for _, rule := range result { + require.Equal(t, models.RuleTypeAlerting, rule.Type()) + } + }) + + t.Run("should return only recording rules", func(t *testing.T) { + query := &models.ListAlertRulesExtendedQuery{ + ListAlertRulesQuery: models.ListAlertRulesQuery{ + OrgID: orgID, + }, + RuleType: models.RuleTypeFilterRecording, + } + result, continueToken, err := store.ListAlertRulesPaginated(context.Background(), query) + require.NoError(t, err) + require.Empty(t, continueToken, "continue token should be empty when no pagination is applied") + require.NotEmpty(t, result) + for _, rule := range result { + require.Equal(t, models.RuleTypeRecording, rule.Type()) + } + }) + + t.Run("should return both alerting and recording rules when RuleType is not set", func(t *testing.T) { + query := &models.ListAlertRulesExtendedQuery{ + ListAlertRulesQuery: models.ListAlertRulesQuery{ + OrgID: orgID, + }, + } + result, continueToken, err := store.ListAlertRulesPaginated(context.Background(), query) + require.NoError(t, err) + require.Empty(t, continueToken, "continue token should be empty when no pagination is applied") + require.NotEmpty(t, result) + var alertingCount, recordingCount int + for _, rule := range result { + switch rule.Type() { + case models.RuleTypeAlerting: + alertingCount++ + case models.RuleTypeRecording: + recordingCount++ + } + } + require.GreaterOrEqual(t, alertingCount, len(alertingRules)) + require.GreaterOrEqual(t, recordingCount, len(recordingRules)) + }) + t.Run("should return both alerting and recording rules when RuleType is all", func(t *testing.T) { + query := &models.ListAlertRulesExtendedQuery{ + ListAlertRulesQuery: models.ListAlertRulesQuery{ + OrgID: orgID, + }, + RuleType: models.RuleTypeFilterAll, + } + result, continueToken, err := store.ListAlertRulesPaginated(context.Background(), query) + require.NoError(t, err) + require.Empty(t, continueToken, "continue token should be empty when no pagination is applied") + require.NotEmpty(t, result) + var alertingCount, recordingCount int + for _, rule := range result { + switch rule.Type() { + case models.RuleTypeAlerting: + alertingCount++ + case models.RuleTypeRecording: + recordingCount++ + } + } + require.GreaterOrEqual(t, alertingCount, len(alertingRules)) + require.GreaterOrEqual(t, recordingCount, len(recordingRules)) + }) + }) + t.Run("list rules with pagination", func(t *testing.T) { + store := createTestStore(sqlStore, folderService, &logtest.Fake{}, cfg.UnifiedAlerting, b) + alertingGen := ruleGen.With(ruleGen.WithNamespaceUID("paginate-test")) + for i := 0; i < 10; i++ { + createRule(t, store, alertingGen) + } + t.Run("should return paginated results", func(t *testing.T) { + query := &models.ListAlertRulesExtendedQuery{ + ListAlertRulesQuery: models.ListAlertRulesQuery{ + OrgID: orgID, + NamespaceUIDs: []string{"paginate-test"}, + }, + Limit: 5, // set page size to 5 + } + result, continueToken, err := store.ListAlertRulesPaginated(context.Background(), query) + require.NoError(t, err) + require.Len(t, result, 5, "should return 5 rules as per page size") + require.NotEmpty(t, continueToken, "continue token should not be empty for paginated results") + + // continue with the next page + query.ContinueToken = continueToken + result2, continueToken, err := store.ListAlertRulesPaginated(context.Background(), query) + require.NoError(t, err) + require.Len(t, result2, 5, "should return next 5 rules") + require.Empty(t, continueToken, "continue token should be empty when all rules are fetched") + + require.NotElementsMatch(t, result, result2, "should not have same rules in both pages") + }) + }) +} + func TestIntegration_ListDeletedRules(t *testing.T) { if testing.Short() { t.Skip("skipping integration test") diff --git a/pkg/services/ngalert/store/models.go b/pkg/services/ngalert/store/models.go index f7b6858a86e..775f6f54542 100644 --- a/pkg/services/ngalert/store/models.go +++ b/pkg/services/ngalert/store/models.go @@ -19,8 +19,8 @@ type alertRule struct { DashboardUID *string `xorm:"dashboard_uid"` PanelID *int64 `xorm:"panel_id"` RuleGroup string - RuleGroupIndex int `xorm:"rule_group_idx"` - Record string + RuleGroupIndex int `xorm:"rule_group_idx"` + Record string // FIXME: record is nullable but we don't save it as null when it's nil NoDataState string ExecErrState string For time.Duration diff --git a/pkg/services/ngalert/tests/fakes/rules.go b/pkg/services/ngalert/tests/fakes/rules.go index 8e7d92666a5..955f2c8091f 100644 --- a/pkg/services/ngalert/tests/fakes/rules.go +++ b/pkg/services/ngalert/tests/fakes/rules.go @@ -268,6 +268,22 @@ func (f *RuleStore) ListAlertRulesByGroup(_ context.Context, q *models.ListAlert return outputRules, nextToken, nil } +// TODO: implement pagination for this fake +func (f *RuleStore) ListAlertRulesPaginated(_ context.Context, q *models.ListAlertRulesExtendedQuery) (models.RulesGroup, string, error) { + f.mtx.Lock() + defer f.mtx.Unlock() + f.RecordedOps = append(f.RecordedOps, *q) + + if err := f.Hook(*q); err != nil { + return nil, "", err + } + rules, err := f.listAlertRules(&q.ListAlertRulesQuery) + if err != nil { + return nil, "", err + } + return rules, "", nil +} + func (f *RuleStore) ListAlertRules(_ context.Context, q *models.ListAlertRulesQuery) (models.RulesGroup, error) { f.mtx.Lock() defer f.mtx.Unlock() From 145577831ba20791529f413be9f9e2c0ac74ec2e Mon Sep 17 00:00:00 2001 From: Andres Martinez Gotor Date: Tue, 26 Aug 2025 14:45:27 +0200 Subject: [PATCH 04/24] Chore: fix betterer issues (#110100) --- .betterer.results | 10 ------- .../src/utils/useFavoriteDatasources.ts | 28 +++++++++++-------- 2 files changed, 16 insertions(+), 22 deletions(-) diff --git a/.betterer.results b/.betterer.results index 38ab3e6a0d5..e0aca642544 100644 --- a/.betterer.results +++ b/.betterer.results @@ -548,16 +548,6 @@ exports[`better eslint`] = { "packages/grafana-runtime/src/utils/queryResponse.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], - "packages/grafana-runtime/src/utils/useFavoriteDatasources.ts:5381": [ - [0, 0, 0, "React Hook \\"useCallback\\" is called conditionally. React Hooks must be called in the exact same order in every component render. Did you accidentally call a React Hook after an early return?", "0"], - [0, 0, 0, "React Hook \\"useCallback\\" is called conditionally. React Hooks must be called in the exact same order in every component render. Did you accidentally call a React Hook after an early return?", "1"], - [0, 0, 0, "React Hook \\"useCallback\\" is called conditionally. React Hooks must be called in the exact same order in every component render. Did you accidentally call a React Hook after an early return?", "2"], - [0, 0, 0, "React Hook \\"useCallback\\" is called conditionally. React Hooks must be called in the exact same order in every component render. Did you accidentally call a React Hook after an early return?", "3"], - [0, 0, 0, "React Hook \\"useEffect\\" is called conditionally. React Hooks must be called in the exact same order in every component render. Did you accidentally call a React Hook after an early return?", "4"], - [0, 0, 0, "React Hook \\"useState\\" is called conditionally. React Hooks must be called in the exact same order in every component render. Did you accidentally call a React Hook after an early return?", "5"], - [0, 0, 0, "React Hook \\"useState\\" is called conditionally. React Hooks must be called in the exact same order in every component render. Did you accidentally call a React Hook after an early return?", "6"], - [0, 0, 0, "React Hook \\"useState\\" is called conditionally. React Hooks must be called in the exact same order in every component render. Did you accidentally call a React Hook after an early return?", "7"] - ], "packages/grafana-runtime/src/utils/userStorage.tsx:5381": [ [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "0"], [0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "1"], diff --git a/packages/grafana-runtime/src/utils/useFavoriteDatasources.ts b/packages/grafana-runtime/src/utils/useFavoriteDatasources.ts index 20233bbf1c7..29cc5caa55c 100644 --- a/packages/grafana-runtime/src/utils/useFavoriteDatasources.ts +++ b/packages/grafana-runtime/src/utils/useFavoriteDatasources.ts @@ -36,24 +36,16 @@ const userStorage = new UserStorage('grafana-runtime'); * @public */ export function useFavoriteDatasources(): FavoriteDatasources { - if (!config.featureToggles.favoriteDatasources) { - return { - enabled: false, - isLoading: false, - favoriteDatasources: [], - initialFavoriteDataSources: [], - addFavoriteDatasource: () => {}, - removeFavoriteDatasource: () => {}, - isFavoriteDatasource: () => false, - }; - } - const [favoriteDatasources, setFavoriteDatasources] = useState([]); const [initialFavoriteDataSources, setInitialFavoriteDataSources] = useState([]); const [isLoading, setIsLoading] = useState(false); // Load favorites from storage on mount useEffect(() => { + if (!config.featureToggles.favoriteDatasources) { + return; + } + const loadFavorites = async () => { setIsLoading(true); const stored = await userStorage.getItem(FAVORITE_DATASOURCES_KEY); @@ -108,6 +100,18 @@ export function useFavoriteDatasources(): FavoriteDatasources { [favoriteDatasources] ); + if (!config.featureToggles.favoriteDatasources) { + return { + enabled: false, + isLoading: false, + favoriteDatasources: [], + initialFavoriteDataSources: [], + addFavoriteDatasource: () => {}, + removeFavoriteDatasource: () => {}, + isFavoriteDatasource: () => false, + }; + } + return { enabled: true, isLoading, From b0471753308baaacc5378cca81bbc32b5a2d268d Mon Sep 17 00:00:00 2001 From: Steffen Baarsgaard Date: Tue, 26 Aug 2025 14:50:06 +0200 Subject: [PATCH 05/24] Auth: Support JWT configs `tls_client_ca` and `jwk_set_bearer_token_file` (#109095) * Auth.jwt: Support config tls_client_ca * Auth.jwt: Support config jwk_set_bearer_token_file * Docs: Document new JWKS url options and mention tls_skip_verify_insecure * Docs: Fix note on JWKS response caching * chore: Refactor getBearerToken into standalone function * docs: Apply wording/formatting suggestions Co-authored-by: Victor Cinaglia * chore: Simplify ca helper function using testcerts Co-authored-by: Victor Cinaglia * chore: Update doc and add comment preventing potential erroneous optimization Co-authored-by: Victor Cinaglia chore: Reword comment prevent an erroneous refactor * docs: Update casing Co-authored-by: Victor Cinaglia --------- Co-authored-by: Victor Cinaglia --- conf/defaults.ini | 2 + conf/sample.ini | 2 + .../configure-authentication/jwt/index.md | 15 +- pkg/services/auth/jwt/auth_test.go | 208 ++++++++++++++++++ pkg/services/auth/jwt/key_sets.go | 67 +++++- pkg/setting/setting_jwt.go | 4 + 6 files changed, 294 insertions(+), 4 deletions(-) diff --git a/conf/defaults.ini b/conf/defaults.ini index 081d660a5d8..c0f116e6a3e 100644 --- a/conf/defaults.ini +++ b/conf/defaults.ini @@ -949,6 +949,7 @@ username_claim = email_attribute_path = username_attribute_path = jwk_set_url = +jwk_set_bearer_token_file = jwk_set_file = cache_ttl = 60m expect_claims = {} @@ -963,6 +964,7 @@ auto_sign_up = false url_login = false allow_assign_grafana_admin = false skip_org_role_sync = false +tls_client_ca = tls_skip_verify_insecure = false #################################### Auth LDAP ########################### diff --git a/conf/sample.ini b/conf/sample.ini index fb6550c23f1..376f9f45231 100644 --- a/conf/sample.ini +++ b/conf/sample.ini @@ -912,6 +912,7 @@ ;email_attribute_path = jmespath.email ;username_attribute_path = jmespath.username ;jwk_set_url = https://foo.bar/.well-known/jwks.json +;jwk_set_bearer_token_file = /path/to/token/file ;jwk_set_file = /path/to/jwks.json ;cache_ttl = 60m ;expect_claims = {"aud": ["foo", "bar"]} @@ -928,6 +929,7 @@ ;allow_assign_grafana_admin = false ;skip_org_role_sync = false ;signout_redirect_url = +;tls_client_ca = ;tls_skip_verify_insecure = false #################################### Auth LDAP ########################## diff --git a/docs/sources/setup-grafana/configure-security/configure-authentication/jwt/index.md b/docs/sources/setup-grafana/configure-security/configure-authentication/jwt/index.md index ee945cf4434..a6de78e495c 100644 --- a/docs/sources/setup-grafana/configure-security/configure-authentication/jwt/index.md +++ b/docs/sources/setup-grafana/configure-security/configure-authentication/jwt/index.md @@ -157,12 +157,23 @@ For more information on JWKS endpoints, refer to [Auth0 docs](https://auth0.com/ jwk_set_url = https://your-auth-provider.example.com/.well-known/jwks.json -# Cache TTL for data loaded from http endpoint. +# When the JWKS url requires an 'Authorization: Bearer ' header +# jwk_set_bearer_token_file = /path/to/bearer_token + +# Cache duration for https endpoint response. cache_ttl = 60m + +# Path to file containing one or more custom PEM-encoded CA certificates. +# Used with jwk_set_url when the JWKS endpoint uses a certificate that is not +# trusted by the default CA bundle (e.g. self-signed certificates). +# tls_client_ca = /path/to/ca.crt + +# Skip CA Verification entirely +# tls_skip_verify_insecure = false ``` {{< admonition type="note" >}} -If the JWKS endpoint includes cache control headers and the value is less than the configured `cache_ttl`, then the cache control header value is used instead. If the `cache_ttl` is not set, no caching is performed. `no-store` and `no-cache` cache control headers are ignored. +If the JWKS endpoint includes cache control headers and the value is less than the configured `cache_ttl`, then the cache control header value is used instead. If the `cache_ttl` is not set, the default of `60m` is used. `no-store` and `no-cache` cache control headers are ignored. To disable JWKS caching, set `cache_ttl = 0s` {{< /admonition >}} ### Verify token using a JSON Web Key Set loaded from JSON file diff --git a/pkg/services/auth/jwt/auth_test.go b/pkg/services/auth/jwt/auth_test.go index 18ce9f5dcad..8e85c464c37 100644 --- a/pkg/services/auth/jwt/auth_test.go +++ b/pkg/services/auth/jwt/auth_test.go @@ -15,6 +15,7 @@ import ( jose "github.com/go-jose/go-jose/v3" "github.com/go-jose/go-jose/v3/jwt" + "github.com/madflojo/testcerts" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -169,6 +170,201 @@ func TestIntegrationVerifyUsingJWKSetURL(t *testing.T) { }) } +// test that caCert and bearer token files have been read and configured and an error is thrown when the file does not exist or is empty +func TestIntegrationCustomRootCAJWKHTTPSClient(t *testing.T) { + if testing.Short() { + t.Skip("skipping integration test in short mode") + } + + urlConfigure := func(t *testing.T, cfg *setting.Cfg) { + cfg.JWTAuth.JWKSetURL = "https://example.com/.well-known/jwks.json" + } + + t.Run("tls_client_ca being empty returns nil RootCAs", func(t *testing.T) { + s, err := initAuthService(t, urlConfigure) + require.NoError(t, err) + + ks := s.keySet.(*keySetHTTP) + assert.Nil(t, ks.client.Transport.(*http.Transport).TLSClientConfig.RootCAs) + }) + + t.Run("tls_client_ca path is read and added to client.RootCAs", func(t *testing.T) { + configure := func(t *testing.T, cfg *setting.Cfg) { + caFilename := createTestRootCAFile(t) + t.Cleanup(func() { + if err := os.Remove(caFilename); err != nil { + panic(err) + } + }) + + cfg.JWTAuth.TlsClientCa = caFilename + } + + s, err := initAuthService(t, urlConfigure, configure) + require.NoError(t, err) + + ks := s.keySet.(*keySetHTTP) + rootCAs := ks.client.Transport.(*http.Transport).TLSClientConfig.RootCAs + assert.NotNil(t, rootCAs) + }) + + t.Run("error when tls_client_ca file does not exist", func(t *testing.T) { + configure := func(t *testing.T, cfg *setting.Cfg) { + // Create and remove tmp file to guarantee the path does not exist + file, err := os.CreateTemp(os.TempDir(), "ca-*.crt") + require.NoError(t, err) + require.NoError(t, os.Remove(file.Name())) + + cfg.JWTAuth.TlsClientCa = file.Name() + } + + _, err := initAuthService(t, urlConfigure, configure) + require.Error(t, err) + }) + + t.Run("error when tls_client_ca path does not contain PEM certs", func(t *testing.T) { + configure := func(t *testing.T, cfg *setting.Cfg) { + file, err := os.CreateTemp(os.TempDir(), "ca-*.crt") + require.NoError(t, err) + t.Cleanup(func() { + if err := os.Remove(file.Name()); err != nil { + panic(err) + } + }) + + cfg.JWTAuth.TlsClientCa = file.Name() + } + + _, err := initAuthService(t, urlConfigure, configure) + require.Error(t, err) + }) +} + +func TestIntegrationAuthorizationHeaderJWKHTTPSClient(t *testing.T) { + if testing.Short() { + t.Skip("skipping integration test in short mode") + } + + urlConfigure := func(t *testing.T, cfg *setting.Cfg) { + cfg.JWTAuth.JWKSetURL = "https://example.com/.well-known/jwks.json" + } + + t.Run("jwk_set_bearer_token_file being empty returns no token", func(t *testing.T) { + _, err := initAuthService(t, urlConfigure) + require.NoError(t, err) + + token, err := getBearerToken("") + assert.Empty(t, token) + assert.Error(t, err) // Error is expected as getBearerToken is only invoked when bearer token file is configured + }) + + t.Run("jwk_set_bearer_token_file is read and added to headers", func(t *testing.T) { + configure := func(t *testing.T, cfg *setting.Cfg) { + file, err := os.CreateTemp(os.TempDir(), "token-*") + require.NoError(t, err) + t.Cleanup(func() { + if err := os.Remove(file.Name()); err != nil { + panic(err) + } + }) + + _, err = file.WriteString("fake_token_string") + require.NoError(t, err) + + cfg.JWTAuth.JWKSetBearerTokenFile = file.Name() + } + + s, err := initAuthService(t, urlConfigure, configure) + require.NoError(t, err) + + token, err := getBearerToken(s.keySet.(*keySetHTTP).bearerTokenPath) + assert.Equal(t, "Bearer fake_token_string", token, "Token should have been prefixed with 'Bearer '") + assert.NoError(t, err) + }) + + t.Run("jwk_set_bearer_token_file prefix is not doubled", func(t *testing.T) { + configure := func(t *testing.T, cfg *setting.Cfg) { + file, err := os.CreateTemp(os.TempDir(), "token-*") + require.NoError(t, err) + t.Cleanup(func() { + if err := os.Remove(file.Name()); err != nil { + panic(err) + } + }) + + _, err = file.WriteString("Bearer fake_token_string") + require.NoError(t, err) + + cfg.JWTAuth.JWKSetBearerTokenFile = file.Name() + } + + s, err := initAuthService(t, urlConfigure, configure) + require.NoError(t, err) + + token, err := getBearerToken(s.keySet.(*keySetHTTP).bearerTokenPath) + assert.Equal(t, "Bearer fake_token_string", token, "Token should have kept existing prefix") + assert.NoError(t, err) + }) + + t.Run("jwk_set_bearer_token_file file is just spaces", func(t *testing.T) { + // Create file outside 'configure' as getBearerToken needs to know the path + // As initAuthService returns an error when token is missing + file, err := os.CreateTemp(os.TempDir(), "token-*") + require.NoError(t, err) + t.Cleanup(func() { + if err := os.Remove(file.Name()); err != nil { + panic(err) + } + }) + + configure := func(t *testing.T, cfg *setting.Cfg) { + _, err = file.WriteString(" ") + require.NoError(t, err) + + cfg.JWTAuth.JWKSetBearerTokenFile = file.Name() + } + + s, err := initAuthService(t, urlConfigure, configure) + require.Nil(t, s.keySet) + require.Error(t, err) + + token, err := getBearerToken(file.Name()) + assert.Equal(t, "", token, "Should return an empty token") + assert.Error(t, err) + }) + + t.Run("error when jwk_set_bearer_token_file does not exist", func(t *testing.T) { + configure := func(t *testing.T, cfg *setting.Cfg) { + // Create and remove tmp file to guarantee the path does not exist + file, err := os.CreateTemp(os.TempDir(), "token-*") + require.NoError(t, err) + require.NoError(t, os.Remove(file.Name())) + + cfg.JWTAuth.JWKSetBearerTokenFile = file.Name() + } + + _, err := initAuthService(t, urlConfigure, configure) + require.Error(t, err) + }) + + t.Run("error when jwk_set_bearer_token_file does not contain a token", func(t *testing.T) { + configure := func(t *testing.T, cfg *setting.Cfg) { + file, err := os.CreateTemp(os.TempDir(), "token-*") + require.NoError(t, err) + t.Cleanup(func() { + if err := os.Remove(file.Name()); err != nil { + panic(err) + } + }) + + cfg.JWTAuth.JWKSetBearerTokenFile = file.Name() + } + + _, err := initAuthService(t, urlConfigure, configure) + require.Error(t, err) + }) +} + func TestIntegrationCachingJWKHTTPResponse(t *testing.T) { if testing.Short() { t.Skip("skipping integration test in short mode") @@ -465,3 +661,15 @@ func configurePKIXPublicKeyFile(t *testing.T, cfg *setting.Cfg) { cfg.JWTAuth.KeyFile = file.Name() } + +func createTestRootCAFile(t *testing.T) (filename string) { + t.Helper() + + tmpDir := t.TempDir() + ca := testcerts.NewCA() + + caCertFile, _, err := ca.ToTempFile(tmpDir) + require.NoError(t, err) + + return caCertFile.Name() +} diff --git a/pkg/services/auth/jwt/key_sets.go b/pkg/services/auth/jwt/key_sets.go index 2e5d6b22001..b569546b6b1 100644 --- a/pkg/services/auth/jwt/key_sets.go +++ b/pkg/services/auth/jwt/key_sets.go @@ -42,6 +42,7 @@ type keySetHTTP struct { url string log log.Logger client *http.Client + bearerTokenPath string cache *remotecache.RemoteCache cacheKey string cacheExpiration time.Duration @@ -70,6 +71,8 @@ func (s *AuthService) checkKeySetConfiguration() error { return nil } +// initKeySet creates a provider for JWKSet, either file, or https +// nolint:gocyclo func (s *AuthService) initKeySet() error { if err := s.checkKeySetConfiguration(); err != nil { return err @@ -155,14 +158,42 @@ func (s *AuthService) initKeySet() error { if urlParsed.Scheme != "https" && s.Cfg.Env != setting.Dev { return ErrJWTSetURLMustHaveHTTPSScheme } + + var caCertPool *x509.CertPool + if s.Cfg.JWTAuth.TlsClientCa != "" { + s.log.Debug("reading ca from TlsClientCa path") + // nolint:gosec + // We can ignore the gosec G304 warning on this one because `tlsClientCa` comes from grafana configuration file + caCert, err := os.ReadFile(s.Cfg.JWTAuth.TlsClientCa) + if err != nil { + s.log.Error("Failed to read TlsClientCa", "path", s.Cfg.JWTAuth.TlsClientCa, "error", err) + return fmt.Errorf("failed to read TlsClientCa: %w", err) + } + + caCertPool = x509.NewCertPool() + if !caCertPool.AppendCertsFromPEM(caCert) { + s.log.Error("failed to decode provided PEM certs", "path", s.Cfg.JWTAuth.TlsClientCa) + return fmt.Errorf("failed to decode provided PEM certs file from TlsClientCa") + } + } + + // Read Bearer token from file during init + if s.Cfg.JWTAuth.JWKSetBearerTokenFile != "" { + if _, err := getBearerToken(s.Cfg.JWTAuth.JWKSetBearerTokenFile); err != nil { + return err + } + } + s.keySet = &keySetHTTP{ - url: urlStr, - log: s.log, + url: urlStr, + log: s.log, + bearerTokenPath: s.Cfg.JWTAuth.JWKSetBearerTokenFile, client: &http.Client{ Transport: &http.Transport{ TLSClientConfig: &tls.Config{ Renegotiation: tls.RenegotiateFreelyAsClient, InsecureSkipVerify: s.Cfg.JWTAuth.TlsSkipVerify, + RootCAs: caCertPool, }, Proxy: http.ProxyFromEnvironment, DialContext: (&net.Dialer{ @@ -189,6 +220,27 @@ func (ks *keySetJWKS) Key(ctx context.Context, keyID string) ([]jose.JSONWebKey, return ks.JSONWebKeySet.Key(keyID), nil } +func getBearerToken(bearerTokenPath string) (string, error) { + // nolint:gosec + // We can ignore the gosec G304 warning as `bearerTokenPath` originates from grafana configuration file + bytes, err := os.ReadFile(bearerTokenPath) + if err != nil { + return "", fmt.Errorf("failed to read JWKSetBearerTokenFile: %w", err) + } + + t := strings.TrimSpace(string(bytes)) + if len(t) == 0 { + return "", fmt.Errorf("empty file configured for JWKSetBearerTokenFile") + } + + if strings.HasPrefix(t, "Bearer ") { + return t, nil + } + + // Prefix with Bearer if missing + return fmt.Sprintf("Bearer %s", t), nil +} + func (ks *keySetHTTP) getJWKS(ctx context.Context) (keySetJWKS, error) { var jwks keySetJWKS @@ -210,6 +262,17 @@ func (ks *keySetHTTP) getJWKS(ctx context.Context) (keySetJWKS, error) { return jwks, err } + if ks.bearerTokenPath != "" { + // Always read the token before fetching JWKS to handle potential key rotation (e.g. short-lived kubernetes ServiceAccount tokens) + token, err := getBearerToken(ks.bearerTokenPath) + if err != nil { + return jwks, err + } + + ks.log.Debug("adding Authorization header", "token_len", len(token)) + req.Header.Set("Authorization", token) + } + resp, err := ks.client.Do(req) if err != nil { return jwks, err diff --git a/pkg/setting/setting_jwt.go b/pkg/setting/setting_jwt.go index 18c7866cccf..af069db8fda 100644 --- a/pkg/setting/setting_jwt.go +++ b/pkg/setting/setting_jwt.go @@ -19,6 +19,7 @@ type AuthJWTSettings struct { UsernameClaim string ExpectClaims string JWKSetURL string + JWKSetBearerTokenFile string CacheTTL time.Duration KeyFile string KeyID string @@ -33,6 +34,7 @@ type AuthJWTSettings struct { GroupsAttributePath string EmailAttributePath string UsernameAttributePath string + TlsClientCa string TlsSkipVerify bool } @@ -64,6 +66,7 @@ func (cfg *Cfg) readAuthJWTSettings() { jwtSettings.UsernameClaim = valueAsString(authJWT, "username_claim", "") jwtSettings.ExpectClaims = valueAsString(authJWT, "expect_claims", "{}") jwtSettings.JWKSetURL = valueAsString(authJWT, "jwk_set_url", "") + jwtSettings.JWKSetBearerTokenFile = valueAsString(authJWT, "jwk_set_bearer_token_file", "") jwtSettings.CacheTTL = authJWT.Key("cache_ttl").MustDuration(time.Minute * 60) jwtSettings.KeyFile = valueAsString(authJWT, "key_file", "") jwtSettings.KeyID = authJWT.Key("key_id").MustString("") @@ -76,6 +79,7 @@ func (cfg *Cfg) readAuthJWTSettings() { jwtSettings.GroupsAttributePath = valueAsString(authJWT, "groups_attribute_path", "") jwtSettings.EmailAttributePath = valueAsString(authJWT, "email_attribute_path", "") jwtSettings.UsernameAttributePath = valueAsString(authJWT, "username_attribute_path", "") + jwtSettings.TlsClientCa = valueAsString(authJWT, "tls_client_ca", "") jwtSettings.TlsSkipVerify = authJWT.Key("tls_skip_verify_insecure").MustBool(false) jwtSettings.OrgAttributePath = valueAsString(authJWT, "org_attribute_path", "") jwtSettings.OrgMapping = util.SplitString(valueAsString(authJWT, "org_mapping", "")) From 58c8fdad7af4606ab00e4b4a1ff4fe3f788f670f Mon Sep 17 00:00:00 2001 From: Kyle Brandt Date: Tue, 26 Aug 2025 09:21:24 -0400 Subject: [PATCH 06/24] SSE/QuerySvc: (Chore) Remove expression parser code and feature toggle (#110117) --- .../src/types/featureToggles.gen.ts | 4 - pkg/expr/graph_test.go | 4 +- pkg/expr/nodes.go | 27 --- pkg/expr/reader.go | 197 ------------------ pkg/expr/reader_test.go | 162 -------------- pkg/services/featuremgmt/registry.go | 7 - pkg/services/featuremgmt/toggles_gen.csv | 1 - pkg/services/featuremgmt/toggles_gen.go | 4 - pkg/services/featuremgmt/toggles_gen.json | 3 +- 9 files changed, 3 insertions(+), 406 deletions(-) delete mode 100644 pkg/expr/reader.go delete mode 100644 pkg/expr/reader_test.go diff --git a/packages/grafana-data/src/types/featureToggles.gen.ts b/packages/grafana-data/src/types/featureToggles.gen.ts index 9ab694d9d58..3f3ccc676d7 100644 --- a/packages/grafana-data/src/types/featureToggles.gen.ts +++ b/packages/grafana-data/src/types/featureToggles.gen.ts @@ -494,10 +494,6 @@ export interface FeatureToggles { */ kubernetesAggregatorCapTokenAuth?: boolean; /** - * Enable new expression parser - */ - expressionParser?: boolean; - /** * Enable groupBy variable support in scenes dashboards */ groupByVariable?: boolean; diff --git a/pkg/expr/graph_test.go b/pkg/expr/graph_test.go index bcfee8fb306..1d1b11e63bb 100644 --- a/pkg/expr/graph_test.go +++ b/pkg/expr/graph_test.go @@ -7,7 +7,6 @@ import ( "github.com/stretchr/testify/require" "github.com/grafana/grafana/pkg/services/datasources" - "github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/setting" ) @@ -234,8 +233,7 @@ func TestServicebuildPipeLine(t *testing.T) { }, } s := Service{ - features: featuremgmt.WithFeatures(featuremgmt.FlagExpressionParser), - cfg: setting.NewCfg(), + cfg: setting.NewCfg(), } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/pkg/expr/nodes.go b/pkg/expr/nodes.go index 2ea0e36c40e..8e65ff9858b 100644 --- a/pkg/expr/nodes.go +++ b/pkg/expr/nodes.go @@ -9,8 +9,6 @@ import ( "time" "github.com/grafana/grafana-plugin-sdk-go/backend" - "github.com/grafana/grafana-plugin-sdk-go/data/utils/jsoniter" - data "github.com/grafana/grafana-plugin-sdk-go/experimental/apis/data/v0alpha1" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" "gonum.org/v1/gonum/graph/simple" @@ -139,31 +137,6 @@ func buildCMDNode(ctx context.Context, rn *rawNode, toggles featuremgmt.FeatureT CMDType: commandType, } - if toggles.IsEnabledGlobally(featuremgmt.FlagExpressionParser) { - rn.QueryType, err = getExpressionCommandTypeString(rn.Query) - if err != nil { - return nil, err // should not happen because the command was parsed first thing - } - - // NOTE: this structure of this is weird now, because it is targeting a structure - // where this is actually run in the root loop, however we want to verify the individual - // node parsing before changing the full tree parser - reader := NewExpressionQueryReader(toggles) - iter, err := jsoniter.ParseBytes(jsoniter.ConfigDefault, rn.QueryRaw) - if err != nil { - return nil, err - } - q, err := reader.ReadQuery(ctx, data.NewDataQuery(map[string]any{ - "refId": rn.RefID, - "type": rn.QueryType, - }), iter) - if err != nil { - return nil, err - } - node.Command = q.Command - return node, err - } - switch commandType { case TypeMath: node.Command, err = UnmarshalMathCommand(rn) diff --git a/pkg/expr/reader.go b/pkg/expr/reader.go deleted file mode 100644 index ddabbf3cb04..00000000000 --- a/pkg/expr/reader.go +++ /dev/null @@ -1,197 +0,0 @@ -package expr - -import ( - "context" - "fmt" - "strings" - - "github.com/grafana/grafana-plugin-sdk-go/backend" - "github.com/grafana/grafana-plugin-sdk-go/backend/gtime" - "github.com/grafana/grafana-plugin-sdk-go/data/utils/jsoniter" - data "github.com/grafana/grafana-plugin-sdk-go/experimental/apis/data/v0alpha1" - - "github.com/grafana/grafana/pkg/expr/classic" - "github.com/grafana/grafana/pkg/expr/mathexp" - "github.com/grafana/grafana/pkg/services/featuremgmt" -) - -// Once we are comfortable with the parsing logic, this struct will -// be merged/replace the existing Query struct in grafana/pkg/expr/transform.go -type ExpressionQuery struct { - GraphID int64 `json:"id,omitempty"` - RefID string `json:"refId"` - QueryType QueryType `json:"type"` - - // The typed query parameters - Properties any `json:"properties"` - - // Hidden in debug JSON - Command Command `json:"-"` -} - -// ID is used to identify nodes in the directed graph -func (q ExpressionQuery) ID() int64 { - return q.GraphID -} - -type ExpressionQueryReader struct { - features featuremgmt.FeatureToggles -} - -func NewExpressionQueryReader(features featuremgmt.FeatureToggles) *ExpressionQueryReader { - return &ExpressionQueryReader{ - features: features, - } -} - -// nolint:gocyclo -func (h *ExpressionQueryReader) ReadQuery( - ctx context.Context, - // Properties that have been parsed off the same node - common data.DataQuery, - // An iterator with context for the full node (include common values) - iter *jsoniter.Iterator, -) (eq ExpressionQuery, err error) { - referenceVar := "" - eq.RefID = common.RefID - eq.QueryType = QueryType(common.GetString("type")) - if eq.QueryType == "" { - return eq, fmt.Errorf("missing type") - } - switch eq.QueryType { - case QueryTypeMath: - q := &MathQuery{} - err = iter.ReadVal(q) - if err == nil { - eq.Command, err = NewMathCommand(common.RefID, q.Expression) - eq.Properties = q - } - - case QueryTypeReduce: - var mapper mathexp.ReduceMapper = nil - q := &ReduceQuery{} - err = iter.ReadVal(q) - if err == nil { - referenceVar, err = getReferenceVar(q.Expression, common.RefID) - eq.Properties = q - } - if err == nil && q.Settings != nil { - switch q.Settings.Mode { - case ReduceModeStrict: - mapper = nil - case ReduceModeDrop: - mapper = mathexp.DropNonNumber{} - case ReduceModeReplace: - if q.Settings.ReplaceWithValue == nil { - err = fmt.Errorf("setting replaceWithValue must be specified when mode is '%s'", q.Settings.Mode) - } - mapper = mathexp.ReplaceNonNumberWithValue{Value: *q.Settings.ReplaceWithValue} - default: - err = fmt.Errorf("unsupported reduce mode") - } - } - if err == nil { - eq.Properties = q - eq.Command, err = NewReduceCommand(common.RefID, - q.Reducer, referenceVar, mapper) - } - - case QueryTypeResample: - q := &ResampleQuery{} - err = iter.ReadVal(q) - if err == nil && common.TimeRange == nil { - err = fmt.Errorf("missing time range in query") - } - if err == nil { - referenceVar, err = getReferenceVar(q.Expression, common.RefID) - } - if err == nil { - tr := gtime.NewTimeRange(common.TimeRange.From, common.TimeRange.To) - eq.Properties = q - eq.Command, err = NewResampleCommand(common.RefID, - q.Window, - referenceVar, - q.Downsampler, - q.Upsampler, - AbsoluteTimeRange{ - From: tr.GetFromAsTimeUTC(), - To: tr.GetToAsTimeUTC(), - }, - ) - } - - case QueryTypeClassic: - q := &ClassicQuery{} - err = iter.ReadVal(q) - if err == nil { - eq.Properties = q - eq.Command, err = classic.NewConditionCmd(common.RefID, q.Conditions) - } - - case QueryTypeSQL: - if !h.features.IsEnabledGlobally(featuremgmt.FlagSqlExpressions) { - return eq, fmt.Errorf("sql expressions are disabled") - } - q := &SQLExpression{} - err = iter.ReadVal(q) - if err == nil { - eq.Properties = q - // TODO: Cascade limit from Grafana config in this (new Expression Parser) branch of the code - cellLimit := 0 // zero means no limit - sqlLogger := backend.NewLoggerWith("logger", SQLLoggerName).FromContext(ctx) - eq.Command, err = NewSQLCommand(ctx, sqlLogger, common.RefID, q.Format, q.Expression, int64(cellLimit), 0, 0) - } - - case QueryTypeThreshold: - q := &ThresholdQuery{} - err = iter.ReadVal(q) - if err == nil { - referenceVar, err = getReferenceVar(q.Expression, common.RefID) - } - if err == nil { - // we only support one condition for now, we might want to turn this in to "OR" expressions later - if len(q.Conditions) != 1 { - return eq, fmt.Errorf("threshold expression requires exactly one condition") - } - firstCondition := q.Conditions[0] - - threshold, err := NewThresholdCommand(common.RefID, referenceVar, firstCondition.Evaluator.Type, firstCondition.Evaluator.Params) - if err != nil { - return eq, fmt.Errorf("invalid condition: %w", err) - } - eq.Command = threshold - eq.Properties = q - - if firstCondition.UnloadEvaluator != nil { - unloading, err := NewThresholdCommand(common.RefID, referenceVar, firstCondition.UnloadEvaluator.Type, firstCondition.UnloadEvaluator.Params) - unloading.Invert = true - if err != nil { - return eq, fmt.Errorf("invalid unloadCondition: %w", err) - } - var d Fingerprints - if firstCondition.LoadedDimensions != nil { - d, err = FingerprintsFromFrame(firstCondition.LoadedDimensions) - if err != nil { - return eq, fmt.Errorf("failed to parse loaded dimensions: %w", err) - } - } - eq.Command, err = NewHysteresisCommand(common.RefID, referenceVar, *threshold, *unloading, d) - if err != nil { - return eq, err - } - } - } - - default: - err = fmt.Errorf("unknown query type (%s)", common.QueryType) - } - return eq, err -} - -func getReferenceVar(exp string, refId string) (string, error) { - exp = strings.TrimPrefix(exp, "$") - if exp == "" { - return "", fmt.Errorf("no variable specified to reference for refId %v", refId) - } - return exp, nil -} diff --git a/pkg/expr/reader_test.go b/pkg/expr/reader_test.go deleted file mode 100644 index 39b99a21ac0..00000000000 --- a/pkg/expr/reader_test.go +++ /dev/null @@ -1,162 +0,0 @@ -package expr - -import ( - "encoding/json" - "reflect" - "testing" - - "github.com/grafana/grafana-plugin-sdk-go/data/utils/jsoniter" - data "github.com/grafana/grafana-plugin-sdk-go/experimental/apis/data/v0alpha1" - "github.com/grafana/grafana/pkg/expr/mathexp" - "github.com/grafana/grafana/pkg/services/featuremgmt" - "github.com/stretchr/testify/require" -) - -func TestReaderReduceMode(t *testing.T) { - testData := []struct { - name string - bytes []byte - expectError bool - hasMapper bool - mapperType reflect.Type - }{ - { - name: "no_settings", - bytes: []byte(` - { - "refId": "B", - "datasource": { - "type": "__expr__", - "uid": "__expr__" - }, - "reducer": "last", - "expression": "A", - "window": "", - "type": "reduce" - } - `), - expectError: false, - hasMapper: false, - }, - { - name: "mode_dropnn", - bytes: []byte(` - { - "refId": "B", - "datasource": { - "type": "__expr__", - "uid": "__expr__" - }, - "reducer": "last", - "expression": "A", - "window": "", - "settings": { - "mode": "dropNN" - }, - "type": "reduce" - } - `), - expectError: false, - hasMapper: true, - mapperType: reflect.TypeOf(mathexp.DropNonNumber{}), - }, - { - name: "mode_replacenn", - bytes: []byte(` - { - "refId": "B", - "datasource": { - "type": "__expr__", - "uid": "__expr__" - }, - "reducer": "last", - "expression": "A", - "window": "", - "settings": { - "mode": "replaceNN", - "replaceWithValue": 42 - }, - "type": "reduce" - } - `), - expectError: false, - hasMapper: true, - mapperType: reflect.TypeOf(mathexp.ReplaceNonNumberWithValue{}), - }, - { - name: "mode_strict", - bytes: []byte(` - { - "refId": "B", - "datasource": { - "type": "__expr__", - "uid": "__expr__" - }, - "reducer": "last", - "expression": "A", - "window": "", - "settings": { - "mode": "" - }, - "type": "reduce" - } - `), - expectError: false, - hasMapper: false, - }, - { - name: "mode_invalid", - bytes: []byte(` - { - "refId": "B", - "datasource": { - "type": "__expr__", - "uid": "__expr__" - }, - "reducer": "last", - "expression": "A", - "window": "", - "settings": { - "mode": "invalid-mode" - }, - "type": "reduce" - } - `), - expectError: true, - }, - } - - for _, test := range testData { - t.Run("TestReduceReader:"+test.name, func(t *testing.T) { - var q data.DataQuery - - err := json.Unmarshal(test.bytes, &q) - require.NoError(t, err) - - raw, err := json.Marshal(q) - require.NoError(t, err) - - iter, err := jsoniter.ParseBytes(jsoniter.ConfigDefault, raw) - require.NoError(t, err) - - reader := NewExpressionQueryReader(featuremgmt.WithFeatures()) - - eq, err := reader.ReadQuery(t.Context(), q, iter) - - if test.expectError { - require.Error(t, err) - } else { - require.NoError(t, err) - rc, ok := eq.Command.(*ReduceCommand) - require.True(t, ok) - - if test.hasMapper { - require.NotNil(t, rc.seriesMapper) - require.Equal(t, test.mapperType, reflect.TypeOf(rc.seriesMapper)) - } else { - require.Nil(t, rc.seriesMapper) - } - } - }) - } -} diff --git a/pkg/services/featuremgmt/registry.go b/pkg/services/featuremgmt/registry.go index 7807eaee089..a9dd743f8cc 100644 --- a/pkg/services/featuremgmt/registry.go +++ b/pkg/services/featuremgmt/registry.go @@ -833,13 +833,6 @@ var ( Owner: grafanaAppPlatformSquad, RequiresRestart: true, }, - { - Name: "expressionParser", - Description: "Enable new expression parser", - Stage: FeatureStageExperimental, - Owner: grafanaAppPlatformSquad, - RequiresRestart: true, - }, { Name: "groupByVariable", Description: "Enable groupBy variable support in scenes dashboards", diff --git a/pkg/services/featuremgmt/toggles_gen.csv b/pkg/services/featuremgmt/toggles_gen.csv index a59feb73671..db6b999667a 100644 --- a/pkg/services/featuremgmt/toggles_gen.csv +++ b/pkg/services/featuremgmt/toggles_gen.csv @@ -109,7 +109,6 @@ newPDFRendering,GA,@grafana/grafana-operator-experience-squad,false,false,false tlsMemcached,GA,@grafana/grafana-operator-experience-squad,false,false,false kubernetesAggregator,experimental,@grafana/grafana-app-platform-squad,false,true,false kubernetesAggregatorCapTokenAuth,experimental,@grafana/grafana-app-platform-squad,false,true,false -expressionParser,experimental,@grafana/grafana-app-platform-squad,false,true,false groupByVariable,experimental,@grafana/dashboards-squad,false,false,false scopeFilters,experimental,@grafana/dashboards-squad,false,false,false oauthRequireSubClaim,experimental,@grafana/identity-access-team,false,false,false diff --git a/pkg/services/featuremgmt/toggles_gen.go b/pkg/services/featuremgmt/toggles_gen.go index da56b91fa61..c6b40779158 100644 --- a/pkg/services/featuremgmt/toggles_gen.go +++ b/pkg/services/featuremgmt/toggles_gen.go @@ -447,10 +447,6 @@ const ( // Enable CAP token based authentication in grafana's embedded kube-aggregator FlagKubernetesAggregatorCapTokenAuth = "kubernetesAggregatorCapTokenAuth" - // FlagExpressionParser - // Enable new expression parser - FlagExpressionParser = "expressionParser" - // FlagGroupByVariable // Enable groupBy variable support in scenes dashboards FlagGroupByVariable = "groupByVariable" diff --git a/pkg/services/featuremgmt/toggles_gen.json b/pkg/services/featuremgmt/toggles_gen.json index 367fcdf165a..26caa2e068e 100644 --- a/pkg/services/featuremgmt/toggles_gen.json +++ b/pkg/services/featuremgmt/toggles_gen.json @@ -1259,7 +1259,8 @@ "metadata": { "name": "expressionParser", "resourceVersion": "1753448760331", - "creationTimestamp": "2024-02-17T00:59:11Z" + "creationTimestamp": "2024-02-17T00:59:11Z", + "deletionTimestamp": "2025-08-25T16:14:10Z" }, "spec": { "description": "Enable new expression parser", From d34338be021149927652a8083713cc7f5c0eb272 Mon Sep 17 00:00:00 2001 From: Andres Martinez Gotor Date: Tue, 26 Aug 2025 16:03:34 +0200 Subject: [PATCH 07/24] Advisor: Avoid pagination when listing checks (#110161) --- apps/advisor/pkg/app/checkscheduler/checkscheduler.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/advisor/pkg/app/checkscheduler/checkscheduler.go b/apps/advisor/pkg/app/checkscheduler/checkscheduler.go index 92fe70fe192..2b337239cb6 100644 --- a/apps/advisor/pkg/app/checkscheduler/checkscheduler.go +++ b/apps/advisor/pkg/app/checkscheduler/checkscheduler.go @@ -139,7 +139,9 @@ func (r *Runner) Run(ctx context.Context) error { } func (r *Runner) listChecks(ctx context.Context, logger logging.Logger) ([]resource.Object, error) { - list, err := r.client.List(ctx, r.namespace, resource.ListOptions{}) + list, err := r.client.List(ctx, r.namespace, resource.ListOptions{ + Limit: 1000, // Avoid pagination for normal uses cases, which is a costly operation + }) if err != nil { return nil, err } @@ -147,7 +149,7 @@ func (r *Runner) listChecks(ctx context.Context, logger logging.Logger) ([]resou checks := list.GetItems() for list.GetContinue() != "" { logger.Debug("List has continue token, listing next page", "continue", list.GetContinue()) - list, err = r.client.List(ctx, r.namespace, resource.ListOptions{Continue: list.GetContinue()}) + list, err = r.client.List(ctx, r.namespace, resource.ListOptions{Continue: list.GetContinue(), Limit: 1000}) if err != nil { return nil, err } From 78ac555c4d3a69b6c8cbb00f94fa85c97d140f90 Mon Sep 17 00:00:00 2001 From: Matheus Macabu Date: Tue, 26 Aug 2025 16:14:03 +0200 Subject: [PATCH 08/24] CI: Run gofmt on Linting action (#110159) * CI: Run gofmt on Linting action * Chore: Run gofmt on all packages --- .github/workflows/go-lint.yml | 21 +++++++++++++++++++ Makefile | 4 ++++ .../wire/testdata/BindInjectorArg/foo/wire.go | 3 ++- .../BindInjectorArgPointer/foo/wire.go | 3 ++- .../BindInterfaceWithValue/foo/wire.go | 3 ++- .../testdata/BuildTagsAllPackages/bar/bar.go | 3 ++- .../BuildTagsAllPackages/bar/bar_inject.go | 3 ++- .../testdata/BuildTagsAllPackages/foo/wire.go | 3 ++- .../internal/wire/testdata/Chain/foo/wire.go | 3 ++- .../wire/testdata/Cleanup/foo/wire.go | 3 ++- .../wire/testdata/CopyOtherDecls/foo/foo.go | 3 ++- .../internal/wire/testdata/Cycle/foo/wire.go | 3 ++- .../wire/testdata/DocComment/foo/wire.go | 3 ++- .../wire/testdata/EmptyVar/foo/wire.go | 3 ++- .../testdata/ExampleWithMocks/foo/wire.go | 3 ++- .../wire/testdata/ExportedValue/foo/wire.go | 3 ++- .../ExportedValueDifferentPackage/foo/wire.go | 3 ++- .../wire/testdata/FieldsOfCycle/foo/wire.go | 3 ++- .../FieldsOfImportedStruct/main/wire.go | 3 ++- .../wire/testdata/FieldsOfStruct/foo/wire.go | 3 ++- .../foo/wire.go | 3 ++- .../FieldsOfStructPointer/foo/wire.go | 3 ++- .../testdata/FieldsOfValueStruct/main/wire.go | 3 ++- .../wire/testdata/FuncArgProvider/foo/wire.go | 3 ++- .../internal/wire/testdata/Header/foo/wire.go | 3 ++- .../ImportedInterfaceBinding/bar/wire.go | 3 ++- .../wire/testdata/InjectInput/foo/wire.go | 3 ++- .../testdata/InjectInputConflict/foo/wire.go | 3 ++- .../wire/testdata/InjectWithPanic/foo/wire.go | 3 ++- .../InjectorMissingCleanup/foo/wire.go | 3 ++- .../testdata/InjectorMissingError/foo/wire.go | 3 ++- .../testdata/InterfaceBinding/foo/wire.go | 3 ++- .../foo/wire.go | 3 ++- .../InterfaceBindingInvalidArg0/foo/wire.go | 3 ++- .../InterfaceBindingNotEnoughArgs/foo/wire.go | 3 ++- .../InterfaceBindingReuse/foo/wire.go | 3 ++- .../wire/testdata/InterfaceValue/foo/wire.go | 3 ++- .../InterfaceValueDoesntImplement/foo/wire.go | 3 ++- .../InterfaceValueInvalidArg0/foo/wire.go | 3 ++- .../InterfaceValueNotEnoughArgs/foo/wire.go | 3 ++- .../wire/testdata/InvalidInjector/foo/wire.go | 3 ++- .../testdata/MultipleArgsSameType/foo/wire.go | 3 ++- .../testdata/MultipleBindings/foo/wire.go | 3 ++- .../MultipleMissingInputs/foo/wire.go | 3 ++- .../MultipleSimilarPackages/main/wire.go | 3 ++- .../wire/testdata/NamingWorstCase/foo/wire.go | 3 ++- .../NamingWorstCaseAllInOne/foo/foo.go | 3 ++- .../wire/testdata/NiladicIdentity/foo/wire.go | 3 ++- .../wire/testdata/NiladicValue/foo/wire.go | 3 ++- .../testdata/NoImplicitInterface/foo/wire.go | 3 ++- .../testdata/NoInjectParamNames/foo/wire.go | 3 ++- .../wire/testdata/PartialCleanup/foo/wire.go | 3 ++- .../wire/testdata/PkgImport/foo/wire.go | 3 ++- .../foo/wire.go | 3 ++- .../wire/testdata/RelativePkg/foo/wire.go | 3 ++- .../testdata/ReservedKeywords/foo/wire.go | 3 ++- .../ReturnArgumentAsInterface/foo/wire.go | 3 ++- .../wire/testdata/ReturnError/foo/wire.go | 3 ++- .../internal/wire/testdata/Struct/foo/wire.go | 3 ++- .../testdata/StructNotAStruct/foo/wire.go | 3 ++- .../wire/testdata/StructPointer/foo/wire.go | 3 ++- .../testdata/StructWithPreventTag/foo/wire.go | 3 ++- .../wire/testdata/TwoDeps/foo/wire.go | 3 ++- .../testdata/UnexportedStruct/foo/wire.go | 3 ++- .../wire/testdata/UnexportedValue/foo/wire.go | 3 ++- .../wire/testdata/UnusedProviders/foo/wire.go | 3 ++- .../wire/testdata/ValueChain/foo/wire.go | 3 ++- .../wire/testdata/ValueConversion/foo/wire.go | 3 ++- .../ValueFromFunctionScope/foo/wire.go | 3 ++- .../ValueIsInterfaceValue/foo/wire.go | 3 ++- .../wire/testdata/ValueIsStruct/foo/wire.go | 3 ++- .../wire/testdata/VarValue/foo/wire.go | 3 ++- .../wire/testdata/Varargs/foo/wire.go | 3 ++- pkg/storage/unified/resource/search_test.go | 2 +- .../unified/testing/storage_backend.go | 2 +- 75 files changed, 169 insertions(+), 73 deletions(-) diff --git a/.github/workflows/go-lint.yml b/.github/workflows/go-lint.yml index d352b8f8235..a32c06b55c1 100644 --- a/.github/workflows/go-lint.yml +++ b/.github/workflows/go-lint.yml @@ -33,6 +33,27 @@ jobs: with: self: .github/workflows/go-lint.yml + go-fmt: + needs: detect-changes + if: needs.detect-changes.outputs.changed == 'true' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + persist-credentials: false + - uses: actions/setup-go@v5.5.0 + with: + go-version-file: ./go.mod + - name: Run gofmt + run: | + GOFMT="$(go list -m -f '{{.Dir}}' | xargs -I{} sh -c 'test ! -f {}/.nolint && echo {}' | xargs gofmt -s -e -l 2>&1 | grep -v '/pkg/build/' || true)" + if [ -n "$GOFMT" ]; then + echo "Found files that are not gofmt'ed" + echo "Run 'gofmt -s -w .' or 'make gofmt' or install the pre-commit hook with 'make lefthook-install'" + echo "${GOFMT}" + exit 1 + fi + lint-go: needs: detect-changes if: needs.detect-changes.outputs.changed == 'true' diff --git a/Makefile b/Makefile index 92c20458a6f..1885015ab18 100644 --- a/Makefile +++ b/Makefile @@ -384,6 +384,10 @@ lint-go-diff: sed 's,^,./,' | \ $(XARGSR) $(golangci-lint) run --config .golangci.yml +.PHONY: gofmt +gofmt: ## Run gofmt for all Go files. + gofmt -s -w . + # with disabled SC1071 we are ignored some TCL,Expect `/usr/bin/env expect` scripts .PHONY: shellcheck shellcheck: $(SH_FILES) ## Run checks for shell scripts. diff --git a/pkg/build/wire/internal/wire/testdata/BindInjectorArg/foo/wire.go b/pkg/build/wire/internal/wire/testdata/BindInjectorArg/foo/wire.go index bd7d773b79b..6e2348e354a 100644 --- a/pkg/build/wire/internal/wire/testdata/BindInjectorArg/foo/wire.go +++ b/pkg/build/wire/internal/wire/testdata/BindInjectorArg/foo/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. // -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/BindInjectorArgPointer/foo/wire.go b/pkg/build/wire/internal/wire/testdata/BindInjectorArgPointer/foo/wire.go index 3fc9ca38046..f4006aec6ee 100644 --- a/pkg/build/wire/internal/wire/testdata/BindInjectorArgPointer/foo/wire.go +++ b/pkg/build/wire/internal/wire/testdata/BindInjectorArgPointer/foo/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. // -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/BindInterfaceWithValue/foo/wire.go b/pkg/build/wire/internal/wire/testdata/BindInterfaceWithValue/foo/wire.go index 54c19ba97b0..e11792a75f3 100644 --- a/pkg/build/wire/internal/wire/testdata/BindInterfaceWithValue/foo/wire.go +++ b/pkg/build/wire/internal/wire/testdata/BindInterfaceWithValue/foo/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. // -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/BuildTagsAllPackages/bar/bar.go b/pkg/build/wire/internal/wire/testdata/BuildTagsAllPackages/bar/bar.go index 97c2561ba9d..610d527a2fb 100644 --- a/pkg/build/wire/internal/wire/testdata/BuildTagsAllPackages/bar/bar.go +++ b/pkg/build/wire/internal/wire/testdata/BuildTagsAllPackages/bar/bar.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build !wireinject +//go:build !wireinject +// +build !wireinject // Package bar includes both wireinject and non-wireinject variants. package bar diff --git a/pkg/build/wire/internal/wire/testdata/BuildTagsAllPackages/bar/bar_inject.go b/pkg/build/wire/internal/wire/testdata/BuildTagsAllPackages/bar/bar_inject.go index aa40270918c..b8b541d8df7 100644 --- a/pkg/build/wire/internal/wire/testdata/BuildTagsAllPackages/bar/bar_inject.go +++ b/pkg/build/wire/internal/wire/testdata/BuildTagsAllPackages/bar/bar_inject.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package bar diff --git a/pkg/build/wire/internal/wire/testdata/BuildTagsAllPackages/foo/wire.go b/pkg/build/wire/internal/wire/testdata/BuildTagsAllPackages/foo/wire.go index 32cc0f80743..6e85c5bd273 100644 --- a/pkg/build/wire/internal/wire/testdata/BuildTagsAllPackages/foo/wire.go +++ b/pkg/build/wire/internal/wire/testdata/BuildTagsAllPackages/foo/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/Chain/foo/wire.go b/pkg/build/wire/internal/wire/testdata/Chain/foo/wire.go index 4106dd8d91f..19e87988007 100644 --- a/pkg/build/wire/internal/wire/testdata/Chain/foo/wire.go +++ b/pkg/build/wire/internal/wire/testdata/Chain/foo/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/Cleanup/foo/wire.go b/pkg/build/wire/internal/wire/testdata/Cleanup/foo/wire.go index 49867e8c1b1..2e4a8a97ff8 100644 --- a/pkg/build/wire/internal/wire/testdata/Cleanup/foo/wire.go +++ b/pkg/build/wire/internal/wire/testdata/Cleanup/foo/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/CopyOtherDecls/foo/foo.go b/pkg/build/wire/internal/wire/testdata/CopyOtherDecls/foo/foo.go index e8045b254e8..514519b27dd 100644 --- a/pkg/build/wire/internal/wire/testdata/CopyOtherDecls/foo/foo.go +++ b/pkg/build/wire/internal/wire/testdata/CopyOtherDecls/foo/foo.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject // All of the declarations are in one file. // Wire should copy non-injectors over, preserving imports. diff --git a/pkg/build/wire/internal/wire/testdata/Cycle/foo/wire.go b/pkg/build/wire/internal/wire/testdata/Cycle/foo/wire.go index b89cdff87d2..b32c0d8a154 100644 --- a/pkg/build/wire/internal/wire/testdata/Cycle/foo/wire.go +++ b/pkg/build/wire/internal/wire/testdata/Cycle/foo/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/DocComment/foo/wire.go b/pkg/build/wire/internal/wire/testdata/DocComment/foo/wire.go index 9a17664592d..bb5462cd03f 100644 --- a/pkg/build/wire/internal/wire/testdata/DocComment/foo/wire.go +++ b/pkg/build/wire/internal/wire/testdata/DocComment/foo/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. // -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/EmptyVar/foo/wire.go b/pkg/build/wire/internal/wire/testdata/EmptyVar/foo/wire.go index f1db1a4db8f..c3ed4421f36 100644 --- a/pkg/build/wire/internal/wire/testdata/EmptyVar/foo/wire.go +++ b/pkg/build/wire/internal/wire/testdata/EmptyVar/foo/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/ExampleWithMocks/foo/wire.go b/pkg/build/wire/internal/wire/testdata/ExampleWithMocks/foo/wire.go index bdec16a6806..258c96278b9 100644 --- a/pkg/build/wire/internal/wire/testdata/ExampleWithMocks/foo/wire.go +++ b/pkg/build/wire/internal/wire/testdata/ExampleWithMocks/foo/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/ExportedValue/foo/wire.go b/pkg/build/wire/internal/wire/testdata/ExportedValue/foo/wire.go index 46f7af5bf5f..fb455bcad76 100644 --- a/pkg/build/wire/internal/wire/testdata/ExportedValue/foo/wire.go +++ b/pkg/build/wire/internal/wire/testdata/ExportedValue/foo/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/ExportedValueDifferentPackage/foo/wire.go b/pkg/build/wire/internal/wire/testdata/ExportedValueDifferentPackage/foo/wire.go index 1da10a3455b..f51c3076d9b 100644 --- a/pkg/build/wire/internal/wire/testdata/ExportedValueDifferentPackage/foo/wire.go +++ b/pkg/build/wire/internal/wire/testdata/ExportedValueDifferentPackage/foo/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/FieldsOfCycle/foo/wire.go b/pkg/build/wire/internal/wire/testdata/FieldsOfCycle/foo/wire.go index b49dc720a42..6f385096306 100644 --- a/pkg/build/wire/internal/wire/testdata/FieldsOfCycle/foo/wire.go +++ b/pkg/build/wire/internal/wire/testdata/FieldsOfCycle/foo/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/FieldsOfImportedStruct/main/wire.go b/pkg/build/wire/internal/wire/testdata/FieldsOfImportedStruct/main/wire.go index 15646542ee2..b42acb70c0f 100644 --- a/pkg/build/wire/internal/wire/testdata/FieldsOfImportedStruct/main/wire.go +++ b/pkg/build/wire/internal/wire/testdata/FieldsOfImportedStruct/main/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/FieldsOfStruct/foo/wire.go b/pkg/build/wire/internal/wire/testdata/FieldsOfStruct/foo/wire.go index 7b35bd86b56..de6a11293cd 100644 --- a/pkg/build/wire/internal/wire/testdata/FieldsOfStruct/foo/wire.go +++ b/pkg/build/wire/internal/wire/testdata/FieldsOfStruct/foo/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/FieldsOfStructDoNotProvidePtrToField/foo/wire.go b/pkg/build/wire/internal/wire/testdata/FieldsOfStructDoNotProvidePtrToField/foo/wire.go index 271ee817094..697e8484764 100644 --- a/pkg/build/wire/internal/wire/testdata/FieldsOfStructDoNotProvidePtrToField/foo/wire.go +++ b/pkg/build/wire/internal/wire/testdata/FieldsOfStructDoNotProvidePtrToField/foo/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/FieldsOfStructPointer/foo/wire.go b/pkg/build/wire/internal/wire/testdata/FieldsOfStructPointer/foo/wire.go index 04ebb253f10..b3e507daaad 100644 --- a/pkg/build/wire/internal/wire/testdata/FieldsOfStructPointer/foo/wire.go +++ b/pkg/build/wire/internal/wire/testdata/FieldsOfStructPointer/foo/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/FieldsOfValueStruct/main/wire.go b/pkg/build/wire/internal/wire/testdata/FieldsOfValueStruct/main/wire.go index d8ccb5caaeb..50973ee3424 100644 --- a/pkg/build/wire/internal/wire/testdata/FieldsOfValueStruct/main/wire.go +++ b/pkg/build/wire/internal/wire/testdata/FieldsOfValueStruct/main/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/FuncArgProvider/foo/wire.go b/pkg/build/wire/internal/wire/testdata/FuncArgProvider/foo/wire.go index ae1011bd26c..93561a26718 100644 --- a/pkg/build/wire/internal/wire/testdata/FuncArgProvider/foo/wire.go +++ b/pkg/build/wire/internal/wire/testdata/FuncArgProvider/foo/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/Header/foo/wire.go b/pkg/build/wire/internal/wire/testdata/Header/foo/wire.go index 4ad2705fc59..91b6bcf8b26 100644 --- a/pkg/build/wire/internal/wire/testdata/Header/foo/wire.go +++ b/pkg/build/wire/internal/wire/testdata/Header/foo/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/ImportedInterfaceBinding/bar/wire.go b/pkg/build/wire/internal/wire/testdata/ImportedInterfaceBinding/bar/wire.go index 952f0753b09..af32bfa8e7a 100644 --- a/pkg/build/wire/internal/wire/testdata/ImportedInterfaceBinding/bar/wire.go +++ b/pkg/build/wire/internal/wire/testdata/ImportedInterfaceBinding/bar/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/InjectInput/foo/wire.go b/pkg/build/wire/internal/wire/testdata/InjectInput/foo/wire.go index 0c1bf839fdd..9f2c89d27ea 100644 --- a/pkg/build/wire/internal/wire/testdata/InjectInput/foo/wire.go +++ b/pkg/build/wire/internal/wire/testdata/InjectInput/foo/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/InjectInputConflict/foo/wire.go b/pkg/build/wire/internal/wire/testdata/InjectInputConflict/foo/wire.go index 24308cb35b7..36803e8524b 100644 --- a/pkg/build/wire/internal/wire/testdata/InjectInputConflict/foo/wire.go +++ b/pkg/build/wire/internal/wire/testdata/InjectInputConflict/foo/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/InjectWithPanic/foo/wire.go b/pkg/build/wire/internal/wire/testdata/InjectWithPanic/foo/wire.go index 349b7746904..b6878192e70 100644 --- a/pkg/build/wire/internal/wire/testdata/InjectWithPanic/foo/wire.go +++ b/pkg/build/wire/internal/wire/testdata/InjectWithPanic/foo/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/InjectorMissingCleanup/foo/wire.go b/pkg/build/wire/internal/wire/testdata/InjectorMissingCleanup/foo/wire.go index 932a9fa7914..c93a1808041 100644 --- a/pkg/build/wire/internal/wire/testdata/InjectorMissingCleanup/foo/wire.go +++ b/pkg/build/wire/internal/wire/testdata/InjectorMissingCleanup/foo/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/InjectorMissingError/foo/wire.go b/pkg/build/wire/internal/wire/testdata/InjectorMissingError/foo/wire.go index 2dced82e731..33d26ce8fbe 100644 --- a/pkg/build/wire/internal/wire/testdata/InjectorMissingError/foo/wire.go +++ b/pkg/build/wire/internal/wire/testdata/InjectorMissingError/foo/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/InterfaceBinding/foo/wire.go b/pkg/build/wire/internal/wire/testdata/InterfaceBinding/foo/wire.go index 250d8adc18e..f647e016a89 100644 --- a/pkg/build/wire/internal/wire/testdata/InterfaceBinding/foo/wire.go +++ b/pkg/build/wire/internal/wire/testdata/InterfaceBinding/foo/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/InterfaceBindingDoesntImplement/foo/wire.go b/pkg/build/wire/internal/wire/testdata/InterfaceBindingDoesntImplement/foo/wire.go index 492e3c2a5b7..910d3c6f8da 100644 --- a/pkg/build/wire/internal/wire/testdata/InterfaceBindingDoesntImplement/foo/wire.go +++ b/pkg/build/wire/internal/wire/testdata/InterfaceBindingDoesntImplement/foo/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/InterfaceBindingInvalidArg0/foo/wire.go b/pkg/build/wire/internal/wire/testdata/InterfaceBindingInvalidArg0/foo/wire.go index 97f6b77ffdd..05ea89e45a4 100644 --- a/pkg/build/wire/internal/wire/testdata/InterfaceBindingInvalidArg0/foo/wire.go +++ b/pkg/build/wire/internal/wire/testdata/InterfaceBindingInvalidArg0/foo/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/InterfaceBindingNotEnoughArgs/foo/wire.go b/pkg/build/wire/internal/wire/testdata/InterfaceBindingNotEnoughArgs/foo/wire.go index 15a3322e589..060e2ba3275 100644 --- a/pkg/build/wire/internal/wire/testdata/InterfaceBindingNotEnoughArgs/foo/wire.go +++ b/pkg/build/wire/internal/wire/testdata/InterfaceBindingNotEnoughArgs/foo/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/InterfaceBindingReuse/foo/wire.go b/pkg/build/wire/internal/wire/testdata/InterfaceBindingReuse/foo/wire.go index b342b3b6f16..4c7f71daaf6 100644 --- a/pkg/build/wire/internal/wire/testdata/InterfaceBindingReuse/foo/wire.go +++ b/pkg/build/wire/internal/wire/testdata/InterfaceBindingReuse/foo/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/InterfaceValue/foo/wire.go b/pkg/build/wire/internal/wire/testdata/InterfaceValue/foo/wire.go index 5ed782b930d..8c221c36fb2 100644 --- a/pkg/build/wire/internal/wire/testdata/InterfaceValue/foo/wire.go +++ b/pkg/build/wire/internal/wire/testdata/InterfaceValue/foo/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/InterfaceValueDoesntImplement/foo/wire.go b/pkg/build/wire/internal/wire/testdata/InterfaceValueDoesntImplement/foo/wire.go index c9ce4a302fc..d6e14b8a908 100644 --- a/pkg/build/wire/internal/wire/testdata/InterfaceValueDoesntImplement/foo/wire.go +++ b/pkg/build/wire/internal/wire/testdata/InterfaceValueDoesntImplement/foo/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/InterfaceValueInvalidArg0/foo/wire.go b/pkg/build/wire/internal/wire/testdata/InterfaceValueInvalidArg0/foo/wire.go index 69985206fb5..d937e27b214 100644 --- a/pkg/build/wire/internal/wire/testdata/InterfaceValueInvalidArg0/foo/wire.go +++ b/pkg/build/wire/internal/wire/testdata/InterfaceValueInvalidArg0/foo/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/InterfaceValueNotEnoughArgs/foo/wire.go b/pkg/build/wire/internal/wire/testdata/InterfaceValueNotEnoughArgs/foo/wire.go index 8d7b884d461..2d9c4818dba 100644 --- a/pkg/build/wire/internal/wire/testdata/InterfaceValueNotEnoughArgs/foo/wire.go +++ b/pkg/build/wire/internal/wire/testdata/InterfaceValueNotEnoughArgs/foo/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/InvalidInjector/foo/wire.go b/pkg/build/wire/internal/wire/testdata/InvalidInjector/foo/wire.go index 8c67b41e08d..2aae75c709b 100644 --- a/pkg/build/wire/internal/wire/testdata/InvalidInjector/foo/wire.go +++ b/pkg/build/wire/internal/wire/testdata/InvalidInjector/foo/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/MultipleArgsSameType/foo/wire.go b/pkg/build/wire/internal/wire/testdata/MultipleArgsSameType/foo/wire.go index 22d939c7cd1..c3414c5bbbc 100644 --- a/pkg/build/wire/internal/wire/testdata/MultipleArgsSameType/foo/wire.go +++ b/pkg/build/wire/internal/wire/testdata/MultipleArgsSameType/foo/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/MultipleBindings/foo/wire.go b/pkg/build/wire/internal/wire/testdata/MultipleBindings/foo/wire.go index 5bc1500b05d..81d8673ce0a 100644 --- a/pkg/build/wire/internal/wire/testdata/MultipleBindings/foo/wire.go +++ b/pkg/build/wire/internal/wire/testdata/MultipleBindings/foo/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/MultipleMissingInputs/foo/wire.go b/pkg/build/wire/internal/wire/testdata/MultipleMissingInputs/foo/wire.go index de8c23907a7..68a6ce0adc5 100644 --- a/pkg/build/wire/internal/wire/testdata/MultipleMissingInputs/foo/wire.go +++ b/pkg/build/wire/internal/wire/testdata/MultipleMissingInputs/foo/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/MultipleSimilarPackages/main/wire.go b/pkg/build/wire/internal/wire/testdata/MultipleSimilarPackages/main/wire.go index fb501789ed4..3a8d02b21e3 100644 --- a/pkg/build/wire/internal/wire/testdata/MultipleSimilarPackages/main/wire.go +++ b/pkg/build/wire/internal/wire/testdata/MultipleSimilarPackages/main/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/NamingWorstCase/foo/wire.go b/pkg/build/wire/internal/wire/testdata/NamingWorstCase/foo/wire.go index 78cc79105f0..ca2d14ca594 100644 --- a/pkg/build/wire/internal/wire/testdata/NamingWorstCase/foo/wire.go +++ b/pkg/build/wire/internal/wire/testdata/NamingWorstCase/foo/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/NamingWorstCaseAllInOne/foo/foo.go b/pkg/build/wire/internal/wire/testdata/NamingWorstCaseAllInOne/foo/foo.go index 0c814f01bef..7f4b32e9a0e 100644 --- a/pkg/build/wire/internal/wire/testdata/NamingWorstCaseAllInOne/foo/foo.go +++ b/pkg/build/wire/internal/wire/testdata/NamingWorstCaseAllInOne/foo/foo.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject // This file is specifically designed to cause issues with copying the // AST, particularly with the identifier "context". diff --git a/pkg/build/wire/internal/wire/testdata/NiladicIdentity/foo/wire.go b/pkg/build/wire/internal/wire/testdata/NiladicIdentity/foo/wire.go index 0570bb2a3bd..7df0fda0aa5 100644 --- a/pkg/build/wire/internal/wire/testdata/NiladicIdentity/foo/wire.go +++ b/pkg/build/wire/internal/wire/testdata/NiladicIdentity/foo/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/NiladicValue/foo/wire.go b/pkg/build/wire/internal/wire/testdata/NiladicValue/foo/wire.go index 50e62af7d3f..d4e3b817299 100644 --- a/pkg/build/wire/internal/wire/testdata/NiladicValue/foo/wire.go +++ b/pkg/build/wire/internal/wire/testdata/NiladicValue/foo/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/NoImplicitInterface/foo/wire.go b/pkg/build/wire/internal/wire/testdata/NoImplicitInterface/foo/wire.go index f4cd412752d..30463e15f5e 100644 --- a/pkg/build/wire/internal/wire/testdata/NoImplicitInterface/foo/wire.go +++ b/pkg/build/wire/internal/wire/testdata/NoImplicitInterface/foo/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/NoInjectParamNames/foo/wire.go b/pkg/build/wire/internal/wire/testdata/NoInjectParamNames/foo/wire.go index 758cb5fb093..49c5bd7ea5d 100644 --- a/pkg/build/wire/internal/wire/testdata/NoInjectParamNames/foo/wire.go +++ b/pkg/build/wire/internal/wire/testdata/NoInjectParamNames/foo/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/PartialCleanup/foo/wire.go b/pkg/build/wire/internal/wire/testdata/PartialCleanup/foo/wire.go index 5d52e9243c7..2f6ba1e5756 100644 --- a/pkg/build/wire/internal/wire/testdata/PartialCleanup/foo/wire.go +++ b/pkg/build/wire/internal/wire/testdata/PartialCleanup/foo/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/PkgImport/foo/wire.go b/pkg/build/wire/internal/wire/testdata/PkgImport/foo/wire.go index 4f9c9d9a7ba..b7d7a58d37a 100644 --- a/pkg/build/wire/internal/wire/testdata/PkgImport/foo/wire.go +++ b/pkg/build/wire/internal/wire/testdata/PkgImport/foo/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/ProviderSetBindingMissingConcreteType/foo/wire.go b/pkg/build/wire/internal/wire/testdata/ProviderSetBindingMissingConcreteType/foo/wire.go index 2e765205481..1e6567663b2 100644 --- a/pkg/build/wire/internal/wire/testdata/ProviderSetBindingMissingConcreteType/foo/wire.go +++ b/pkg/build/wire/internal/wire/testdata/ProviderSetBindingMissingConcreteType/foo/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/RelativePkg/foo/wire.go b/pkg/build/wire/internal/wire/testdata/RelativePkg/foo/wire.go index 0570bb2a3bd..7df0fda0aa5 100644 --- a/pkg/build/wire/internal/wire/testdata/RelativePkg/foo/wire.go +++ b/pkg/build/wire/internal/wire/testdata/RelativePkg/foo/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/ReservedKeywords/foo/wire.go b/pkg/build/wire/internal/wire/testdata/ReservedKeywords/foo/wire.go index c375ee159f7..6c54496eb52 100644 --- a/pkg/build/wire/internal/wire/testdata/ReservedKeywords/foo/wire.go +++ b/pkg/build/wire/internal/wire/testdata/ReservedKeywords/foo/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/ReturnArgumentAsInterface/foo/wire.go b/pkg/build/wire/internal/wire/testdata/ReturnArgumentAsInterface/foo/wire.go index fb79f9ceb41..d311052cf63 100644 --- a/pkg/build/wire/internal/wire/testdata/ReturnArgumentAsInterface/foo/wire.go +++ b/pkg/build/wire/internal/wire/testdata/ReturnArgumentAsInterface/foo/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/ReturnError/foo/wire.go b/pkg/build/wire/internal/wire/testdata/ReturnError/foo/wire.go index a729e826273..56d379b986a 100644 --- a/pkg/build/wire/internal/wire/testdata/ReturnError/foo/wire.go +++ b/pkg/build/wire/internal/wire/testdata/ReturnError/foo/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/Struct/foo/wire.go b/pkg/build/wire/internal/wire/testdata/Struct/foo/wire.go index 29ef937ddfc..4ec713fe569 100644 --- a/pkg/build/wire/internal/wire/testdata/Struct/foo/wire.go +++ b/pkg/build/wire/internal/wire/testdata/Struct/foo/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/StructNotAStruct/foo/wire.go b/pkg/build/wire/internal/wire/testdata/StructNotAStruct/foo/wire.go index 7a84cdb9dcb..068b6fa2f2e 100644 --- a/pkg/build/wire/internal/wire/testdata/StructNotAStruct/foo/wire.go +++ b/pkg/build/wire/internal/wire/testdata/StructNotAStruct/foo/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/StructPointer/foo/wire.go b/pkg/build/wire/internal/wire/testdata/StructPointer/foo/wire.go index c08f942eb8b..2554df47944 100644 --- a/pkg/build/wire/internal/wire/testdata/StructPointer/foo/wire.go +++ b/pkg/build/wire/internal/wire/testdata/StructPointer/foo/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/StructWithPreventTag/foo/wire.go b/pkg/build/wire/internal/wire/testdata/StructWithPreventTag/foo/wire.go index 5e0d5800859..0ad39062cf6 100644 --- a/pkg/build/wire/internal/wire/testdata/StructWithPreventTag/foo/wire.go +++ b/pkg/build/wire/internal/wire/testdata/StructWithPreventTag/foo/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/TwoDeps/foo/wire.go b/pkg/build/wire/internal/wire/testdata/TwoDeps/foo/wire.go index 4106dd8d91f..19e87988007 100644 --- a/pkg/build/wire/internal/wire/testdata/TwoDeps/foo/wire.go +++ b/pkg/build/wire/internal/wire/testdata/TwoDeps/foo/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/UnexportedStruct/foo/wire.go b/pkg/build/wire/internal/wire/testdata/UnexportedStruct/foo/wire.go index e0b4618c316..e15b4d88acc 100644 --- a/pkg/build/wire/internal/wire/testdata/UnexportedStruct/foo/wire.go +++ b/pkg/build/wire/internal/wire/testdata/UnexportedStruct/foo/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/UnexportedValue/foo/wire.go b/pkg/build/wire/internal/wire/testdata/UnexportedValue/foo/wire.go index a26cbf97b25..7fd5280932b 100644 --- a/pkg/build/wire/internal/wire/testdata/UnexportedValue/foo/wire.go +++ b/pkg/build/wire/internal/wire/testdata/UnexportedValue/foo/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/UnusedProviders/foo/wire.go b/pkg/build/wire/internal/wire/testdata/UnusedProviders/foo/wire.go index 681b80d10ff..1adae2b9d05 100644 --- a/pkg/build/wire/internal/wire/testdata/UnusedProviders/foo/wire.go +++ b/pkg/build/wire/internal/wire/testdata/UnusedProviders/foo/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/ValueChain/foo/wire.go b/pkg/build/wire/internal/wire/testdata/ValueChain/foo/wire.go index 4106dd8d91f..19e87988007 100644 --- a/pkg/build/wire/internal/wire/testdata/ValueChain/foo/wire.go +++ b/pkg/build/wire/internal/wire/testdata/ValueChain/foo/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/ValueConversion/foo/wire.go b/pkg/build/wire/internal/wire/testdata/ValueConversion/foo/wire.go index a389be1b630..9a2b672648d 100644 --- a/pkg/build/wire/internal/wire/testdata/ValueConversion/foo/wire.go +++ b/pkg/build/wire/internal/wire/testdata/ValueConversion/foo/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/ValueFromFunctionScope/foo/wire.go b/pkg/build/wire/internal/wire/testdata/ValueFromFunctionScope/foo/wire.go index e2ca3f2f3e4..8fa89493472 100644 --- a/pkg/build/wire/internal/wire/testdata/ValueFromFunctionScope/foo/wire.go +++ b/pkg/build/wire/internal/wire/testdata/ValueFromFunctionScope/foo/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/ValueIsInterfaceValue/foo/wire.go b/pkg/build/wire/internal/wire/testdata/ValueIsInterfaceValue/foo/wire.go index eb548fa1f00..282e6f9f041 100644 --- a/pkg/build/wire/internal/wire/testdata/ValueIsInterfaceValue/foo/wire.go +++ b/pkg/build/wire/internal/wire/testdata/ValueIsInterfaceValue/foo/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/ValueIsStruct/foo/wire.go b/pkg/build/wire/internal/wire/testdata/ValueIsStruct/foo/wire.go index 51633dd0626..27620781ea0 100644 --- a/pkg/build/wire/internal/wire/testdata/ValueIsStruct/foo/wire.go +++ b/pkg/build/wire/internal/wire/testdata/ValueIsStruct/foo/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/VarValue/foo/wire.go b/pkg/build/wire/internal/wire/testdata/VarValue/foo/wire.go index e8a718a2e26..6b2e219aca6 100644 --- a/pkg/build/wire/internal/wire/testdata/VarValue/foo/wire.go +++ b/pkg/build/wire/internal/wire/testdata/VarValue/foo/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/Varargs/foo/wire.go b/pkg/build/wire/internal/wire/testdata/Varargs/foo/wire.go index 33d7acc5f45..0843aef8f68 100644 --- a/pkg/build/wire/internal/wire/testdata/Varargs/foo/wire.go +++ b/pkg/build/wire/internal/wire/testdata/Varargs/foo/wire.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//+build wireinject +//go:build wireinject +// +build wireinject package main diff --git a/pkg/storage/unified/resource/search_test.go b/pkg/storage/unified/resource/search_test.go index 45c32f2e462..091110f2c4e 100644 --- a/pkg/storage/unified/resource/search_test.go +++ b/pkg/storage/unified/resource/search_test.go @@ -382,7 +382,7 @@ func TestSearchGetOrCreateIndexWithIndexUpdate(t *testing.T) { buildEmptyIndexCalls: []buildEmptyIndexCall{}, cache: map[NamespacedResource]ResourceIndex{ - NamespacedResource{Namespace: "ns", Group: "group", Resource: "bad"}: &MockResourceIndex{ + {Namespace: "ns", Group: "group", Resource: "bad"}: &MockResourceIndex{ updateIndexError: failedErr, }, }, diff --git a/pkg/storage/unified/testing/storage_backend.go b/pkg/storage/unified/testing/storage_backend.go index 5488b44a144..4ae283c1f38 100644 --- a/pkg/storage/unified/testing/storage_backend.go +++ b/pkg/storage/unified/testing/storage_backend.go @@ -524,7 +524,7 @@ func runTestIntegrationBackendListModifiedSince(t *testing.T, backend resource.S require.GreaterOrEqual(t, latestRv, rvDeleted) counter := 0 - for _, _ = range seq { + for range seq { counter++ } require.Equal(t, 0, counter) // no events should be returned From 2cba22c09224ee3ef5de0e8eba15aa4aeb93611c Mon Sep 17 00:00:00 2001 From: Kim Nylander <104772500+knylander-grafana@users.noreply.github.com> Date: Tue, 26 Aug 2025 08:14:41 -0600 Subject: [PATCH 09/24] [DOC] Tempo DS: Clone a provisioned data source doc (#110114) * Tempo DS: Clone a provisioned data source doc * Chagnes from prettier * Update docs/sources/datasources/tempo/configure-tempo-data-source.md --- .../tempo/configure-tempo-data-source.md | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/docs/sources/datasources/tempo/configure-tempo-data-source.md b/docs/sources/datasources/tempo/configure-tempo-data-source.md index 620459d765b..84320575ed0 100644 --- a/docs/sources/datasources/tempo/configure-tempo-data-source.md +++ b/docs/sources/datasources/tempo/configure-tempo-data-source.md @@ -48,7 +48,7 @@ refs: - pattern: /docs/grafana/ destination: /docs/grafana//administration/provisioning/#data-sources - pattern: /docs/grafana-cloud/provision - destination: /docs/grafana//administration/provisioning/#data-sources + destination: /docs/grafana/next/administration/provisioning/#data-sources explore: - pattern: /docs/grafana/ destination: /docs/grafana//explore/ @@ -81,6 +81,13 @@ Refer to [Provision the data source](#provision-the-data-source) for next steps. You can use these procedures to configure a new Tempo data source or to edit an existing one. +{{< admonition type="note" >}} +You can't modify a provisioned data source using the Tempo data source settings in Grafana Cloud. + +If you want to modify any capabilities of a provisioned data source, you can clone the provisioned data source and then edit the new data source in the Grafana UI. +Refer to [Clone a provisioned data source for Grafana Cloud](#clone-a-provisioned-data-source-for-grafana-cloud) for more information. +{{< /admonition >}} + ### Add a new data source Follow these steps to set up a new Tempo data source: @@ -425,3 +432,21 @@ datasources: streamingEnabled: search: true ``` + +### Clone a provisioned data source for Grafana Cloud + +If you have a data source that is provisioned by a configuration file in Grafana Cloud, you can clone that provisioned data source and then edit the new data source in the Grafana UI. + +For example, let's say you want to edit the **Trace to log**s settings in your Tempo data source that is provisioned on Grafana Cloud. +You'd like to enable traceID and spanID but you can't because the data source is provisioned. +By cloning the data source, you'd be able to edit these capabilities. + +To clone a provisioned data source, follow these steps: + +1. Create a viewer [Cloud Access Policy token](https://grafana.com/docs/grafana-cloud/security-and-account-management/authentication-and-permissions/access-policies/) in the Grafana Cloud Portal, making sure it has read permissions at least for the data types you are trying to clone. +1. [Create a new data source](#add-a-new-data-source) of the same type you want to clone. +1. Copy all of the settings from the existing provisioned data source into the new data source while replacing the password with the API key you created. + +The easiest way to do this is to open separate browser windows with the provisioned data source in one and the newly created data source in another. + +Of course, after copying the HTTP and Auth section details, pasting the Cloud Access Policy token into the Password field, and changing any of the other options that you want, you can save and test the data source. From 60f31342c0d2ea1ea1c471192ae2f067f6f41336 Mon Sep 17 00:00:00 2001 From: Matheus Macabu Date: Tue, 26 Aug 2025 16:24:26 +0200 Subject: [PATCH 10/24] Secrets: Remove proxy certs (#110140) --- pkg/registry/apis/secret/decrypt/grpc_client.go | 10 ---------- pkg/registry/apis/secret/decrypt/service.go | 6 +----- pkg/registry/apis/secret/decrypt/service_test.go | 4 ---- pkg/registry/apis/secret/inline/grpc_client.go | 10 ---------- pkg/registry/apis/secret/inline/service.go | 6 +----- pkg/registry/apis/secret/inline/service_test.go | 4 ---- pkg/services/apiserver/options/storage.go | 4 ---- 7 files changed, 2 insertions(+), 42 deletions(-) diff --git a/pkg/registry/apis/secret/decrypt/grpc_client.go b/pkg/registry/apis/secret/decrypt/grpc_client.go index 9919ec67f9b..2f3a2faa919 100644 --- a/pkg/registry/apis/secret/decrypt/grpc_client.go +++ b/pkg/registry/apis/secret/decrypt/grpc_client.go @@ -34,8 +34,6 @@ var _ contracts.DecryptService = &GRPCDecryptClient{} type TLSConfig struct { UseTLS bool - CertFile string - KeyFile string CAFile string ServerName string InsecureSkipVerify bool @@ -91,14 +89,6 @@ func createTLSCredentials(config TLSConfig) (credentials.TransportCredentials, e tlsConfig.RootCAs = caCertPool } - if config.CertFile != "" && config.KeyFile != "" { - cert, err := tls.LoadX509KeyPair(config.CertFile, config.KeyFile) - if err != nil { - return nil, fmt.Errorf("failed to load client certificate: %w", err) - } - tlsConfig.Certificates = []tls.Certificate{cert} - } - if config.ServerName != "" { tlsConfig.ServerName = config.ServerName } diff --git a/pkg/registry/apis/secret/decrypt/service.go b/pkg/registry/apis/secret/decrypt/service.go index 28a247ae103..983c06f542f 100644 --- a/pkg/registry/apis/secret/decrypt/service.go +++ b/pkg/registry/apis/secret/decrypt/service.go @@ -52,13 +52,9 @@ func readTLSFromConfig(cfg *setting.Cfg) TLSConfig { } } - apiServer := cfg.SectionWithEnvOverrides("grafana-apiserver") - return TLSConfig{ UseTLS: true, - CertFile: apiServer.Key("proxy_client_cert_file").MustString(""), - KeyFile: apiServer.Key("proxy_client_key_file").MustString(""), - CAFile: apiServer.Key("apiservice_ca_bundle_file").MustString(""), + CAFile: cfg.SectionWithEnvOverrides("grafana-apiserver").Key("apiservice_ca_bundle_file").MustString(""), ServerName: cfg.SecretsManagement.GrpcServerTLSServerName, InsecureSkipVerify: cfg.SecretsManagement.GrpcServerTLSSkipVerify, } diff --git a/pkg/registry/apis/secret/decrypt/service_test.go b/pkg/registry/apis/secret/decrypt/service_test.go index e1fd85a6fe3..077e481e112 100644 --- a/pkg/registry/apis/secret/decrypt/service_test.go +++ b/pkg/registry/apis/secret/decrypt/service_test.go @@ -207,10 +207,6 @@ func TestDecryptService(t *testing.T) { require.NoError(t, err) apiServer := cfg.Raw.Section("grafana-apiserver") - _, err = apiServer.NewKey("proxy_client_cert_file", certPaths.ClientCert) - require.NoError(t, err) - _, err = apiServer.NewKey("proxy_client_key_file", certPaths.ClientKey) - require.NoError(t, err) _, err = apiServer.NewKey("apiservice_ca_bundle_file", certPaths.CA) require.NoError(t, err) diff --git a/pkg/registry/apis/secret/inline/grpc_client.go b/pkg/registry/apis/secret/inline/grpc_client.go index 9ca027e2c00..7b9157aef80 100644 --- a/pkg/registry/apis/secret/inline/grpc_client.go +++ b/pkg/registry/apis/secret/inline/grpc_client.go @@ -31,8 +31,6 @@ var _ contracts.InlineSecureValueSupport = &GRPCInlineClient{} type TLSConfig struct { UseTLS bool - CertFile string - KeyFile string CAFile string ServerName string InsecureSkipVerify bool @@ -79,14 +77,6 @@ func createTLSCredentials(config TLSConfig) (credentials.TransportCredentials, e tlsConfig.RootCAs = caCertPool } - if config.CertFile != "" && config.KeyFile != "" { - cert, err := tls.LoadX509KeyPair(config.CertFile, config.KeyFile) - if err != nil { - return nil, fmt.Errorf("failed to load client certificate: %w", err) - } - tlsConfig.Certificates = []tls.Certificate{cert} - } - if config.ServerName != "" { tlsConfig.ServerName = config.ServerName } diff --git a/pkg/registry/apis/secret/inline/service.go b/pkg/registry/apis/secret/inline/service.go index 8d48dfa8fb9..73b6798a7de 100644 --- a/pkg/registry/apis/secret/inline/service.go +++ b/pkg/registry/apis/secret/inline/service.go @@ -67,13 +67,9 @@ func readTLSFromConfig(cfg *setting.Cfg) TLSConfig { } } - apiServer := cfg.SectionWithEnvOverrides("grafana-apiserver") - return TLSConfig{ UseTLS: true, - CertFile: apiServer.Key("proxy_client_cert_file").MustString(""), - KeyFile: apiServer.Key("proxy_client_key_file").MustString(""), - CAFile: apiServer.Key("apiservice_ca_bundle_file").MustString(""), + CAFile: cfg.SectionWithEnvOverrides("grafana-apiserver").Key("apiservice_ca_bundle_file").MustString(""), ServerName: cfg.SecretsManagement.GrpcServerTLSServerName, InsecureSkipVerify: cfg.SecretsManagement.GrpcServerTLSSkipVerify, } diff --git a/pkg/registry/apis/secret/inline/service_test.go b/pkg/registry/apis/secret/inline/service_test.go index 9fc7679c2ad..aad28e6c973 100644 --- a/pkg/registry/apis/secret/inline/service_test.go +++ b/pkg/registry/apis/secret/inline/service_test.go @@ -128,10 +128,6 @@ func TestProvideInlineSecureValueService(t *testing.T) { require.NoError(t, err) apiServer := cfg.Raw.Section("grafana-apiserver") - _, err = apiServer.NewKey("proxy_client_cert_file", certPaths.ClientCert) - require.NoError(t, err) - _, err = apiServer.NewKey("proxy_client_key_file", certPaths.ClientKey) - require.NoError(t, err) _, err = apiServer.NewKey("apiservice_ca_bundle_file", certPaths.CA) require.NoError(t, err) diff --git a/pkg/services/apiserver/options/storage.go b/pkg/services/apiserver/options/storage.go index db4a0fe891d..f692c40b293 100644 --- a/pkg/services/apiserver/options/storage.go +++ b/pkg/services/apiserver/options/storage.go @@ -204,10 +204,6 @@ func (o *StorageOptions) ApplyTo(serverConfig *genericapiserver.RecommendedConfi ServerName: o.SecretsManagerGrpcServerTLSServerName, InsecureSkipVerify: o.SecretsManagerGrpcServerTLSSkipVerify, } - if o.SecretsManagerGrpcServerUseTLS && secureServing != nil { - tlsCfg.CertFile = secureServing.ServerCert.CertKey.CertFile - tlsCfg.KeyFile = secureServing.ServerCert.CertKey.KeyFile - } inlineSecureValueService, err := inlinesecurevalue.NewGRPCSecureValueService( &grpcutils.GrpcClientConfig{ Token: o.GrpcClientAuthenticationToken, From 3eebe43c6d2c5d1a9ff1d7dbce8073cbbf0be61a Mon Sep 17 00:00:00 2001 From: Mihai Turdean <6640685+mihai-turdean@users.noreply.github.com> Date: Tue, 26 Aug 2025 08:34:15 -0600 Subject: [PATCH 11/24] Slight refactor of Zanzana GRPC Client to use it in the IAM Folder Operator (#110120) --- apps/iam/cmd/operator/config.go | 12 +- apps/iam/cmd/operator/main.go | 2 +- apps/iam/go.mod | 102 +++++++++++++- apps/iam/go.sum | 124 +++++++++++++++++- apps/iam/pkg/app/app.go | 21 ++- apps/iam/pkg/reconcilers/folder_reconciler.go | 52 ++------ pkg/services/authz/zanzana.go | 91 ++++++++----- 7 files changed, 313 insertions(+), 91 deletions(-) diff --git a/apps/iam/cmd/operator/config.go b/apps/iam/cmd/operator/config.go index 7cb1133851c..939399699c5 100644 --- a/apps/iam/cmd/operator/config.go +++ b/apps/iam/cmd/operator/config.go @@ -8,6 +8,7 @@ import ( "github.com/grafana/grafana-app-sdk/plugin/kubeconfig" "github.com/grafana/grafana-app-sdk/simple" + "github.com/grafana/grafana/pkg/services/authz" ) const ( @@ -19,7 +20,7 @@ type Config struct { OTelConfig simple.OpenTelemetryConfig WebhookServer WebhookServerConfig KubeConfig *kubeconfig.NamespacedConfig - ZanzanaClient ZanzanaClientConfig + ZanzanaClient authz.ZanzanaClientConfig FolderReconciler FolderReconcilerConfig } @@ -29,10 +30,6 @@ type WebhookServerConfig struct { TLSKeyPath string } -type ZanzanaClientConfig struct { - Addr string -} - type FolderReconcilerConfig struct { Namespace string } @@ -112,7 +109,10 @@ func LoadConfigFromEnv() (*Config, error) { cfg.KubeConfig = kubeConfig } - cfg.ZanzanaClient.Addr = os.Getenv("ZANZANA_ADDR") + cfg.ZanzanaClient.Address = os.Getenv("ZANZANA_ADDR") + cfg.ZanzanaClient.Token = os.Getenv("ZANZANA_TOKEN") + cfg.ZanzanaClient.TokenExchangeURL = os.Getenv("ZANZANA_TOKEN_EXCHANGE_URL") + cfg.ZanzanaClient.ServerCertFile = os.Getenv("ZANZANA_SERVER_CERT_FILE") cfg.FolderReconciler.Namespace = os.Getenv("FOLDER_RECONCILER_NAMESPACE") diff --git a/apps/iam/cmd/operator/main.go b/apps/iam/cmd/operator/main.go index dd4f770e1f8..6d4a0982f9d 100644 --- a/apps/iam/cmd/operator/main.go +++ b/apps/iam/cmd/operator/main.go @@ -67,7 +67,7 @@ func main() { // Create app config from operator config appCfg := app.AppConfig{ - ZanzanaAddr: cfg.ZanzanaClient.Addr, + ZanzanaCfg: cfg.ZanzanaClient, FolderReconcilerNamespace: cfg.FolderReconciler.Namespace, } diff --git a/apps/iam/go.mod b/apps/iam/go.mod index 18ba313658b..fb19d7d8758 100644 --- a/apps/iam/go.mod +++ b/apps/iam/go.mod @@ -28,7 +28,6 @@ require ( github.com/grafana/grafana-app-sdk/plugin v0.40.3 github.com/grafana/grafana/apps/folder v0.0.0 github.com/grafana/grafana/pkg/apimachinery v0.0.0 - google.golang.org/grpc v1.74.2 k8s.io/apimachinery v0.33.3 k8s.io/client-go v0.33.3 k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff @@ -36,53 +35,106 @@ require ( require ( cel.dev/expr v0.24.0 // indirect + cloud.google.com/go v0.121.1 // indirect + cloud.google.com/go/auth v0.16.1 // indirect + cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect cloud.google.com/go/compute/metadata v0.7.0 // indirect + cloud.google.com/go/iam v1.5.2 // indirect + cloud.google.com/go/monitoring v1.24.2 // indirect + cloud.google.com/go/storage v1.55.0 // indirect cuelang.org/go v0.11.1 // indirect dario.cat/mergo v1.0.1 // indirect filippo.io/edwards25519 v1.1.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.1 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1 // indirect + github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 // indirect + github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.1 // indirect + github.com/Azure/go-autorest v14.2.0+incompatible // indirect + github.com/Azure/go-autorest/autorest/to v0.4.1 // indirect + github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e // indirect + github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 // indirect github.com/BurntSushi/toml v1.5.0 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.51.0 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.51.0 // indirect github.com/HdrHistogram/hdrhistogram-go v1.1.2 // indirect github.com/Masterminds/goutils v1.1.1 // indirect + github.com/Masterminds/semver v1.5.0 // indirect github.com/Masterminds/semver/v3 v3.4.0 // indirect github.com/Masterminds/sprig/v3 v3.3.0 // indirect github.com/Masterminds/squirrel v1.5.4 // indirect + github.com/NYTimes/gziphandler v1.1.1 // indirect github.com/ProtonMail/go-crypto v1.1.6 // indirect + github.com/RoaringBitmap/roaring v1.9.3 // indirect github.com/VividCortex/mysqlerr v0.0.0-20170204212430-6c6b55f8796f // indirect github.com/Yiling-J/theine-go v0.6.1 // indirect github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b // indirect github.com/antlr4-go/antlr/v4 v4.13.1 // indirect github.com/apache/arrow-go/v18 v18.3.0 // indirect github.com/armon/go-metrics v0.4.1 // indirect + github.com/armon/go-radix v1.0.0 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/at-wat/mqtt-go v0.19.4 // indirect github.com/aws/aws-sdk-go v1.55.7 // indirect github.com/aws/aws-sdk-go-v2 v1.36.5 // indirect + github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.11 // indirect + github.com/aws/aws-sdk-go-v2/config v1.29.17 // indirect github.com/aws/aws-sdk-go-v2/credentials v1.17.70 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.32 // indirect + github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.69 // indirect github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.36 // indirect github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.36 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect + github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.34 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.4 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.0 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.17 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.15 // indirect + github.com/aws/aws-sdk-go-v2/service/s3 v1.78.2 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.25.5 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.3 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.34.0 // indirect github.com/aws/smithy-go v1.22.4 // indirect + github.com/axiomhq/hyperloglog v0.0.0-20240507144631-af9851f82b27 // indirect + github.com/bahlo/generic-list-go v0.2.0 // indirect github.com/barkimedes/go-deepcopy v0.0.0-20220514131651-17c30cfc62df // indirect github.com/benbjohnson/clock v1.3.5 // indirect github.com/beorn7/perks v1.0.1 // indirect + github.com/bits-and-blooms/bitset v1.22.0 // indirect github.com/blang/semver v3.5.1+incompatible // indirect github.com/blang/semver/v4 v4.0.0 // indirect + github.com/blevesearch/go-porterstemmer v1.0.3 // indirect + github.com/blevesearch/mmap-go v1.0.4 // indirect + github.com/blevesearch/segment v0.9.1 // indirect + github.com/blevesearch/snowballstem v0.9.0 // indirect + github.com/blevesearch/vellum v1.1.0 // indirect github.com/bluele/gcache v0.0.2 // indirect + github.com/blugelabs/bluge v0.2.2 // indirect + github.com/blugelabs/bluge_segment_api v0.2.0 // indirect + github.com/blugelabs/ice v1.0.0 // indirect + github.com/blugelabs/ice/v2 v2.0.1 // indirect github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 // indirect github.com/bufbuild/protocompile v0.4.0 // indirect + github.com/buger/jsonparser v1.1.1 // indirect github.com/bwmarrin/snowflake v0.3.0 // indirect + github.com/caio/go-tdigest v3.1.0+incompatible // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cenkalti/backoff/v5 v5.0.2 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cheekybits/genny v1.0.0 // indirect github.com/chromedp/cdproto v0.0.0-20250429231605-6ed5b53462d4 // indirect github.com/cloudflare/circl v1.6.1 // indirect + github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443 // indirect github.com/cockroachdb/apd/v3 v3.2.1 // indirect + github.com/coreos/go-semver v0.3.1 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/dennwc/varint v1.0.0 // indirect + github.com/dgraph-io/badger/v4 v4.7.0 // indirect + github.com/dgraph-io/ristretto/v2 v2.2.0 // indirect + github.com/dgryski/go-metro v0.0.0-20180109044635-280f6062b5bc // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/diegoholiveira/jsonlogic/v3 v3.7.4 // indirect github.com/dlmiddlecote/sqlstats v1.0.2 // indirect github.com/docker/go-units v0.5.0 // indirect @@ -95,6 +147,7 @@ require ( github.com/elazarl/goproxy v1.7.2 // indirect github.com/emicklei/go-restful/v3 v3.12.1 // 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 github.com/fatih/color v1.18.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect @@ -103,8 +156,11 @@ require ( github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/gchaincl/sqlhooks v1.3.0 // indirect github.com/getkin/kin-openapi v0.132.0 // indirect + github.com/go-asn1-ber/asn1-ber v1.5.4 // indirect github.com/go-jose/go-jose/v3 v3.0.4 // indirect + github.com/go-jose/go-jose/v4 v4.1.0 // indirect github.com/go-kit/log v0.2.1 // indirect + github.com/go-ldap/ldap/v3 v3.4.4 // indirect github.com/go-logfmt/logfmt v0.6.0 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect @@ -118,6 +174,7 @@ require ( github.com/go-openapi/strfmt v0.23.0 // indirect github.com/go-openapi/swag v0.23.0 // indirect github.com/go-openapi/validate v0.24.0 // indirect + github.com/go-redis/redis/v8 v8.11.5 // indirect github.com/go-sql-driver/mysql v1.9.3 // indirect github.com/go-stack/stack v1.8.1 // indirect github.com/go-viper/mapstructure/v2 v2.3.0 // indirect @@ -127,16 +184,23 @@ require ( github.com/gogo/googleapis v1.4.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/gogo/status v1.1.1 // indirect + github.com/golang-jwt/jwt/v4 v4.5.2 // indirect + github.com/golang-jwt/jwt/v5 v5.2.3 // indirect github.com/golang-migrate/migrate/v4 v4.7.0 // indirect + github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect github.com/golang/protobuf v1.5.4 // indirect + github.com/golang/snappy v1.0.0 // indirect github.com/google/btree v1.1.3 // indirect github.com/google/cel-go v0.25.0 // indirect github.com/google/flatbuffers v25.2.10+incompatible // indirect github.com/google/gnostic-models v0.6.9 // indirect github.com/google/go-cmp v0.7.0 // indirect github.com/google/go-querystring v1.1.0 // indirect + github.com/google/s2a-go v0.1.9 // indirect github.com/google/uuid v1.6.0 // indirect github.com/google/wire v0.6.0 // indirect + 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-20250821181654-10915888e4f0 // indirect github.com/grafana/authlib/types v0.0.0-20250710201142-9542f2f28d43 // indirect @@ -146,27 +210,40 @@ require ( github.com/grafana/grafana-azure-sdk-go/v2 v2.2.0 // indirect github.com/grafana/grafana-plugin-sdk-go v0.278.0 // indirect github.com/grafana/grafana/apps/dashboard v0.0.0 // indirect + github.com/grafana/grafana/apps/provisioning v0.0.0 // indirect + github.com/grafana/grafana/apps/secret v0.0.0 // indirect + github.com/grafana/grafana/pkg/aggregator v0.0.0 // indirect github.com/grafana/grafana/pkg/apiserver v0.0.0 // indirect + github.com/grafana/grafana/pkg/promlib v0.0.8 // indirect + github.com/grafana/grafana/pkg/semconv v0.0.0-20250804150913-990f1c69ecc2 // indirect github.com/grafana/otel-profiling-go v0.5.1 // indirect github.com/grafana/pyroscope-go/godeltaprof v0.1.8 // indirect + github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // indirect github.com/grafana/sqlds/v4 v4.2.4 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0 // indirect github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 // indirect + github.com/grpc-ecosystem/go-grpc-prometheus v1.2.1-0.20191002090509-6af20e3a5340 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 // indirect + github.com/hashicorp/consul/api v1.31.2 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-hclog v1.6.3 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect github.com/hashicorp/go-metrics v0.5.4 // indirect github.com/hashicorp/go-msgpack/v2 v2.1.2 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-plugin v1.6.3 // indirect + github.com/hashicorp/go-rootcerts v1.0.2 // indirect github.com/hashicorp/go-sockaddr v1.0.7 // indirect github.com/hashicorp/golang-lru v1.0.2 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/hashicorp/memberlist v0.5.2 // indirect + github.com/hashicorp/serf v0.10.2 // indirect github.com/hashicorp/yamux v0.1.1 // indirect github.com/huandu/xstrings v1.5.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/invopop/jsonschema v0.13.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect github.com/jackc/pgx/v5 v5.7.5 // indirect @@ -183,12 +260,14 @@ require ( github.com/jszwedko/go-datemath v0.1.1-0.20230526204004-640a500621d6 // indirect github.com/klauspost/compress v1.18.0 // indirect github.com/klauspost/cpuid/v2 v2.2.10 // indirect + github.com/kylelemons/godebug v1.1.0 // indirect github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect github.com/lestrrat-go/strftime v1.0.4 // indirect github.com/lib/pq v1.10.9 // indirect github.com/magefile/mage v1.15.0 // indirect github.com/mailru/easyjson v0.9.0 // indirect + github.com/mattbaird/jsonpatch v0.0.0-20240118010651-0ba75a80ca38 // indirect github.com/mattetti/filebuffer v1.0.1 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect @@ -211,6 +290,7 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect + github.com/mschoch/smat v0.2.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect github.com/natefinch/wrap v0.2.0 // indirect @@ -234,7 +314,9 @@ require ( github.com/pelletier/go-toml/v2 v2.2.3 // indirect github.com/perimeterx/marshmallow v1.1.5 // indirect github.com/pierrec/lz4/v4 v4.1.22 // indirect + github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect github.com/pkg/errors v0.9.1 // indirect + github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/pressly/goose/v3 v3.24.3 // indirect github.com/prometheus/alertmanager v0.28.0 // indirect @@ -244,6 +326,7 @@ require ( github.com/prometheus/common/sigv4 v0.1.0 // indirect github.com/prometheus/exporter-toolkit v0.14.0 // indirect github.com/prometheus/procfs v0.16.1 // indirect + github.com/prometheus/prometheus v0.303.1 // indirect github.com/puzpuzpuz/xsync/v2 v2.5.1 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/rivo/uniseg v0.4.7 // indirect @@ -259,8 +342,10 @@ require ( github.com/sourcegraph/conc v0.3.0 // indirect github.com/spf13/afero v1.12.0 // indirect github.com/spf13/cast v1.7.1 // indirect + github.com/spf13/cobra v1.9.1 // indirect github.com/spf13/pflag v1.0.7 // indirect github.com/spf13/viper v1.20.1 // indirect + github.com/spiffe/go-spiffe/v2 v2.5.0 // indirect github.com/stoewer/go-strcase v1.3.0 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/stretchr/testify v1.10.0 // indirect @@ -274,11 +359,18 @@ require ( github.com/unknwon/com v1.0.1 // indirect github.com/unknwon/log v0.0.0-20200308114134-929b1006e34a // indirect github.com/urfave/cli v1.22.16 // indirect + github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect github.com/x448/float16 v0.8.4 // indirect + github.com/zeebo/errs v1.4.0 // indirect github.com/zeebo/xxh3 v1.0.2 // indirect + go.etcd.io/etcd/api/v3 v3.5.21 // indirect + go.etcd.io/etcd/client/pkg/v3 v3.5.21 // indirect + go.etcd.io/etcd/client/v3 v3.5.21 // indirect go.mongodb.org/mongo-driver v1.17.3 // indirect + go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/contrib/bridges/prometheus v0.61.0 // indirect + go.opentelemetry.io/contrib/detectors/gcp v1.36.0 // indirect go.opentelemetry.io/contrib/exporters/autoexport v0.61.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.61.0 // indirect @@ -310,6 +402,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect go.yaml.in/yaml/v2 v2.4.2 // indirect + gocloud.dev v0.42.0 // indirect golang.org/x/crypto v0.41.0 // indirect golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 // indirect golang.org/x/mod v0.27.0 // indirect @@ -324,14 +417,19 @@ require ( golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect gonum.org/v1/gonum v0.16.0 // indirect + google.golang.org/api v0.235.0 // indirect + google.golang.org/genproto v0.0.0-20250603155806-513f23925822 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect + google.golang.org/grpc v1.74.2 // indirect google.golang.org/protobuf v1.36.6 // indirect gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect + gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/fsnotify/fsnotify.v1 v1.4.7 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/mail.v2 v2.3.1 // indirect + gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect gopkg.in/src-d/go-errors.v1 v1.0.0 // indirect gopkg.in/telebot.v3 v3.2.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect @@ -341,11 +439,13 @@ require ( k8s.io/apiserver v0.33.3 // indirect k8s.io/component-base v0.33.3 // indirect k8s.io/klog/v2 v2.130.1 // indirect + k8s.io/kms v0.33.3 // indirect k8s.io/utils v0.0.0-20241210054802-24370beab758 // indirect modernc.org/libc v1.65.0 // indirect modernc.org/mathutil v1.7.1 // indirect modernc.org/memory v1.10.0 // indirect modernc.org/sqlite v1.37.0 // indirect + sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 // 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/v4 v4.7.0 // indirect diff --git a/apps/iam/go.sum b/apps/iam/go.sum index 0b292c52c5f..976815cf892 100644 --- a/apps/iam/go.sum +++ b/apps/iam/go.sum @@ -56,6 +56,10 @@ cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1 cloud.google.com/go/firestore v1.6.1/go.mod h1:asNXNOzBdyVQmEU+ggO8UPodTkEVFW5Qx+rwHnAz+EY= cloud.google.com/go/iam v1.5.2 h1:qgFRAGEmd8z6dJ/qyEchAuL9jpswyODjA2lS+w234g8= cloud.google.com/go/iam v1.5.2/go.mod h1:SE1vg0N81zQqLzQEwxL2WI6yhetBdbNQuTvIKCSkUHE= +cloud.google.com/go/logging v1.13.0 h1:7j0HgAp0B94o1YRDqiqm26w4q1rDMH7XNRU34lJXHYc= +cloud.google.com/go/logging v1.13.0/go.mod h1:36CoKh6KA/M0PbhPKMq6/qety2DCAErbhXT62TuXALA= +cloud.google.com/go/longrunning v0.6.7 h1:IGtfDWHhQCgCjwQjV9iiLnUta9LBCo8R9QmAFsS/PrE= +cloud.google.com/go/longrunning v0.6.7/go.mod h1:EAFV3IZAKmM56TyiE6VAP3VoTzhZzySwI/YI1s/nRsY= cloud.google.com/go/monitoring v1.24.2 h1:5OTsoJ1dXYIiMiuL+sYscLc9BumrL3CarVLL7dd7lHM= cloud.google.com/go/monitoring v1.24.2/go.mod h1:x7yzPWcgDRnPEv3sI+jJGBkwl5qINf+6qY4eq0I9B4U= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= @@ -70,6 +74,8 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9 cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= cloud.google.com/go/storage v1.55.0 h1:NESjdAToN9u1tmhVqhXCaCwYBuvEhZLLv0gBr+2znf0= cloud.google.com/go/storage v1.55.0/go.mod h1:ztSmTTwzsdXe5syLVS0YsbFxXuvEmEyZj7v7zChEmuY= +cloud.google.com/go/trace v1.11.6 h1:2O2zjPzqPYAHrn3OKl029qlqG6W8ZdYaOWRyr8NgMT4= +cloud.google.com/go/trace v1.11.6/go.mod h1:GA855OeDEBiBMzcckLPE2kDunIpC72N+Pq8WFieFjnI= cuelabs.dev/go/oci/ociregistry v0.0.0-20240906074133-82eb438dd565 h1:R5wwEcbEZSBmeyg91MJZTxfd7WpBo2jPof3AYjRbxwY= cuelabs.dev/go/oci/ociregistry v0.0.0-20240906074133-82eb438dd565/go.mod h1:5A4xfTzHTXfeVJBU6RAUf+QrlfTCW+017q/QiW+sMLg= cuelang.org/go v0.11.1 h1:pV+49MX1mmvDm8Qh3Za3M786cty8VKPWzQ1Ho4gZRP0= @@ -84,18 +90,23 @@ github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.1 h1:Wc1ml6QlJs2BHQ/9Bqu1jiyg github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.1/go.mod h1:Ot/6aikWnKWi4l9QB7qVSwa8iMphQNqkWALMoNT3rzM= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1 h1:B+blDbyVIG3WaikNxPnhPiJ1MThR03b3vKGtER95TP4= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1/go.mod h1:JdM5psgjfBf5fo2uWOZhflPWyDBZ/O/CNAH9CtsuZE4= +github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY= +github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8= github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 h1:FPKJS1T+clwv+OLGt13a8UjqeRuh0O4SJ3lUriThc+4= github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1/go.mod h1:j2chePtV91HrC22tGoRX3sGY42uF13WzmmV80/OdVAA= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.8.0 h1:LR0kAX9ykz8G4YgLCaRDVJ3+n43R8MneB5dTy2konZo= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.8.0/go.mod h1:DWAciXemNf++PQJLeXUB4HHH5OpsAh12HZnu2wXE1jA= github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.1 h1:lhZdRq7TIx0GJQvSyX2Si406vrYsov2FXGp/RnSEtcs= github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.1/go.mod h1:8cl44BDmi+effbARHMQjgOKA2AYvcohNm7KEt42mSV8= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest/autorest v0.11.29 h1:I4+HL/JDvErx2LjyzaVxllw2lRDB5/BT2Bm4g20iqYw= github.com/Azure/go-autorest/autorest/to v0.4.1 h1:CxNHBqdzTr7rLtdrtb5CMjJcDut+WNGCVv7OmS5+lTc= github.com/Azure/go-autorest/autorest/to v0.4.1/go.mod h1:EtaofgU4zmtvn1zT2ARsjRFdq9vXx0YWtmElwL+GZ9M= github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e h1:NeAW1fUYUEWhft7pkxDf6WoUvEZJ/uOKsvtpjLnn8MU= github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= +github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM= +github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE= github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 h1:oygO0locgZJe7PpYPXT5A29ZkwJaPqcva7BVeemZOZs= github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= @@ -108,6 +119,8 @@ github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0 github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0/go.mod h1:yAZHSGnqScoU556rBOVkwLze6WP5N+U11RHuWaGVxwY= github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.51.0 h1:fYE9p3esPxA/C0rQ0AHhP0drtPXDRhaWiwg1DPqO7IU= github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.51.0/go.mod h1:BnBReJLvVYx2CS/UHOgVz2BXKXD9wsQPxZug20nZhd0= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.51.0 h1:OqVGm6Ei3x5+yZmSJG1Mh2NwHvpVmZ08CB5qJhT9Nuk= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.51.0/go.mod h1:SZiPHWGOOk3bl8tkevxkoiwPgsIl6CwrWcbwjfHZpdM= github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.51.0 h1:6/0iUd0xrnX7qt+mLNRwg5c0PGv8wpE8K90ryANQwMI= github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.51.0/go.mod h1:otE2jQekW/PqXk1Awf5lmfokJx4uwuqcj1ab5SpGeW0= github.com/HdrHistogram/hdrhistogram-go v1.1.2 h1:5IcZpTvzydCQeHzK4Ef/D5rrSqwxob0t8PQPMybUNFM= @@ -131,6 +144,10 @@ github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8 github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNxpLfdw= github.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= +github.com/RoaringBitmap/gocroaring v0.4.0/go.mod h1:NieMwz7ZqwU2DD73/vvYwv7r4eWBKuPVSXZIpsaMwCI= +github.com/RoaringBitmap/real-roaring-datasets v0.0.0-20190726190000-eb7c87156f76/go.mod h1:oM0MHmQ3nDsq609SS36p+oYbRi16+oVvU2Bw4Ipv0SE= +github.com/RoaringBitmap/roaring v0.9.1/go.mod h1:h1B7iIUOmnAeb5ytYMvnHJwxMc6LUrwBnzXWRuqTQUc= +github.com/RoaringBitmap/roaring v0.9.4/go.mod h1:icnadbWcNyfEHlYdr+tDlOTih1Bf/h+rzPpv4sbomAA= github.com/RoaringBitmap/roaring v1.9.3 h1:t4EbC5qQwnisr5PrP9nt0IRhRTb9gMUgQF4t4S2OByM= github.com/RoaringBitmap/roaring v1.9.3/go.mod h1:6AXUsoIEzDTFFQCe1RbGA6uFONMhvejWj5rqITANK90= github.com/RoaringBitmap/roaring/v2 v2.4.5 h1:uGrrMreGjvAtTBobc0g5IrW1D5ldxDQYe2JW2gggRdg= @@ -160,6 +177,7 @@ github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb github.com/apache/thrift v0.21.0 h1:tdPmh/ptjE1IJnhbhrcl2++TauVjy242rkV/UzJChnE= github.com/apache/thrift v0.21.0/go.mod h1:W1H8aR/QRtYNvrPeFXBtobyRkd0/YVhTc6i07XIAgDw= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA= @@ -213,12 +231,15 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.34.0 h1:NFOJ/NXEGV4Rq//71Hs1jC/NvPs1 github.com/aws/aws-sdk-go-v2/service/sts v1.34.0/go.mod h1:7ph2tGpfQvwzgistp2+zga9f+bCjlQJPkPUmMgDSD7w= github.com/aws/smithy-go v1.22.4 h1:uqXzVZNuNexwc/xrh6Tb56u89WDlJY6HS+KC0S4QSjw= github.com/aws/smithy-go v1.22.4/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI= +github.com/axiomhq/hyperloglog v0.0.0-20191112132149-a4c4c47bc57f/go.mod h1:2stgcRjl6QmW+gU2h5E7BQXg4HU0gzxKWDuT5HviN9s= github.com/axiomhq/hyperloglog v0.0.0-20240507144631-af9851f82b27 h1:60m4tnanN1ctzIu4V3bfCNJ39BiOPSm1gHFlFjTkRE0= github.com/axiomhq/hyperloglog v0.0.0-20240507144631-af9851f82b27/go.mod h1:k08r+Yj1PRAmuayFiRK6MYuR5Ve4IuZtTfxErMIh0+c= github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk= github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= github.com/barkimedes/go-deepcopy v0.0.0-20220514131651-17c30cfc62df h1:GSoSVRLoBaFpOOds6QyY1L8AX7uoY+Ln3BHc22W40X0= github.com/barkimedes/go-deepcopy v0.0.0-20220514131651-17c30cfc62df/go.mod h1:hiVxq5OP2bUGBRNS3Z/bt/reCLFNbdcST6gISi1fiOM= +github.com/bboreham/go-loser v0.0.0-20230920113527-fcc2c21820a3 h1:6df1vn4bBlDDo4tARvBm7l6KA9iVMnE3NWizDeWSrps= +github.com/bboreham/go-loser v0.0.0-20230920113527-fcc2c21820a3/go.mod h1:CIWtjkly68+yqLPbvwwR/fjNJA/idrtULjZWh2v1ys0= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= @@ -228,6 +249,8 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k= +github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= +github.com/bits-and-blooms/bitset v1.12.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/bits-and-blooms/bitset v1.22.0 h1:Tquv9S8+SGaS3EhyA+up3FXzmkhxPGjQQCkcs2uw7w4= github.com/bits-and-blooms/bitset v1.22.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= @@ -246,16 +269,21 @@ github.com/blevesearch/go-porterstemmer v1.0.3 h1:GtmsqID0aZdCSNiY8SkuPJ12pD4jI+ github.com/blevesearch/go-porterstemmer v1.0.3/go.mod h1:angGc5Ht+k2xhJdZi511LtmxuEf0OVpvUUNrwmM1P7M= github.com/blevesearch/gtreap v0.1.1 h1:2JWigFrzDMR+42WGIN/V2p0cUvn4UP3C4Q5nmaZGW8Y= github.com/blevesearch/gtreap v0.1.1/go.mod h1:QaQyDRAT51sotthUWAH4Sj08awFSSWzgYICSZ3w0tYk= +github.com/blevesearch/mmap-go v1.0.2/go.mod h1:ol2qBqYaOUsGdm7aRMRrYGgPvnwLe6Y+7LMvAB5IbSA= +github.com/blevesearch/mmap-go v1.0.3/go.mod h1:pYvKl/grLQrBxuaRYgoTssa4rVujYYeenDp++2E+yvs= github.com/blevesearch/mmap-go v1.0.4 h1:OVhDhT5B/M1HNPpYPBKIEJaD0F3Si+CrEKULGCDPWmc= github.com/blevesearch/mmap-go v1.0.4/go.mod h1:EWmEAOmdAS9z/pi/+Toxu99DnsbhG1TIxUoRmJw/pSs= github.com/blevesearch/scorch_segment_api/v2 v2.3.9 h1:X6nJXnNHl7nasXW+U6y2Ns2Aw8F9STszkYkyBfQ+p0o= github.com/blevesearch/scorch_segment_api/v2 v2.3.9/go.mod h1:IrzspZlVjhf4X29oJiEhBxEteTqOY9RlYlk1lCmYHr4= +github.com/blevesearch/segment v0.9.0/go.mod h1:9PfHYUdQCgHktBgvtUOF4x+pc4/l8rdH0u5spnW85UQ= github.com/blevesearch/segment v0.9.1 h1:+dThDy+Lvgj5JMxhmOVlgFfkUtZV2kw49xax4+jTfSU= github.com/blevesearch/segment v0.9.1/go.mod h1:zN21iLm7+GnBHWTao9I+Au/7MBiL8pPFtJBJTsk6kQw= github.com/blevesearch/snowballstem v0.9.0 h1:lMQ189YspGP6sXvZQ4WZ+MLawfV8wOmPoD/iWeNXm8s= github.com/blevesearch/snowballstem v0.9.0/go.mod h1:PivSj3JMc8WuaFkTSRDW2SlrulNWPl4ABg1tC/hlgLs= github.com/blevesearch/upsidedown_store_api v1.0.2 h1:U53Q6YoWEARVLd1OYNc9kvhBMGZzVrdmaozG2MfoB+A= github.com/blevesearch/upsidedown_store_api v1.0.2/go.mod h1:M01mh3Gpfy56Ps/UXHjEO/knbqyQ1Oamg8If49gRwrQ= +github.com/blevesearch/vellum v1.0.5/go.mod h1:atE0EH3fvk43zzS7t1YNdNC7DbmcC3uz+eMD5xZ2OyQ= +github.com/blevesearch/vellum v1.0.7/go.mod h1:doBZpmRhwTsASB4QdUZANlJvqVAUdUyX0ZK7QJCTeBE= github.com/blevesearch/vellum v1.1.0 h1:CinkGyIsgVlYf8Y2LUQHvdelgXr6PYuvoDIajq6yR9w= github.com/blevesearch/vellum v1.1.0/go.mod h1:QgwWryE8ThtNPxtgWJof5ndPfx0/YMBh+W2weHKPw8Y= github.com/blevesearch/zapx/v11 v11.4.1 h1:qFCPlFbsEdwbbckJkysptSQOsHn4s6ZOHL5GMAIAVHA= @@ -328,14 +356,19 @@ github.com/cockroachdb/apd/v3 v3.2.1 h1:U+8j7t0axsIgvQUqthuNm82HIrYXodOV2iWLWtEa github.com/cockroachdb/apd/v3 v3.2.1/go.mod h1:klXJcjp+FffLTHlhIG69tezTDvdP065naDsHzKhYSqc= github.com/cockroachdb/cockroach-go v0.0.0-20181001143604-e0a95dfd547c/go.mod h1:XGLbWH/ujMcbPbhZq52Nv6UrCghb1yGn//133kEsvDk= github.com/containerd/containerd v1.2.7/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4= github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo= github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= @@ -360,6 +393,8 @@ github.com/dgraph-io/badger/v4 v4.7.0 h1:Q+J8HApYAY7UMpL8d9owqiB+odzEc0zn/aqOD9j github.com/dgraph-io/badger/v4 v4.7.0/go.mod h1:He7TzG3YBy3j4f5baj5B7Zl2XyfNe5bl4Udl0aPemVA= github.com/dgraph-io/ristretto/v2 v2.2.0 h1:bkY3XzJcXoMuELV8F+vS8kzNgicwQFAaGINAEJdWGOM= github.com/dgraph-io/ristretto/v2 v2.2.0/go.mod h1:RZrm63UmcBAaYWC1DotLYBmTvgkrs0+XhBd7Npn7/zI= +github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da h1:aIftn67I1fkbMa512G+w+Pxci9hJPB8oMnkcP3iZF38= +github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-metro v0.0.0-20180109044635-280f6062b5bc h1:8WFBn63wegobsYAX0YjD+8suexZDga5CctH4CCTx2+8= github.com/dgryski/go-metro v0.0.0-20180109044635-280f6062b5bc/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= @@ -417,13 +452,16 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.m github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/go-control-plane v0.13.4 h1:zEqyPVyku6IvWCFwux4x9RxkLOMUL+1vC9xUFv5l2/M= +github.com/envoyproxy/go-control-plane v0.13.4/go.mod h1:kDfuBlDVsSj2MjrLEtRWtHlsWIFcGyB2RMO44Dc5GZA= github.com/envoyproxy/go-control-plane/envoy v1.32.4 h1:jb83lalDRZSpPWW2Z7Mck/8kXZ5CQAFYVjQcdVIr83A= github.com/envoyproxy/go-control-plane/envoy v1.32.4/go.mod h1:Gzjc5k8JcJswLjAx1Zm+wSYE20UrLtt7JZMWiWQXQEw= +github.com/envoyproxy/go-control-plane/ratelimit v0.1.0 h1:/G9QYbddjL25KvtKTv3an9lx6VBE2cnb8wp1vEGNYGI= +github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v1.2.1 h1:DEo3O99U8j4hBFwbJfrz9VtgcDfUKS7KJ7spH3d86P8= github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU= -github.com/evanphx/json-patch v0.5.2 h1:xVCHIVMUu1wtM/VkR9jVZ45N3FhZfYMMYGorLCR8P3k= -github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= +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/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= @@ -477,6 +515,8 @@ 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-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= +github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= github.com/go-openapi/analysis v0.23.0 h1:aGday7OWupfMs+LbmLZG4k0MYXIANxcuBTYUC03zFCU= github.com/go-openapi/analysis v0.23.0/go.mod h1:9mz9ZWaSlV8TvjQHLl2mUW2PbZtemkE8yA5v22ohupo= github.com/go-openapi/errors v0.22.0 h1:c4xY/OLxUBSTiepAg3j/MHuAv5mJhnf53LLMWFB+u/w= @@ -623,11 +663,18 @@ github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+u github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= +github.com/google/go-replayers/grpcreplay v1.3.0 h1:1Keyy0m1sIpqstQmgz307zhiJ1pV4uIlFds5weTmxbo= +github.com/google/go-replayers/grpcreplay v1.3.0/go.mod h1:v6NgKtkijC0d3e3RW8il6Sy5sqRVUwoQa4mHOGEy8DI= +github.com/google/go-replayers/httpreplay v1.2.0 h1:VM1wEyyjaoU53BwrOnaf9VhAyQQEEioJvFYxYcLRKzk= +github.com/google/go-replayers/httpreplay v1.2.0/go.mod h1:WahEFFZZ7a1P4VM1qEeHy+tME4bwyqPcwWbNlUI1Mcg= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= +github.com/google/martian/v3 v3.3.3 h1:DIhPTQrbPkgs2yJYdXU/eNACCG5DVQjySNRNlflZ9Fc= +github.com/google/martian/v3 v3.3.3/go.mod h1:iEPrYcgCF7jA9OtScMFQyAlZZ4YXTKEtJ1E6RWzmBA0= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= @@ -673,6 +720,8 @@ github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2z github.com/gorilla/mux v1.7.1/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= 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-20250821181654-10915888e4f0 h1:CC+ShIgoLTBsAulRfJH9xF++Us6Y3va3VFbeDeJym2M= github.com/grafana/alerting v0.0.0-20250821181654-10915888e4f0/go.mod h1:VKxaR93Gff0ZlO2sPcdPVob1a/UzArFEW5zx3Bpyhls= github.com/grafana/authlib v0.0.0-20250710201142-9542f2f28d43 h1:vVPT0i5Y1vI6qzecYStV2yk7cHKrC3Pc7AgvwT5KydQ= @@ -695,6 +744,8 @@ github.com/grafana/grafana-aws-sdk v1.1.0 h1:G0fvwbQmHw14c5RXPd7Gnw9ZQcgzl139LtM github.com/grafana/grafana-aws-sdk v1.1.0/go.mod h1:7e+47EdHynteYWGoT5Ere9KeOXQObsk8F0vkOLQ1tz8= github.com/grafana/grafana-azure-sdk-go/v2 v2.2.0 h1:0TYrkzAc3u0HX+9GK86cGrLTUAcmQfl3/LEB3tL+SOA= github.com/grafana/grafana-azure-sdk-go/v2 v2.2.0/go.mod h1:H9sVh9A4yg5egMGZeh0mifxT1Q/uqwKe1LBjBJU6pN8= +github.com/grafana/grafana-openapi-client-go v0.0.0-20231213163343-bd475d63fb79 h1:r+mU5bGMzcXCRVAuOrTn54S80qbfVkvTdUJZfSfTNbs= +github.com/grafana/grafana-openapi-client-go v0.0.0-20231213163343-bd475d63fb79/go.mod h1:wc6Hbh3K2TgCUSfBC/BOzabItujtHMESZeFk5ZhdxhQ= github.com/grafana/grafana-plugin-sdk-go v0.278.0 h1:5/rIYparLi02pofdaag8wnjspMMVNCi8cZhC4cdC3Ho= github.com/grafana/grafana-plugin-sdk-go v0.278.0/go.mod h1:+8NXT/XUJ/89GV6FxGQ366NZ3nU+cAXDMd0OUESF9H4= github.com/grafana/grafana/pkg/promlib v0.0.8 h1:VUWsqttdf0wMI4j9OX9oNrykguQpZcruudDAFpJJVw0= @@ -720,6 +771,7 @@ github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2/go.mod h1:wd1YpapPLivG6nQ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.1-0.20191002090509-6af20e3a5340 h1:uGoIog/wiQHI9GAxXO5TJbT0wWKH3O9HhOJW1F9c3fY= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.1-0.20191002090509-6af20e3a5340/go.mod h1:3bDW6wMZJB7tiONtC/1Xpicra6Wp5GgbTbQWCbI5fkc= +github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 h1:X5VWvz21y3gzm9Nw/kaUeku/1+uBhcekkmy4IkffJww= github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1/go.mod h1:Zanoh4+gvIgluNqcfMVTJueD4wSS5hT7zTt4Mrutd90= @@ -728,6 +780,8 @@ github.com/hashicorp/consul/api v1.12.0/go.mod h1:6pVBMo0ebnYdt2S3H87XhekM/HHrUo github.com/hashicorp/consul/api v1.31.2 h1:NicObVJHcCmyOIl7Z9iHPvvFrocgTYo9cITSGg0/7pw= github.com/hashicorp/consul/api v1.31.2/go.mod h1:Z8YgY0eVPukT/17ejW+l+C7zJmKwgPHtjU1q16v/Y40= github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= +github.com/hashicorp/consul/sdk v0.16.1 h1:V8TxTnImoPD5cj0U9Spl0TUxcytjcbbJeADFF07KdHg= +github.com/hashicorp/consul/sdk v0.16.1/go.mod h1:fSXvwxB2hmh1FMZCNl6PwX0Q/1wdWtHJcZ7Ea5tns0s= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -766,6 +820,8 @@ github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= +github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= @@ -790,8 +846,10 @@ github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/influxdata/influxdb v1.7.6/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY= github.com/invopop/jsonschema v0.13.0 h1:KvpoAJWEjR3uD9Kbm2HWJmqsEaHt8lBUpd0qHcIi21E= github.com/invopop/jsonschema v0.13.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0= github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ= @@ -823,6 +881,8 @@ github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGw github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= +github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I= +github.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7XN3SzBPjZF60= 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/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= @@ -845,11 +905,14 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= +github.com/keybase/go-keychain v0.0.1 h1:way+bWYa6lDppZoZcgMbYsvC7GxljxrskdNInRtuthU= +github.com/keybase/go-keychain v0.0.1/go.mod h1:PdEILRW3i9D8JcdM+FmY6RwkHGnhHxXwkPPMeUgOK1k= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/asmfmt v1.3.2 h1:4Ri7ox3EwapiOjCki+hw14RyKk201CN4rzyCJRFLpK4= github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE= +github.com/klauspost/compress v1.15.2/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE= @@ -875,6 +938,8 @@ github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o= github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk= github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw= +github.com/leesper/go_rng v0.0.0-20190531154944-a612b043e353 h1:X/79QL0b4YJVO5+OsPH9rF2u428CIrGL/jLmPsoOQQ4= +github.com/leesper/go_rng v0.0.0-20190531154944-a612b043e353/go.mod h1:N0SVk0uhy+E1PZ3C9ctsPRlvOPAFPkCNlcPBDkt0N3U= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc h1:RKf14vYWi2ttpEmkA4aQ3j4u9dStX2t4M8UM6qqNsG8= github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc/go.mod h1:kopuH9ugFRkIXf3YoqHKyrJ9YfUFsckUU9S7B+XP+is= @@ -884,8 +949,11 @@ github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/madflojo/testcerts v1.1.1 h1:YsSHWV79nMNZK0mJtwXjKoYHjJEbLPFefR8TxmmWupY= +github.com/madflojo/testcerts v1.1.1/go.mod h1:MW8sh39gLnkKh4K0Nc55AyHEDl9l/FBLDUsQhpmkuo0= github.com/magefile/mage v1.15.0 h1:BvGheCMAsG3bWUDbZ8AyXXpCNwU9u5CB6sM+HNb9HYg= github.com/magefile/mage v1.15.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= @@ -961,6 +1029,8 @@ github.com/mithrandie/ternary v1.1.1 h1:k/joD6UGVYxHixYmSR8EGgDFNONBMqyD373xT4QR github.com/mithrandie/ternary v1.1.1/go.mod h1:0D9Ba3+09K2TdSZO7/bFCC0GjSXetCvYuYq0u8FY/1g= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= +github.com/mocktools/go-smtp-mock/v2 v2.3.1 h1:wq75NDSsOy5oHo/gEQQT0fRRaYKRqr1IdkjhIPXxagM= +github.com/mocktools/go-smtp-mock/v2 v2.3.1/go.mod h1:h9AOf/IXLSU2m/1u4zsjtOM/WddPwdOUBz56dV9f81M= 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= @@ -986,6 +1056,8 @@ github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJm github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nikunjy/rules v1.5.0 h1:KJDSLOsFhwt7kcXUyZqwkgrQg5YoUwj+TVu6ItCQShw= github.com/nikunjy/rules v1.5.0/go.mod h1:TlZtZdBChrkqi8Lr2AXocme8Z7EsbxtFdDoKeI6neBQ= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 h1:G7ERwszslrBzRxj//JalHPu/3yz+De2J+4aLtSRlHiY= github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037/go.mod h1:2bpvgLBZEtENV5scfDFEtB/5+1M4hkQhDQrccEJ/qGw= github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 h1:bQx3WeLcUWy+RletIKwUIt4x3t8n2SxavmoclizMb8c= @@ -999,8 +1071,9 @@ github.com/oklog/ulid/v2 v2.1.0/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNs github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU= github.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= @@ -1036,6 +1109,7 @@ github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144T github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas= github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= @@ -1062,6 +1136,7 @@ github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSg github.com/pressly/goose/v3 v3.24.3 h1:DSWWNwwggVUsYZ0X2VitiAa9sKuqtBfe+Jr9zFGwWlM= github.com/pressly/goose/v3 v3.24.3/go.mod h1:v9zYL4xdViLHCUUJh/mhjnm6JrK7Eul8AS93IxiZM4E= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= @@ -1079,6 +1154,7 @@ github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6T github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= +github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= @@ -1093,6 +1169,7 @@ github.com/prometheus/common/sigv4 v0.1.0/go.mod h1:2Jkxxk9yYvCkE5G1sQT7GuEXm57J github.com/prometheus/exporter-toolkit v0.14.0 h1:NMlswfibpcZZ+H0sZBiTjrA3/aBFHkNZqE+iCj5EmRg= github.com/prometheus/exporter-toolkit v0.14.0/go.mod h1:Gu5LnVvt7Nr/oqTBUC23WILZepW0nffNo10XdhQcwWA= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= @@ -1102,11 +1179,15 @@ github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzM github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= github.com/prometheus/prometheus v0.303.1 h1:He/2jRE6sB23Ew38AIoR1WRR3fCMgPlJA2E0obD2WSY= github.com/prometheus/prometheus v0.303.1/go.mod h1:WEq2ogBPZoLjj9x5K67VEk7ECR0nRD9XCjaOt1lsYck= +github.com/prometheus/sigv4 v0.1.2 h1:R7570f8AoM5YnTUPFm3mjZH5q2k4D+I/phCWvZ4PXG8= +github.com/prometheus/sigv4 v0.1.2/go.mod h1:GF9fwrvLgkQwDdQ5BXeV9XUSCH/IPNqzvAoaohfjqMU= github.com/protocolbuffers/txtpbfmt v0.0.0-20241112170944-20d2c9ebc01d h1:HWfigq7lB31IeJL8iy7jkUmU/PG1Sr8jVGhS749dbUA= github.com/protocolbuffers/txtpbfmt v0.0.0-20241112170944-20d2c9ebc01d/go.mod h1:jgxiZysxFPM+iWKwQwPR+y+Jvo54ARd4EisXxKYpB5c= github.com/puzpuzpuz/xsync/v2 v2.5.1 h1:mVGYAvzDSu52+zaGyNjC+24Xw2bQi3kTr4QJ6N9pIIU= github.com/puzpuzpuz/xsync/v2 v2.5.1/go.mod h1:gD2H2krq/w52MfPLE+Uy64TzJDVY7lP2znR9qmR35kU= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/redis/go-redis/v9 v9.8.0 h1:q3nRvjrlge/6UD7eTu/DSg2uYiU2mCL0G/uzBWqhicI= +github.com/redis/go-redis/v9 v9.8.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= @@ -1119,6 +1200,7 @@ github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0t github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA= github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -1150,21 +1232,30 @@ github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod h1 github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js= +github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo= github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs= github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M= github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.13.0/go.mod h1:Icm2xNL3/8uyh/wFuB1jI7TiTNKp8632Nwegu+zgdYw= github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4= github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= @@ -1207,11 +1298,14 @@ github.com/thomaspoignant/go-feature-flag v1.42.0/go.mod h1:y0QiWH7chHWhGATb/+Xq github.com/tidwall/pretty v0.0.0-20180105212114-65a9db5fad51/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tjhop/slog-gokit v0.1.3 h1:6SdexP3UIeg93KLFeiM1Wp1caRwdTLgsD/THxBUy1+o= github.com/tjhop/slog-gokit v0.1.3/go.mod h1:Bbu5v2748qpAWH7k6gse/kw3076IJf6owJmh7yArmJs= +github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75 h1:6fotK7otjonDflCTK0BCfls4SPy3NcCVb5dqqmbRknE= +github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75/go.mod h1:KO6IkyS8Y3j8OdNO85qEYBsRPuteD+YciPomcXdrMnk= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/uber/jaeger-client-go v2.30.0+incompatible h1:D6wyKGCecFaSRUpo8lCVbaOOb6ThwMmTEbhRwtKR97o= github.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVKhn2Um6rjCsSsg= github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/unknwon/bra v0.0.0-20200517080246-1e3013ecaff8 h1:aVGB3YnaS/JNfOW3tiHIlmNmTDg618va+eT0mVomgyI= @@ -1224,7 +1318,6 @@ github.com/unknwon/log v0.0.0-20200308114134-929b1006e34a/go.mod h1:1xEUf2abjfP9 github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.16 h1:MH0k6uJxdwdeWQTwhSO42Pwr4YLrNLwBtg1MRgTqPdQ= github.com/urfave/cli v1.22.16/go.mod h1:EeJR6BKodywf4zciqrdw6hpCPk68JO9z5LazXZMn5Po= -github.com/wk8/go-ordered-map v1.0.0 h1:BV7z+2PaK8LTSd/mWgY12HyMAo5CEgkHqbkVq2thqr8= github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc= github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= @@ -1232,6 +1325,9 @@ github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcY github.com/xanzy/go-gitlab v0.15.0/go.mod h1:8zdQa/ri1dfn8eS3Ir1SyfvOKlw7WBJ8DVThkpGiXrs= github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= +github.com/xiang90/probing v0.0.0-20221125231312-a49e3df8f510 h1:S2dVYn90KE98chqDkyE9Z4N61UnQd+KOfgp5Iu53llk= +github.com/xiang90/probing v0.0.0-20221125231312-a49e3df8f510/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -1254,9 +1350,17 @@ go.etcd.io/etcd/client/pkg/v3 v3.5.4/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3 go.etcd.io/etcd/client/pkg/v3 v3.5.21 h1:lPBu71Y7osQmzlflM9OfeIV2JlmpBjqBNlLtcoBqUTc= go.etcd.io/etcd/client/pkg/v3 v3.5.21/go.mod h1:BgqT/IXPjK9NkeSDjbzwsHySX3yIle2+ndz28nVsjUs= go.etcd.io/etcd/client/v2 v2.305.4/go.mod h1:Ud+VUwIi9/uQHOMA+4ekToJ12lTxlv0zB/+DHwTGEbU= +go.etcd.io/etcd/client/v2 v2.305.21 h1:eLiFfexc2mE+pTLz9WwnoEsX5JTTpLCYVivKkmVXIRA= +go.etcd.io/etcd/client/v2 v2.305.21/go.mod h1:OKkn4hlYNf43hpjEM3Ke3aRdUkhSl8xjKjSf8eCq2J8= go.etcd.io/etcd/client/v3 v3.5.4/go.mod h1:ZaRkVgBZC+L+dLCjTcF1hRXpgZXQPOvnA/Ak/gq3kiY= go.etcd.io/etcd/client/v3 v3.5.21 h1:T6b1Ow6fNjOLOtM0xSoKNQt1ASPCLWrF9XMHcH9pEyY= go.etcd.io/etcd/client/v3 v3.5.21/go.mod h1:mFYy67IOqmbRf/kRUvsHixzo3iG+1OF2W2+jVIQRAnU= +go.etcd.io/etcd/pkg/v3 v3.5.21 h1:jUItxeKyrDuVuWhdh0HtjUANwyuzcb7/FAeUfABmQsk= +go.etcd.io/etcd/pkg/v3 v3.5.21/go.mod h1:wpZx8Egv1g4y+N7JAsqi2zoUiBIUWznLjqJbylDjWgU= +go.etcd.io/etcd/raft/v3 v3.5.21 h1:dOmE0mT55dIUsX77TKBLq+RgyumsQuYeiRQnW/ylugk= +go.etcd.io/etcd/raft/v3 v3.5.21/go.mod h1:fmcuY5R2SNkklU4+fKVBQi2biVp5vafMrWUEj4TJ4Cs= +go.etcd.io/etcd/server/v3 v3.5.21 h1:9w0/k12majtgarGmlMVuhwXRI2ob3/d1Ik3X5TKo0yU= +go.etcd.io/etcd/server/v3 v3.5.21/go.mod h1:G1mOzdwuzKT1VRL7SqRchli/qcFrtLBTAQ4lV20sXXo= go.mongodb.org/mongo-driver v1.1.0/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= go.mongodb.org/mongo-driver v1.17.3 h1:TQyXhnsWfWtgAhMtOgtYHMTkZIfBTpMTsMnd9ZBeHxQ= go.mongodb.org/mongo-driver v1.17.3/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ= @@ -1357,6 +1461,7 @@ go.yaml.in/yaml/v3 v3.0.3/go.mod h1:tBHosrYAkRZjRAOREWbDnBXUf08JOwYq++0QNwQiWzI= gocloud.dev v0.42.0 h1:qzG+9ItUL3RPB62/Amugws28n+4vGZXEoJEAMfjutzw= gocloud.dev v0.42.0/go.mod h1:zkaYAapZfQisXOA4bzhsbA4ckiStGQ3Psvs9/OQ5dPM= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -1369,6 +1474,7 @@ golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= @@ -1426,6 +1532,7 @@ golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181108082009-03003ca0c849/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1529,6 +1636,8 @@ golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181221143128-b4a75ba826a6/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190102155601-82a175fd1598/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1541,6 +1650,7 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1611,6 +1721,7 @@ golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1727,6 +1838,7 @@ golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6f gomodules.xyz/jsonpatch/v2 v2.5.0 h1:JELs8RLM12qJGXU4u/TO3V25KW8GreMKl9pdkk14RM0= gomodules.xyz/jsonpatch/v2 v2.5.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= +gonum.org/v1/gonum v0.7.0/go.mod h1:L02bwd0sqlsvRv41G7wGWFCsVNZFv/k1xzGIxeANHGM= gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= @@ -1872,6 +1984,7 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 h1: google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= +google.golang.org/grpc v1.18.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -1948,6 +2061,7 @@ gopkg.in/src-d/go-errors.v1 v1.0.0 h1:cooGdZnCjYbeS1zb1s6pVAAimTdKceRrpn7aKOnNIf gopkg.in/src-d/go-errors.v1 v1.0.0/go.mod h1:q1cBlomlw2FnDBDNGlnh6X0jPihy+QxZfMMNxPCbdYg= gopkg.in/telebot.v3 v3.2.1 h1:3I4LohaAyJBiivGmkfB+CiVu7QFOWkuZ4+KHgO/G3rs= gopkg.in/telebot.v3 v3.2.1/go.mod h1:GJKwwWqp9nSkIVN51eRKU78aB5f5OnQuWdwiIZfPbko= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/apps/iam/pkg/app/app.go b/apps/iam/pkg/app/app.go index edde9404280..2da47c45575 100644 --- a/apps/iam/pkg/app/app.go +++ b/apps/iam/pkg/app/app.go @@ -9,21 +9,34 @@ import ( "github.com/grafana/grafana-app-sdk/simple" foldersKind "github.com/grafana/grafana/apps/folder/pkg/apis/folder/v1beta1" "github.com/grafana/grafana/apps/iam/pkg/reconcilers" + "github.com/grafana/grafana/pkg/services/authz" ) -type AppConfig = reconcilers.AppConfig - var appManifestData = app.ManifestData{ AppName: "iam-folder-reconciler", Group: "iam.grafana.app", } -func Provider(appCfg AppConfig) app.Provider { +type AppConfig struct { + ZanzanaCfg authz.ZanzanaClientConfig + FolderReconcilerNamespace string +} + +func Provider(appCfg app.SpecificConfig) app.Provider { return simple.NewAppProvider(app.NewEmbeddedManifest(appManifestData), appCfg, New) } func New(cfg app.Config) (app.App, error) { - folderReconciler, err := reconcilers.NewFolderReconciler(cfg) + appSpecificConfig, ok := cfg.SpecificConfig.(AppConfig) + if !ok { + return nil, fmt.Errorf("invalid config type: expected AppConfig, got %T", cfg.SpecificConfig) + } + + folderReconciler, err := reconcilers.NewFolderReconciler(reconcilers.ReconcilerConfig{ + ZanzanaCfg: appSpecificConfig.ZanzanaCfg, + KubeConfig: &cfg.KubeConfig, + FolderReconcilerNamespace: appSpecificConfig.FolderReconcilerNamespace, + }) if err != nil { return nil, fmt.Errorf("unable to create FolderReconciler: %w", err) } diff --git a/apps/iam/pkg/reconcilers/folder_reconciler.go b/apps/iam/pkg/reconcilers/folder_reconciler.go index 21472487e29..93ac7264e0c 100644 --- a/apps/iam/pkg/reconcilers/folder_reconciler.go +++ b/apps/iam/pkg/reconcilers/folder_reconciler.go @@ -5,13 +5,11 @@ import ( "fmt" "time" - "github.com/grafana/grafana-app-sdk/app" "github.com/grafana/grafana-app-sdk/logging" "github.com/grafana/grafana-app-sdk/operator" foldersKind "github.com/grafana/grafana/apps/folder/pkg/apis/folder/v1beta1" - "github.com/grafana/grafana/pkg/services/authz/zanzana" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials/insecure" + "github.com/grafana/grafana/pkg/services/authz" + "k8s.io/client-go/rest" ) // FolderStore interface for retrieving folder information @@ -27,8 +25,9 @@ type PermissionStore interface { } // AppConfig represents the app-specific configuration -type AppConfig struct { - ZanzanaAddr string +type ReconcilerConfig struct { + ZanzanaCfg authz.ZanzanaClientConfig + KubeConfig *rest.Config FolderReconcilerNamespace string } @@ -37,21 +36,16 @@ type FolderReconciler struct { folderStore FolderStore } -func NewFolderReconciler(cfg app.Config) (operator.Reconciler, error) { - // Extract Zanzana address from config - appCfg, ok := cfg.SpecificConfig.(AppConfig) - if !ok { - return nil, fmt.Errorf("invalid config type: expected AppConfig, got %T", cfg.SpecificConfig) - } - +func NewFolderReconciler(cfg ReconcilerConfig) (operator.Reconciler, error) { // Create Zanzana client - zanzanaClient, err := getZanzanaClient(appCfg.ZanzanaAddr) + zanzanaClient, err := authz.NewZanzanaClient("*", cfg.ZanzanaCfg) + if err != nil { return nil, fmt.Errorf("unable to create zanzana client: %w", err) } // Create dependencies - folderStore := NewAPIFolderStore(&cfg.KubeConfig) + folderStore := NewAPIFolderStore(cfg.KubeConfig) permissionStore := NewZanzanaPermissionStore(zanzanaClient) folderReconciler := &FolderReconciler{ @@ -66,34 +60,11 @@ func NewFolderReconciler(cfg app.Config) (operator.Reconciler, error) { return reconciler, nil } -func getZanzanaClient(addr string) (zanzana.Client, error) { - transportCredentials := insecure.NewCredentials() - - dialOptions := []grpc.DialOption{ - grpc.WithTransportCredentials(transportCredentials), - } - - conn, err := grpc.NewClient(addr, dialOptions...) - if err != nil { - return nil, fmt.Errorf("failed to create zanzana client to remote server: %w", err) - } - - client, err := zanzana.NewClient(conn) - if err != nil { - return nil, fmt.Errorf("failed to initialize zanzana client: %w", err) - } - - return client, nil -} - func (r *FolderReconciler) reconcile(ctx context.Context, req operator.TypedReconcileRequest[*foldersKind.Folder]) (operator.ReconcileResult, error) { // Add timeout to prevent hanging operations ctx, cancel := context.WithTimeout(ctx, 60*time.Second) defer cancel() - logger := logging.FromContext(ctx) - logger.Info("Reconciling request", "req", req) - err := validateFolder(req.Object) if err != nil { return operator.ReconcileResult{}, err @@ -119,22 +90,24 @@ func (r *FolderReconciler) handleUpdateFolder(ctx context.Context, folder *folde parentUID, err := r.folderStore.GetFolderParent(ctx, namespace, folderUID) if err != nil { + logger.Error("Error getting folder parent", "error", err) return operator.ReconcileResult{}, err } parents, err := r.permissionStore.GetFolderParents(ctx, namespace, folderUID) if err != nil { + logger.Error("Error getting folder parents", "error", err) return operator.ReconcileResult{}, err } if (len(parents) == 0 && parentUID == "") || (len(parents) == 1 && parents[0] == parentUID) { - // Folder is already reconciled logger.Info("Folder is already reconciled", "folder", folderUID, "parent", parentUID, "namespace", namespace) return operator.ReconcileResult{}, nil } err = r.permissionStore.SetFolderParent(ctx, namespace, folderUID, parentUID) if err != nil { + logger.Error("Error setting folder parent", "error", err) return operator.ReconcileResult{}, err } @@ -151,6 +124,7 @@ func (r *FolderReconciler) handleDeleteFolder(ctx context.Context, folder *folde err := r.permissionStore.DeleteFolderParents(ctx, namespace, folderUID) if err != nil { + logger.Error("Error deleting folder parents", "error", err) return operator.ReconcileResult{}, err } diff --git a/pkg/services/authz/zanzana.go b/pkg/services/authz/zanzana.go index c42befe8d12..3c411f02fd5 100644 --- a/pkg/services/authz/zanzana.go +++ b/pkg/services/authz/zanzana.go @@ -42,41 +42,14 @@ func ProvideZanzana(cfg *setting.Cfg, db db.DB, tracer tracing.Tracer, features var client zanzana.Client switch cfg.ZanzanaClient.Mode { case setting.ZanzanaModeClient: - tokenClient, err := authnlib.NewTokenExchangeClient(authnlib.TokenExchangeConfig{ - Token: cfg.ZanzanaClient.Token, - TokenExchangeURL: cfg.ZanzanaClient.TokenExchangeURL, - }) - if err != nil { - return nil, fmt.Errorf("failed to initialize token exchange client: %w", err) - } - - if cfg.StackID == "" { - return nil, fmt.Errorf("missing stack ID") - } - - transportCredentials := insecure.NewCredentials() - if cfg.ZanzanaClient.ServerCertFile != "" { - transportCredentials, err = credentials.NewClientTLSFromFile(cfg.ZanzanaClient.ServerCertFile, "") - if err != nil { - return nil, fmt.Errorf("failed to initialize TLS certificate: %w", err) - } - } - dialOptions := []grpc.DialOption{ - grpc.WithTransportCredentials(transportCredentials), - grpc.WithPerRPCCredentials( - NewGRPCTokenAuth(AuthzServiceAudience, fmt.Sprintf("stacks-%s", cfg.StackID), tokenClient), - ), - } - - conn, err := grpc.NewClient(cfg.ZanzanaClient.Addr, dialOptions...) - if err != nil { - return nil, fmt.Errorf("failed to create zanzana client to remote server: %w", err) - } - - client, err = zanzana.NewClient(conn) - if err != nil { - return nil, fmt.Errorf("failed to initialize zanzana client: %w", err) - } + return NewZanzanaClient( + fmt.Sprintf("stacks-%s", cfg.StackID), + ZanzanaClientConfig{ + Address: cfg.ZanzanaClient.Addr, + Token: cfg.ZanzanaClient.Token, + TokenExchangeURL: cfg.ZanzanaClient.TokenExchangeURL, + ServerCertFile: cfg.ZanzanaClient.ServerCertFile, + }) case setting.ZanzanaModeEmbedded: store, err := zanzana.NewEmbeddedStore(cfg, db, logger) if err != nil { @@ -120,6 +93,54 @@ func ProvideZanzana(cfg *setting.Cfg, db db.DB, tracer tracing.Tracer, features return client, nil } +type ZanzanaClientConfig struct { + Address string + Token string + TokenExchangeURL string + ServerCertFile string +} + +func NewZanzanaClient(namespace string, cfg ZanzanaClientConfig) (zanzana.Client, error) { + tokenClient, err := authnlib.NewTokenExchangeClient(authnlib.TokenExchangeConfig{ + Token: cfg.Token, + TokenExchangeURL: cfg.TokenExchangeURL, + }) + if err != nil { + return nil, fmt.Errorf("failed to create token exchange client: %w", err) + } + + transportCredentials := insecure.NewCredentials() + if cfg.ServerCertFile != "" { + transportCredentials, err = credentials.NewClientTLSFromFile(cfg.ServerCertFile, "") + if err != nil { + return nil, fmt.Errorf("failed to initialize TLS certificate: %w", err) + } + } + + dialOptions := []grpc.DialOption{ + grpc.WithTransportCredentials(transportCredentials), + grpc.WithPerRPCCredentials( + NewGRPCTokenAuth( + AuthzServiceAudience, + namespace, + tokenClient, + ), + ), + } + + conn, err := grpc.NewClient(cfg.Address, dialOptions...) + if err != nil { + return nil, fmt.Errorf("failed to create zanzana client to remote server: %w", err) + } + + client, err := zanzana.NewClient(conn) + if err != nil { + return nil, fmt.Errorf("failed to initialize zanzana client: %w", err) + } + + return client, nil +} + type ZanzanaService interface { services.NamedService } From fc739de01dee970edab1e43a4bceb2af2dcae5a5 Mon Sep 17 00:00:00 2001 From: Matheus Macabu Date: Tue, 26 Aug 2025 16:42:48 +0200 Subject: [PATCH 12/24] Dependencies(js): Update sha.js to 2.4.12 (#110153) --- yarn.lock | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/yarn.lock b/yarn.lock index dfd8656a6b8..0bd82fd1044 100644 --- a/yarn.lock +++ b/yarn.lock @@ -28781,7 +28781,7 @@ __metadata: languageName: node linkType: hard -"safe-buffer@npm:5.2.1, safe-buffer@npm:>=5.1.0, safe-buffer@npm:^5.0.1, safe-buffer@npm:^5.1.0, safe-buffer@npm:^5.1.2, safe-buffer@npm:~5.2.0": +"safe-buffer@npm:5.2.1, safe-buffer@npm:>=5.1.0, safe-buffer@npm:^5.0.1, safe-buffer@npm:^5.1.0, safe-buffer@npm:^5.1.2, safe-buffer@npm:^5.2.1, safe-buffer@npm:~5.2.0": version: 5.2.1 resolution: "safe-buffer@npm:5.2.1" checksum: 10/32872cd0ff68a3ddade7a7617b8f4c2ae8764d8b7d884c651b74457967a9e0e886267d3ecc781220629c44a865167b61c375d2da6c720c840ecd73f45d5d9451 @@ -29115,14 +29115,15 @@ __metadata: linkType: hard "sha.js@npm:^2.4.11": - version: 2.4.11 - resolution: "sha.js@npm:2.4.11" + version: 2.4.12 + resolution: "sha.js@npm:2.4.12" dependencies: - inherits: "npm:^2.0.1" - safe-buffer: "npm:^5.0.1" + inherits: "npm:^2.0.4" + safe-buffer: "npm:^5.2.1" + to-buffer: "npm:^1.2.0" bin: - sha.js: ./bin.js - checksum: 10/d833bfa3e0a67579a6ce6e1bc95571f05246e0a441dd8c76e3057972f2a3e098465687a4369b07e83a0375a88703577f71b5b2e966809e67ebc340dbedb478c7 + sha.js: bin.js + checksum: 10/39c0993592c2ab34eb2daae2199a2a1d502713765aecb611fd97c0c4ab7cd53e902d628e1962aaf384bafd28f55951fef46dcc78799069ce41d74b03aa13b5a7 languageName: node linkType: hard @@ -31021,6 +31022,17 @@ __metadata: languageName: node linkType: hard +"to-buffer@npm:^1.2.0": + version: 1.2.1 + resolution: "to-buffer@npm:1.2.1" + dependencies: + isarray: "npm:^2.0.5" + safe-buffer: "npm:^5.2.1" + typed-array-buffer: "npm:^1.0.3" + checksum: 10/f8d03f070b8567d9c949f1b59c8d47c83ed2e59b50b5449258f931df9a1fcb751aa8bb8756a9345adc529b6b1822521157c48e1a7d01779a47185060d7bf96d4 + languageName: node + linkType: hard + "to-camel-case@npm:1.0.0": version: 1.0.0 resolution: "to-camel-case@npm:1.0.0" From 1c587a983f855f1fbd478aeb7f476ffc4bc5a602 Mon Sep 17 00:00:00 2001 From: Kristina Date: Tue, 26 Aug 2025 10:14:05 -0500 Subject: [PATCH 13/24] Correlations: Add better handling to correlation type and update documentation (#109922) * Add better type handling when correlation is created from provisioning and fix documentation * add external example * change error when deprecated key is used --- .../create-a-new-correlation/index.md | 16 ++++++++++++---- pkg/services/correlations/database.go | 2 +- pkg/services/correlations/models.go | 2 +- .../provisioning/datasources/datasources.go | 15 +++++++++------ .../correlations_provisioning_api_test.go | 2 +- 5 files changed, 24 insertions(+), 13 deletions(-) diff --git a/docs/sources/administration/correlations/create-a-new-correlation/index.md b/docs/sources/administration/correlations/create-a-new-correlation/index.md index 485376804b4..9eedf7ed657 100644 --- a/docs/sources/administration/correlations/create-a-new-correlation/index.md +++ b/docs/sources/administration/correlations/create-a-new-correlation/index.md @@ -52,8 +52,8 @@ datasources: - targetUID: uid label: "test" description: "..." + type: query config: - type: "query" target: expr: "..." field: "name" @@ -64,6 +64,14 @@ datasources: mapValue: "other" - type: logfmt field: "test" + - targetUID: uid2 + label: "test 2" + description: "..." + type: external + config: + target: + url: "http://${example}" + field: "name" ``` Description of provisioning properties: @@ -77,12 +85,12 @@ Description of provisioning properties: **description** : Optional description +**type** +: Correlation type. Valid values are "query" for linking to a data source query and "external" for linking to an external URL. + **config** : Config object -**config.type** -: Correlation type. Valid values are "query" for linking to a data source query and "external" for linking to an external URL. - **config.target** : [Target query model](#determine-target-query-model-structure) diff --git a/pkg/services/correlations/database.go b/pkg/services/correlations/database.go index cd8361108a3..c9a78cbb042 100644 --- a/pkg/services/correlations/database.go +++ b/pkg/services/correlations/database.go @@ -30,7 +30,7 @@ func (s CorrelationsService) createCorrelation(ctx context.Context, cmd CreateCo if correlation.Config.Type == CorrelationType("query") { correlation.Type = CorrelationType("query") } else if correlation.Config.Type != "" { - return correlation, ErrInvalidConfigType + return correlation, ErrConfigTypeDeprecated } err := s.SQLStore.WithTransactionalDbSession(ctx, func(session *db.Session) error { diff --git a/pkg/services/correlations/models.go b/pkg/services/correlations/models.go index 4e941517a38..5c655db2798 100644 --- a/pkg/services/correlations/models.go +++ b/pkg/services/correlations/models.go @@ -20,7 +20,7 @@ var ( ErrTransformationRegexReqExp = errors.New("regex transformations require expression") ErrCorrelationsQuotaFailed = errors.New("error getting correlations quota") ErrCorrelationsQuotaReached = errors.New("correlations quota reached") - ErrInvalidConfigType = errors.New("correlation contains non default value in config.type") + ErrConfigTypeDeprecated = errors.New("config.type is deprecated; please move type to be sibling of config") ) const ( diff --git a/pkg/services/provisioning/datasources/datasources.go b/pkg/services/provisioning/datasources/datasources.go index 55edd021d00..05c242fb9c4 100644 --- a/pkg/services/provisioning/datasources/datasources.go +++ b/pkg/services/provisioning/datasources/datasources.go @@ -194,9 +194,12 @@ func (dc *DatasourceProvisioner) applyChanges(ctx context.Context, configPath st func makeCreateCorrelationCommand(correlation map[string]any, SourceUID string, OrgId int64) (correlations.CreateCorrelationCommand, error) { // we look for a correlation type at the root if it is defined, if not use default // we ignore the legacy config.type value - the only valid value at that version was "query" - var corrType = correlation["type"] - if corrType == nil || corrType == "" { - corrType = correlations.CorrelationType("query") + var corrTypeStr = correlation["type"] + var corrType = correlations.CorrelationType("query") + + // if corTypeStr is nil, an empty string, or query, leave it as query + if corrTypeStr == "external" { + corrType = correlations.CorrelationType("external") } var json = jsoniter.ConfigCompatibleWithStandardLibrary @@ -206,7 +209,7 @@ func makeCreateCorrelationCommand(correlation map[string]any, SourceUID string, Description: correlation["description"].(string), OrgId: OrgId, Provisioned: true, - Type: corrType.(correlations.CorrelationType), + Type: corrType, } targetUID, ok := correlation["targetUID"].(string) @@ -230,8 +233,8 @@ func makeCreateCorrelationCommand(correlation map[string]any, SourceUID string, } // config.type is a deprecated place for this value. We will default it to "query" for legacy purposes but non-query correlations should have type outside of config - if config.Type != correlations.CorrelationType("query") { - return correlations.CreateCorrelationCommand{}, correlations.ErrInvalidConfigType + if config.Type != "" && config.Type != correlations.CorrelationType("query") { + return correlations.CreateCorrelationCommand{}, correlations.ErrConfigTypeDeprecated } createCommand.Config = config diff --git a/pkg/tests/api/correlations/correlations_provisioning_api_test.go b/pkg/tests/api/correlations/correlations_provisioning_api_test.go index e8c1f1a5d8a..2146652f5d4 100644 --- a/pkg/tests/api/correlations/correlations_provisioning_api_test.go +++ b/pkg/tests/api/correlations/correlations_provisioning_api_test.go @@ -175,6 +175,6 @@ func TestIntegrationCreateOrUpdateCorrelation(t *testing.T) { }) require.Error(t, err) - require.ErrorIs(t, err, correlations.ErrInvalidConfigType) + require.ErrorIs(t, err, correlations.ErrConfigTypeDeprecated) }) } From c5667476a759c029d64d60fa57b1ba90baaab8fe Mon Sep 17 00:00:00 2001 From: Yuri Tseretyan Date: Tue, 26 Aug 2025 12:17:48 -0400 Subject: [PATCH 14/24] Alerting: Update ticker to accept logger in the constructor (#110176) * add logger to ticker * move ticker to schedule --- pkg/services/ngalert/metrics/scheduler.go | 2 +- pkg/services/ngalert/schedule/schedule.go | 4 ++-- .../ngalert/schedule}/ticker/metrics.go | 0 .../ngalert/schedule}/ticker/ticker.go | 9 +++++---- .../ngalert/schedule}/ticker/ticker_test.go | 16 +++++++++------- 5 files changed, 17 insertions(+), 14 deletions(-) rename pkg/{util => services/ngalert/schedule}/ticker/metrics.go (100%) rename pkg/{util => services/ngalert/schedule}/ticker/ticker.go (89%) rename pkg/{util => services/ngalert/schedule}/ticker/ticker_test.go (95%) diff --git a/pkg/services/ngalert/metrics/scheduler.go b/pkg/services/ngalert/metrics/scheduler.go index 2a3cf441702..d6c6707f0a9 100644 --- a/pkg/services/ngalert/metrics/scheduler.go +++ b/pkg/services/ngalert/metrics/scheduler.go @@ -4,7 +4,7 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" - "github.com/grafana/grafana/pkg/util/ticker" + "github.com/grafana/grafana/pkg/services/ngalert/schedule/ticker" ) const ( diff --git a/pkg/services/ngalert/schedule/schedule.go b/pkg/services/ngalert/schedule/schedule.go index 67aba2ab917..e0b3b6faf0d 100644 --- a/pkg/services/ngalert/schedule/schedule.go +++ b/pkg/services/ngalert/schedule/schedule.go @@ -18,9 +18,9 @@ import ( "github.com/grafana/grafana/pkg/services/ngalert/eval" "github.com/grafana/grafana/pkg/services/ngalert/metrics" ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models" + "github.com/grafana/grafana/pkg/services/ngalert/schedule/ticker" "github.com/grafana/grafana/pkg/services/ngalert/state" "github.com/grafana/grafana/pkg/setting" - "github.com/grafana/grafana/pkg/util/ticker" ) // ScheduleService is an interface for a service that schedules the evaluation @@ -169,7 +169,7 @@ func NewScheduler(cfg SchedulerCfg, stateManager *state.Manager) *schedule { func (sch *schedule) Run(ctx context.Context) error { sch.log.Info("Starting scheduler", "tickInterval", sch.baseInterval, "maxAttempts", sch.maxAttempts) - t := ticker.New(sch.clock, sch.baseInterval, sch.metrics.Ticker) + t := ticker.New(sch.clock, sch.baseInterval, sch.metrics.Ticker, sch.log) defer t.Stop() if err := sch.schedulePeriodic(ctx, t); err != nil { diff --git a/pkg/util/ticker/metrics.go b/pkg/services/ngalert/schedule/ticker/metrics.go similarity index 100% rename from pkg/util/ticker/metrics.go rename to pkg/services/ngalert/schedule/ticker/metrics.go diff --git a/pkg/util/ticker/ticker.go b/pkg/services/ngalert/schedule/ticker/ticker.go similarity index 89% rename from pkg/util/ticker/ticker.go rename to pkg/services/ngalert/schedule/ticker/ticker.go index 502325ca07f..a52b9c4e559 100644 --- a/pkg/util/ticker/ticker.go +++ b/pkg/services/ngalert/schedule/ticker/ticker.go @@ -21,10 +21,11 @@ type T struct { interval time.Duration metrics *Metrics stopCh chan struct{} + logger log.Logger } // NewTicker returns a Ticker that ticks on interval marks (or very shortly after) starting at c.Now(), and never drops ticks. interval should not be negative or zero. -func New(c clock.Clock, interval time.Duration, metric *Metrics) *T { +func New(c clock.Clock, interval time.Duration, metric *Metrics, logger log.Logger) *T { if interval <= 0 { panic(fmt.Errorf("non-positive interval [%v] is not allowed", interval)) } @@ -35,6 +36,7 @@ func New(c clock.Clock, interval time.Duration, metric *Metrics) *T { interval: interval, metrics: metric, stopCh: make(chan struct{}), + logger: logger, } metric.IntervalSeconds.Set(t.interval.Seconds()) // Seconds report fractional part as well, so it matches the format of the timestamp we report below go t.run() @@ -47,8 +49,7 @@ func getStartTick(clk clock.Clock, interval time.Duration) time.Time { } func (t *T) run() { - logger := log.New("ticker") - logger.Info("starting", "first_tick", t.last.Add(t.interval)) + t.logger.Info("starting", "component", "ticker", "first_tick", t.last.Add(t.interval)) LOOP: for { next := t.last.Add(t.interval) // calculate the time of the next tick @@ -72,7 +73,7 @@ LOOP: break LOOP } } - logger.Info("stopped", "last_tick", t.last) + t.logger.Info("stopped", "component", "ticker", "last_tick", t.last) } // Stop stops the ticker. It does not close the C channel diff --git a/pkg/util/ticker/ticker_test.go b/pkg/services/ngalert/schedule/ticker/ticker_test.go similarity index 95% rename from pkg/util/ticker/ticker_test.go rename to pkg/services/ngalert/schedule/ticker/ticker_test.go index 39cbca221e7..b83dabff083 100644 --- a/pkg/util/ticker/ticker_test.go +++ b/pkg/services/ngalert/schedule/ticker/ticker_test.go @@ -13,6 +13,8 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/testutil" "github.com/stretchr/testify/require" + + "github.com/grafana/grafana/pkg/infra/log/logtest" ) func TestTicker(t *testing.T) { @@ -51,7 +53,7 @@ func TestTicker(t *testing.T) { interval := time.Duration(rand.Int63n(100)+10) * time.Second clk := clock.NewMock() clk.Add(interval) // align clock with the start tick - ticker := New(clk, interval, NewMetrics(prometheus.NewRegistry(), "test")) + ticker := New(clk, interval, NewMetrics(prometheus.NewRegistry(), "test"), &logtest.Fake{}) ticks := rand.Intn(9) + 1 jitter := rand.Int63n(int64(interval) - 1) @@ -85,7 +87,7 @@ func TestTicker(t *testing.T) { t.Run("should not put anything to channel until it's time", func(t *testing.T) { clk := clock.NewMock() interval := time.Duration(rand.Int63n(9)+1) * time.Second - ticker := New(clk, interval, NewMetrics(prometheus.NewRegistry(), "test")) + ticker := New(clk, interval, NewMetrics(prometheus.NewRegistry(), "test"), &logtest.Fake{}) expectedTick := clk.Now().Add(interval) for { require.Empty(t, ticker.C) @@ -102,7 +104,7 @@ func TestTicker(t *testing.T) { t.Run("should put the tick in the channel immediately if it is behind", func(t *testing.T) { clk := clock.NewMock() interval := time.Duration(rand.Int63n(9)+1) * time.Second - ticker := New(clk, interval, NewMetrics(prometheus.NewRegistry(), "test")) + ticker := New(clk, interval, NewMetrics(prometheus.NewRegistry(), "test"), &logtest.Fake{}) // We can expect the first tick to be at a consistent interval. Take a snapshot of the clock now, before we advance it. expectedTick := clk.Now().Add(interval) @@ -131,7 +133,7 @@ func TestTicker(t *testing.T) { clk.Set(time.Now()) interval := time.Duration(rand.Int63n(9)+1) * time.Second registry := prometheus.NewPedanticRegistry() - ticker := New(clk, interval, NewMetrics(registry, "test")) + ticker := New(clk, interval, NewMetrics(registry, "test"), &logtest.Fake{}) expectedTick := getStartTick(clk, interval).Add(interval) expectedMetricFmt := `# HELP grafana_test_ticker_interval_seconds Interval at which the ticker is meant to tick. @@ -174,7 +176,7 @@ func TestTicker(t *testing.T) { t.Run("when it waits for the next tick", func(t *testing.T) { clk := clock.NewMock() interval := time.Duration(rand.Int63n(9)+1) * time.Second - ticker := New(clk, interval, NewMetrics(prometheus.NewRegistry(), "test")) + ticker := New(clk, interval, NewMetrics(prometheus.NewRegistry(), "test"), &logtest.Fake{}) clk.Add(interval) readChanOrFail(t, ticker.C) ticker.Stop() @@ -185,7 +187,7 @@ func TestTicker(t *testing.T) { t.Run("when it waits for the tick to be consumed", func(t *testing.T) { clk := clock.NewMock() interval := time.Duration(rand.Int63n(9)+1) * time.Second - ticker := New(clk, interval, NewMetrics(prometheus.NewRegistry(), "test")) + ticker := New(clk, interval, NewMetrics(prometheus.NewRegistry(), "test"), &logtest.Fake{}) clk.Add(interval) ticker.Stop() require.Empty(t, ticker.C) @@ -194,7 +196,7 @@ func TestTicker(t *testing.T) { t.Run("multiple times", func(t *testing.T) { clk := clock.NewMock() interval := time.Duration(rand.Int63n(9)+1) * time.Second - ticker := New(clk, interval, NewMetrics(prometheus.NewRegistry(), "test")) + ticker := New(clk, interval, NewMetrics(prometheus.NewRegistry(), "test"), &logtest.Fake{}) ticker.Stop() ticker.Stop() ticker.Stop() From 5e1f79ca83f22caafdaac3c15999834189eb56fd Mon Sep 17 00:00:00 2001 From: Alexander Akhmetov Date: Tue, 26 Aug 2025 18:20:50 +0200 Subject: [PATCH 15/24] Alerting: Fix alert rule comparison with the same missing_evals_to_resolve (#110174) --- pkg/services/ngalert/store/models.go | 6 +++++- pkg/services/ngalert/store/models_test.go | 24 +++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/pkg/services/ngalert/store/models.go b/pkg/services/ngalert/store/models.go index 775f6f54542..254ec8cb048 100644 --- a/pkg/services/ngalert/store/models.go +++ b/pkg/services/ngalert/store/models.go @@ -92,7 +92,11 @@ func (a alertRuleVersion) EqualSpec(b alertRuleVersion) bool { a.IsPaused == b.IsPaused && a.NotificationSettings == b.NotificationSettings && a.Metadata == b.Metadata && - a.MissingSeriesEvalsToResolve == b.MissingSeriesEvalsToResolve + compareInt64Pointer(a.MissingSeriesEvalsToResolve, b.MissingSeriesEvalsToResolve) +} + +func compareInt64Pointer(a, b *int64) bool { + return (a == nil && b == nil) || (a != nil && b != nil && *a == *b) } func (a alertRuleVersion) TableName() string { diff --git a/pkg/services/ngalert/store/models_test.go b/pkg/services/ngalert/store/models_test.go index e37598c3714..ddbc34b4036 100644 --- a/pkg/services/ngalert/store/models_test.go +++ b/pkg/services/ngalert/store/models_test.go @@ -71,6 +71,30 @@ func TestAlertRuleVersion_EqualSpec(t *testing.T) { b: func() alertRuleVersion { v := baseVersion; v.MissingSeriesEvalsToResolve = nil; return v }(), expect: true, }, + { + name: "same MissingSeriesEvalsToResolve value, different pointers", + a: func() alertRuleVersion { + v := baseVersion + v.MissingSeriesEvalsToResolve = util.Pointer(int64(10)) + return v + }(), + b: func() alertRuleVersion { + v := baseVersion + v.MissingSeriesEvalsToResolve = util.Pointer(int64(10)) + return v + }(), + expect: true, + }, + { + name: "different MissingSeriesEvalsToResolve", + a: func() alertRuleVersion { + v := baseVersion + v.MissingSeriesEvalsToResolve = util.Pointer(int64(123)) + return v + }(), + b: baseVersion, + expect: false, + }, { name: "different NotificationSettings", a: baseVersion, From 3056924d107d95abe02a81d1d86f968ea8e105dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mustafa=20Sencer=20=C3=96zcan?= <32759850+mustafasencer@users.noreply.github.com> Date: Tue, 26 Aug 2025 18:40:22 +0200 Subject: [PATCH 16/24] fix: enforce timeout for requests run in qos (#110162) --- pkg/storage/unified/resource/server.go | 110 ++++++++++++-------- pkg/storage/unified/resource/server_test.go | 109 +++++++++++++++++++ 2 files changed, 173 insertions(+), 46 deletions(-) diff --git a/pkg/storage/unified/resource/server.go b/pkg/storage/unified/resource/server.go index f5dc2b7f2a5..2152011d901 100644 --- a/pkg/storage/unified/resource/server.go +++ b/pkg/storage/unified/resource/server.go @@ -28,15 +28,6 @@ import ( "github.com/grafana/grafana/pkg/util/scheduler" ) -const ( - // DefaultMaxBackoff is the default maximum backoff duration for enqueue operations. - DefaultMaxBackoff = 1 * time.Second - // DefaultMinBackoff is the default minimum backoff duration for enqueue operations. - DefaultMinBackoff = 100 * time.Millisecond - // DefaultMaxRetries is the default maximum number of retries for enqueue operations. - DefaultMaxRetries = 3 -) - // ResourceServer implements all gRPC services type ResourceServer interface { resourcepb.ResourceStoreServer @@ -162,6 +153,13 @@ type QOSEnqueuer interface { Enqueue(ctx context.Context, tenantID string, runnable func()) error } +type QueueConfig struct { + MaxBackoff time.Duration + MinBackoff time.Duration + MaxRetries int + Timeout time.Duration +} + type BlobConfig struct { // The CDK configuration URL URL string @@ -240,7 +238,8 @@ type ResourceServerOptions struct { MaxPageSizeBytes int // QOSQueue is the quality of service queue used to enqueue - QOSQueue QOSEnqueuer + QOSQueue QOSEnqueuer + QOSConfig QueueConfig Ring *ring.Ring RingLifecycler *ring.BasicLifecycler @@ -281,6 +280,19 @@ func NewResourceServer(opts ResourceServerOptions) (*server, error) { opts.QOSQueue = scheduler.NewNoopQueue() } + if opts.QOSConfig.Timeout == 0 { + opts.QOSConfig.Timeout = 30 * time.Second + } + if opts.QOSConfig.MaxBackoff == 0 { + opts.QOSConfig.MaxBackoff = 1 * time.Second + } + if opts.QOSConfig.MinBackoff == 0 { + opts.QOSConfig.MinBackoff = 100 * time.Millisecond + } + if opts.QOSConfig.MaxRetries == 0 { + opts.QOSConfig.MaxRetries = 3 + } + // Initialize the blob storage blobstore := opts.Blob.Backend if blobstore == nil { @@ -326,6 +338,7 @@ func NewResourceServer(opts ResourceServerOptions) (*server, error) { maxPageSizeBytes: opts.MaxPageSizeBytes, reg: opts.Reg, queue: opts.QOSQueue, + queueConfig: opts.QOSConfig, } if opts.Search.Resources != nil { @@ -375,6 +388,7 @@ type server struct { maxPageSizeBytes int reg prometheus.Registerer queue QOSEnqueuer + queueConfig QueueConfig } // Init implements ResourceServer. @@ -635,8 +649,8 @@ func (s *server) Create(ctx context.Context, req *resourcepb.CreateRequest) (*re res *resourcepb.CreateResponse err error ) - runErr := s.runInQueue(ctx, req.Key.Namespace, func() { - res, err = s.create(ctx, user, req) + runErr := s.runInQueue(ctx, req.Key.Namespace, func(queueCtx context.Context) { + res, err = s.create(queueCtx, user, req) }) if runErr != nil { return HandleQueueError(runErr, func(e *resourcepb.ErrorResult) *resourcepb.CreateResponse { @@ -689,8 +703,8 @@ func (s *server) Update(ctx context.Context, req *resourcepb.UpdateRequest) (*re res *resourcepb.UpdateResponse err error ) - runErr := s.runInQueue(ctx, req.Key.Namespace, func() { - res, err = s.update(ctx, user, req) + runErr := s.runInQueue(ctx, req.Key.Namespace, func(queueCtx context.Context) { + res, err = s.update(queueCtx, user, req) }) if runErr != nil { return HandleQueueError(runErr, func(e *resourcepb.ErrorResult) *resourcepb.UpdateResponse { @@ -757,8 +771,8 @@ func (s *server) Delete(ctx context.Context, req *resourcepb.DeleteRequest) (*re err error ) - runErr := s.runInQueue(ctx, req.Key.Namespace, func() { - res, err = s.delete(ctx, user, req) + runErr := s.runInQueue(ctx, req.Key.Namespace, func(queueCtx context.Context) { + res, err = s.delete(queueCtx, user, req) }) if runErr != nil { return HandleQueueError(runErr, func(e *resourcepb.ErrorResult) *resourcepb.DeleteResponse { @@ -868,8 +882,8 @@ func (s *server) Read(ctx context.Context, req *resourcepb.ReadRequest) (*resour res *resourcepb.ReadResponse err error ) - runErr := s.runInQueue(ctx, req.Key.Namespace, func() { - res, err = s.read(ctx, user, req) + runErr := s.runInQueue(ctx, req.Key.Namespace, func(queueCtx context.Context) { + res, err = s.read(queueCtx, user, req) }) if runErr != nil { return HandleQueueError(runErr, func(e *resourcepb.ErrorResult) *resourcepb.ReadResponse { @@ -1375,40 +1389,44 @@ func (s *server) GetBlob(ctx context.Context, req *resourcepb.GetBlobRequest) (* return rsp, nil } -func (s *server) runInQueue(ctx context.Context, tenantID string, runnable func()) error { - boff := backoff.New(ctx, backoff.Config{ - MinBackoff: DefaultMinBackoff, - MaxBackoff: DefaultMaxBackoff, - MaxRetries: DefaultMaxRetries, +func (s *server) runInQueue(ctx context.Context, tenantID string, runnable func(ctx context.Context)) error { + // Enforce a timeout for the entire operation, including queueing and execution. + queueCtx, cancel := context.WithTimeout(ctx, s.queueConfig.Timeout) + defer cancel() + + done := make(chan struct{}) + wrappedRunnable := func() { + defer close(done) + runnable(queueCtx) + } + + // Retry enqueueing with backoff, respecting the timeout context. + boff := backoff.New(queueCtx, backoff.Config{ + MinBackoff: s.queueConfig.MinBackoff, + MaxBackoff: s.queueConfig.MaxBackoff, + MaxRetries: s.queueConfig.MaxRetries, }) - var ( - wg sync.WaitGroup - err error - ) - wg.Add(1) - wrapped := func() { - defer wg.Done() - runnable() - } - for boff.Ongoing() { - err = s.queue.Enqueue(ctx, tenantID, wrapped) + for { + err := s.queue.Enqueue(queueCtx, tenantID, wrappedRunnable) if err == nil { + // Successfully enqueued. break } - s.log.Warn("failed to enqueue runnable, retrying", - "maxRetries", DefaultMaxRetries, - "tenantID", tenantID, - "error", err) + + s.log.Warn("failed to enqueue runnable, retrying", "tenantID", tenantID, "error", err) + if !boff.Ongoing() { + // Backoff finished (retries exhausted or context canceled). + return fmt.Errorf("failed to enqueue for tenant %s: %w", tenantID, err) + } boff.Wait() } - if err != nil { - s.log.Error("failed to enqueue runnable", - "maxRetries", DefaultMaxRetries, - "tenantID", tenantID, - "error", err) - return fmt.Errorf("failed to enqueue runnable for tenant %s: %w", tenantID, err) + + // Wait for the runnable to complete or for the context to be done. + select { + case <-done: + return nil // Completed successfully. + case <-queueCtx.Done(): + return queueCtx.Err() // Timed out or canceled while waiting for execution. } - wg.Wait() - return nil } diff --git a/pkg/storage/unified/resource/server_test.go b/pkg/storage/unified/resource/server_test.go index b0dff754a18..98bc928da63 100644 --- a/pkg/storage/unified/resource/server_test.go +++ b/pkg/storage/unified/resource/server_test.go @@ -4,20 +4,27 @@ import ( "context" "encoding/json" "fmt" + "log/slog" "net/http" "os" + "sync" "testing" "time" + "github.com/prometheus/client_golang/prometheus" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "gocloud.dev/blob/fileblob" "gocloud.dev/blob/memblob" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" authlib "github.com/grafana/authlib/types" + "github.com/grafana/dskit/services" "github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/apimachinery/utils" + "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/storage/unified/resourcepb" + "github.com/grafana/grafana/pkg/util/scheduler" ) func TestSimpleServer(t *testing.T) { @@ -242,3 +249,105 @@ func TestSimpleServer(t *testing.T) { require.ErrorIs(t, err, ErrOptimisticLockingFailed) }) } + +func TestRunInQueue(t *testing.T) { + const testTenantID = "test-tenant" + t.Run("should execute successfully when queue has capacity", func(t *testing.T) { + s, _ := newTestServerWithQueue(t, 1, 1) + executed := make(chan bool, 1) + + runnable := func(ctx context.Context) { + executed <- true + } + + err := s.runInQueue(context.Background(), testTenantID, runnable) + require.NoError(t, err) + assert.True(t, <-executed, "runnable should have been executed") + }) + + t.Run("should time out if a task is sitting in the queue beyond the timeout", func(t *testing.T) { + s, _ := newTestServerWithQueue(t, 1, 1) + executed := make(chan struct{}, 1) + runnable := func(ctx context.Context) { + time.Sleep(1 * time.Second) + executed <- struct{}{} + } + + err := s.runInQueue(context.Background(), testTenantID, runnable) + require.Error(t, err) + assert.Equal(t, context.DeadlineExceeded, err) + <-executed + }) + + t.Run("should return an error if queue is consistently full after retrying", func(t *testing.T) { + s, q := newTestServerWithQueue(t, 1, 1) + // Task 1: This will be picked up by the worker and block it. + blocker := make(chan struct{}) + defer close(blocker) + blockingRunnable := func() { + <-blocker + } + err := q.Enqueue(context.Background(), testTenantID, blockingRunnable) + require.NoError(t, err) + for q.Len() > 0 { + time.Sleep(100 * time.Millisecond) + } + err = q.Enqueue(context.Background(), testTenantID, blockingRunnable) + require.NoError(t, err) + + // Task 2: This runnable should never execute because the queue is full. + mu := sync.Mutex{} + executed := false + runnable := func(ctx context.Context) { + mu.Lock() + defer mu.Unlock() + executed = true + } + + err = s.runInQueue(context.Background(), testTenantID, runnable) + require.Error(t, err) + require.ErrorIs(t, err, scheduler.ErrTenantQueueFull) + require.False(t, executed, "runnable should not have been executed") + }) +} + +// newTestServerWithQueue creates a server with a real scheduler.Queue for testing. +// It also sets up a worker to consume items from the queue. +func newTestServerWithQueue(t *testing.T, maxSizePerTenant int, numWorkers int) (*server, *scheduler.Queue) { + t.Helper() + q := scheduler.NewQueue(&scheduler.QueueOptions{ + MaxSizePerTenant: maxSizePerTenant, + Registerer: prometheus.NewRegistry(), + Logger: log.NewNopLogger(), + }) + err := services.StartAndAwaitRunning(context.Background(), q) + require.NoError(t, err) + t.Cleanup(func() { + err := services.StopAndAwaitTerminated(context.Background(), q) + require.NoError(t, err) + }) + + // Create a worker to consume from the queue + worker, err := scheduler.NewScheduler(q, &scheduler.Config{ + Logger: log.NewNopLogger(), + NumWorkers: numWorkers, + }) + require.NoError(t, err) + err = services.StartAndAwaitRunning(context.Background(), worker) + require.NoError(t, err) + t.Cleanup(func() { + err := services.StopAndAwaitTerminated(context.Background(), worker) + require.NoError(t, err) + }) + + s := &server{ + queue: q, + queueConfig: QueueConfig{ + Timeout: 500 * time.Millisecond, + MaxRetries: 2, + MinBackoff: 10 * time.Millisecond, + }, + log: slog.Default(), + } + return s, q +} From 578e07fd0ea4885586f9c12f25eb69fd36c7e497 Mon Sep 17 00:00:00 2001 From: Hugo Kiyodi Oshiro Date: Tue, 26 Aug 2025 14:43:46 -0300 Subject: [PATCH 17/24] Plugins: Fix typo in connections homepage card (#109793) --- public/app/features/connections/Connections.test.tsx | 2 +- public/app/features/connections/components/PageCard/CardData.ts | 2 +- public/locales/en-US/grafana.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/public/app/features/connections/Connections.test.tsx b/public/app/features/connections/Connections.test.tsx index 3778042af10..1b92b5dfd1c 100644 --- a/public/app/features/connections/Connections.test.tsx +++ b/public/app/features/connections/Connections.test.tsx @@ -51,7 +51,7 @@ describe('Connections', () => { // Add new connection card expect(await screen.findByText('Add new connection')).toBeVisible(); - expect(await screen.findByText('Collector:')).toBeVisible(); + expect(await screen.findByText('Collector')).toBeVisible(); expect(await screen.findByText('Data sources')).toBeVisible(); expect(await screen.findByText('Integrations')).toBeVisible(); expect(await screen.findByText('Private data source connect')).toBeVisible(); diff --git a/public/app/features/connections/components/PageCard/CardData.ts b/public/app/features/connections/components/PageCard/CardData.ts index 8355fdc5aa8..670072a5ee9 100644 --- a/public/app/features/connections/components/PageCard/CardData.ts +++ b/public/app/features/connections/components/PageCard/CardData.ts @@ -20,7 +20,7 @@ export function getCloudCardData(): CardData[] { icon: 'plus-circle', }, { - text: t('connections.cloud.connections-home-page.collector.title', 'Collector:'), + text: t('connections.cloud.connections-home-page.collector.title', 'Collector'), subTitle: t( 'connections.cloud.connections-home-page.collector.subtitle', 'Manage the configuration of Grafana Alloy, our distribution of the OpenTelemetry Collector' diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json index 3c86d003d9a..928977ce31f 100644 --- a/public/locales/en-US/grafana.json +++ b/public/locales/en-US/grafana.json @@ -4167,7 +4167,7 @@ }, "collector": { "subtitle": "Manage the configuration of Grafana Alloy, our distribution of the OpenTelemetry Collector", - "title": "Collector:" + "title": "Collector" }, "data-sources": { "subtitle": "Manage your existing data source connections", From 740945511da141243c0bf35c72c82b527659f70f Mon Sep 17 00:00:00 2001 From: Matias Chomicki Date: Tue, 26 Aug 2025 19:46:01 +0200 Subject: [PATCH 18/24] New Logs Panel: More analytics (#110149) * LogLineDetailsDisplayedFields: report fields reorganization * LogLineDetailsTrace: analytics * Prettier * Rename interactions key --- .../panel/LogLineDetailsDisplayedFields.tsx | 16 +++++++++++----- .../components/panel/LogLineDetailsTrace.tsx | 10 +++++++++- .../logs/components/panel/LogListContext.tsx | 12 ++---------- .../features/logs/components/panel/analytics.ts | 10 ++++++++++ 4 files changed, 32 insertions(+), 16 deletions(-) create mode 100644 public/app/features/logs/components/panel/analytics.ts diff --git a/public/app/features/logs/components/panel/LogLineDetailsDisplayedFields.tsx b/public/app/features/logs/components/panel/LogLineDetailsDisplayedFields.tsx index 50e997ad80e..e5bce32bd86 100644 --- a/public/app/features/logs/components/panel/LogLineDetailsDisplayedFields.tsx +++ b/public/app/features/logs/components/panel/LogLineDetailsDisplayedFields.tsx @@ -10,12 +10,13 @@ import { LOG_LINE_BODY_FIELD_NAME } from '../LogDetailsBody'; import { LogLineDetailsMode } from './LogLineDetails'; import { useLogListContext } from './LogListContext'; +import { reportInteractionOnce } from './analytics'; export const LogLineDetailsDisplayedFields = () => { const { displayedFields, setDisplayedFields } = useLogListContext(); const reorganizeDisplayedFields = useCallback( - (srcIndex: number, destIndex: number) => { + (srcIndex: number, destIndex: number, mode: 'drag' | 'button') => { const newDisplayedFields = [...displayedFields]; const element = displayedFields[srcIndex]; @@ -23,6 +24,11 @@ export const LogLineDetailsDisplayedFields = () => { newDisplayedFields.splice(destIndex, 0, element); setDisplayedFields?.(newDisplayedFields); + + reportInteractionOnce('logs_log_line_details_organized_fields', { + quantity: newDisplayedFields.length, + mode, + }); }, [displayedFields, setDisplayedFields] ); @@ -32,7 +38,7 @@ export const LogLineDetailsDisplayedFields = () => { if (result.destination == null) { return; } - reorganizeDisplayedFields(result.source.index, result.destination.index); + reorganizeDisplayedFields(result.source.index, result.destination.index, 'drag'); }, [reorganizeDisplayedFields] ); @@ -65,7 +71,7 @@ export const LogLineDetailsDisplayedFields = () => { interface DraggableDisplayedFieldProps { field: string; index: number; - moveField: (srcIndex: number, destIndex: number) => void; + moveField: (srcIndex: number, destIndex: number, mode: 'drag' | 'button') => void; } const DraggableDisplayedField = ({ field, index, moveField }: DraggableDisplayedFieldProps) => { @@ -99,12 +105,12 @@ const DisplayedField = ({ <> moveField(index, nextIndex)} + onClick={() => moveField(index, nextIndex, 'button')} tooltip={t('logs.log-line-details.move-displayed-field-down', 'Move down')} /> moveField(index, prevIndex)} + onClick={() => moveField(index, prevIndex, 'button')} tooltip={t('logs.log-line-details.move-displayed-field-up', 'Move up')} /> diff --git a/public/app/features/logs/components/panel/LogLineDetailsTrace.tsx b/public/app/features/logs/components/panel/LogLineDetailsTrace.tsx index a9f80b841a4..28f49069dba 100644 --- a/public/app/features/logs/components/panel/LogLineDetailsTrace.tsx +++ b/public/app/features/logs/components/panel/LogLineDetailsTrace.tsx @@ -4,7 +4,7 @@ import { isObservable, lastValueFrom } from 'rxjs'; import { DataFrame, DataQueryRequest, DataSourceApi, GrafanaTheme2, TimeRange } from '@grafana/data'; import { t } from '@grafana/i18n'; -import { getDataSourceSrv } from '@grafana/runtime'; +import { getDataSourceSrv, reportInteraction } from '@grafana/runtime'; import { Icon, Spinner, Tooltip, useStyles2 } from '@grafana/ui'; import { TraceView } from 'app/features/explore/TraceView/TraceView'; import { transformDataFrames } from 'app/features/explore/TraceView/utils/transform'; @@ -76,6 +76,14 @@ export const LogLineDetailsTrace = ({ timeRange, timeZone, traceRef }: Props) => const traceProp = useMemo(() => (dataFrames?.length ? transformDataFrames(dataFrames[0]) : undefined), [dataFrames]); + useEffect(() => { + if (dataSource && Array.isArray(dataFrames) && traceProp) { + reportInteraction('logs_log_line_details_trace_displayed'); + } else if (dataFrames === null) { + reportInteraction('logs_log_line_details_trace_display_failed'); + } + }, [dataFrames, dataSource, traceProp]); + return (
{dataSource && Array.isArray(dataFrames) && traceProp && ( diff --git a/public/app/features/logs/components/panel/LogListContext.tsx b/public/app/features/logs/components/panel/LogListContext.tsx index 5ba76f50518..7e5c4154213 100644 --- a/public/app/features/logs/components/panel/LogListContext.tsx +++ b/public/app/features/logs/components/panel/LogListContext.tsx @@ -24,7 +24,7 @@ import { store, } from '@grafana/data'; import { t } from '@grafana/i18n'; -import { config, getDataSourceSrv, reportInteraction } from '@grafana/runtime'; +import { config, getDataSourceSrv } from '@grafana/runtime'; import { PopoverContent } from '@grafana/ui'; import { checkLogsError, checkLogsSampled, downloadLogs as download, DownloadFormat } from '../../utils'; @@ -34,6 +34,7 @@ import { LogLineTimestampResolution } from './LogLine'; import { LogLineDetailsMode } from './LogLineDetails'; import { GetRowContextQueryFn, LogLineMenuCustomItem } from './LogLineMenu'; import { LogListFontSize } from './LogList'; +import { reportInteractionOnce } from './analytics'; import { LogListModel } from './processing'; import { getScrollbarWidth, LOG_LIST_CONTROLS_WIDTH, LOG_LIST_MIN_WIDTH } from './virtualization'; @@ -710,15 +711,6 @@ export function removeDetailsScrollPosition(log: LogListModel) { detailsScrollMap.delete(log.uid); } -const reportInteractionOnce = (interactionName: string, properties?: Record) => { - const key = `logs.log-list-context.events.${interactionName}`; - if (sessionStorage.getItem(key)) { - return; - } - sessionStorage.setItem(key, '1'); - reportInteraction(interactionName, properties); -}; - async function handleOpenAssistant(openAssistant: (props: OpenAssistantProps) => void, log: LogListModel) { const datasource = await getDataSourceSrv().get(log.datasourceUid); const context = []; diff --git a/public/app/features/logs/components/panel/analytics.ts b/public/app/features/logs/components/panel/analytics.ts new file mode 100644 index 00000000000..39ec415b939 --- /dev/null +++ b/public/app/features/logs/components/panel/analytics.ts @@ -0,0 +1,10 @@ +import { reportInteraction } from '@grafana/runtime'; + +export const reportInteractionOnce = (interactionName: string, properties?: Record) => { + const key = `logs.interactions.${interactionName}`; + if (sessionStorage.getItem(key)) { + return; + } + sessionStorage.setItem(key, '1'); + reportInteraction(interactionName, properties); +}; From b4ed217656f56002e0db3bd2cbd5d3eddcd842e4 Mon Sep 17 00:00:00 2001 From: owensmallwood Date: Tue, 26 Aug 2025 12:39:30 -0600 Subject: [PATCH 19/24] Unified Storage: Removes check for name ordering and adds test (#110183) removes check for name ordering. Adds test to assert expected name and rv ordering. --- pkg/storage/unified/sql/backend.go | 7 ---- .../unified/testing/storage_backend.go | 34 +++++++++++++++++++ 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/pkg/storage/unified/sql/backend.go b/pkg/storage/unified/sql/backend.go index 2560943c92b..be6789844e6 100644 --- a/pkg/storage/unified/sql/backend.go +++ b/pkg/storage/unified/sql/backend.go @@ -709,13 +709,6 @@ func (b *backend) ListModifiedSince(ctx context.Context, key resource.Namespaced continue } - if mr.Key.Name <= lastSeen { - // resource names should be sorted alphabetically. So if not, the query is not correct. - if !yield(nil, fmt.Errorf("listModifiedSince: resources are not sorted by name ASC, lastSeen: %q, current: %q", lastSeen, mr.Key.Name)) { - return - } - } - lastSeen = mr.Key.Name if !yield(mr, nil) { diff --git a/pkg/storage/unified/testing/storage_backend.go b/pkg/storage/unified/testing/storage_backend.go index 4ae283c1f38..d7f693fd991 100644 --- a/pkg/storage/unified/testing/storage_backend.go +++ b/pkg/storage/unified/testing/storage_backend.go @@ -11,6 +11,7 @@ import ( "github.com/go-jose/go-jose/v3/jwt" "github.com/google/uuid" + "github.com/grafana/grafana/pkg/infra/db" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -553,6 +554,39 @@ func runTestIntegrationBackendListModifiedSince(t *testing.T, backend resource.S } require.Equal(t, 1, counter) // only one event should be returned }) + + t.Run("will order events by resource version ascending and name descending", func(t *testing.T) { + // When we order by name ASC, sqlite orders upper case strings first - so skipping for sqlite + // For example: for this test, the actual ordering of events for sqlite is CItem, aItem, bItem + if db.IsTestDbSQLite() { + t.Skip("Skipping test for sqlite since ordering by name is different than mysql due to case sensitivity") + } + + key := resource.NamespacedResource{ + Namespace: ns, + Group: "group", + Resource: "resource", + } + + rvCreated1, _ := writeEvent(ctx, backend, "aItem", resourcepb.WatchEvent_ADDED, WithNamespace(ns)) + rvCreated2, _ := writeEvent(ctx, backend, "bItem", resourcepb.WatchEvent_ADDED, WithNamespace(ns)) + rvCreated3, _ := writeEvent(ctx, backend, "CItem", resourcepb.WatchEvent_ADDED, WithNamespace(ns)) + + latestRv, seq := backend.ListModifiedSince(ctx, key, rvCreated1-1) + require.Greater(t, latestRv, rvCreated3) + + counter := 0 + names := []string{"aItem", "bItem", "CItem"} + rvs := []int64{rvCreated1, rvCreated2, rvCreated3} + for res, err := range seq { + require.NoError(t, err) + require.Equal(t, key.Namespace, res.Key.Namespace) + require.Equal(t, names[counter], res.Key.Name) + require.Equal(t, rvs[counter], res.ResourceVersion) + counter++ + } + require.Equal(t, 3, counter) + }) } func runTestIntegrationBackendListHistory(t *testing.T, backend resource.StorageBackend, nsPrefix string) { From 99fc606e17642bae18807d4e2e35f47a6a7cd5f4 Mon Sep 17 00:00:00 2001 From: maicon Date: Tue, 26 Aug 2025 16:12:13 -0300 Subject: [PATCH 20/24] Add permission role_id action index (#110125) * Add permission role_id action index Signed-off-by: Maicon Costa * Drop permission role_id index Signed-off-by: Maicon Costa --------- Signed-off-by: Maicon Costa --- .../sqlstore/migrations/accesscontrol/migrations.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pkg/services/sqlstore/migrations/accesscontrol/migrations.go b/pkg/services/sqlstore/migrations/accesscontrol/migrations.go index 31e9530c959..2502caf51dc 100644 --- a/pkg/services/sqlstore/migrations/accesscontrol/migrations.go +++ b/pkg/services/sqlstore/migrations/accesscontrol/migrations.go @@ -210,4 +210,12 @@ func AddMigration(mg *migrator.Migrator) { Type: migrator.UniqueIndex, Cols: []string{"org_id", "user_id", "role_id"}, })) + + mg.AddMigration("add permission role_id action index", migrator.NewAddIndexMigration(permissionV1, &migrator.Index{ + Cols: []string{"role_id", "action"}, + })) + + mg.AddMigration("Remove permission role_id index", migrator.NewDropIndexMigration(permissionV1, &migrator.Index{ + Cols: []string{"role_id"}, + })) } From 41f7774ccffda5dc1d381402bb793c977ce6fe56 Mon Sep 17 00:00:00 2001 From: Jacob Valdez Date: Tue, 26 Aug 2025 14:22:05 -0500 Subject: [PATCH 21/24] Docs: Fixing broken links in data sources (#110068) Co-authored-by: Irene Rodriguez Co-authored-by: Kim Nylander <104772500+knylander-grafana@users.noreply.github.com> --- docs/sources/datasources/_index.md | 2 ++ .../configure-elasticsearch-data-source.md | 7 ++++++- .../sources/datasources/mssql/configure/index.md | 4 ++-- .../datasources/postgres/query-editor/_index.md | 16 +++++++++++++--- .../tempo/configure-tempo-data-source.md | 4 ++-- 5 files changed, 25 insertions(+), 8 deletions(-) diff --git a/docs/sources/datasources/_index.md b/docs/sources/datasources/_index.md index 8f876ff1a66..7ba5e1756a5 100644 --- a/docs/sources/datasources/_index.md +++ b/docs/sources/datasources/_index.md @@ -24,6 +24,8 @@ refs: grafana-enterprise: - pattern: /docs/grafana/ destination: /docs/grafana//introduction/grafana-enterprise/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana//introduction/grafana-enterprise/ organization-roles: - pattern: /docs/grafana/ destination: /docs/grafana//administration/roles-and-permissions/#organization-roles diff --git a/docs/sources/datasources/elasticsearch/configure-elasticsearch-data-source.md b/docs/sources/datasources/elasticsearch/configure-elasticsearch-data-source.md index feb170a5c16..4233b218136 100644 --- a/docs/sources/datasources/elasticsearch/configure-elasticsearch-data-source.md +++ b/docs/sources/datasources/elasticsearch/configure-elasticsearch-data-source.md @@ -32,6 +32,11 @@ refs: destination: /docs/grafana//panels-visualizations/query-transform-data/ - pattern: /docs/grafana-cloud/ destination: /docs/grafana-cloud/visualizations/panels-visualizations/query-transform-data/ + provisioning-data-source: + - pattern: /docs/grafana/ + destination: /docs/grafana//datasources/elasticsearch/#provision-the-data-source + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/connect-externally-hosted/data-sources/elasticsearch/#provision-the-data-source --- # Configure the Elasticsearch data source @@ -42,7 +47,7 @@ You can create a variety of queries to visualize logs or metrics stored in Elast For instructions on how to add a data source to Grafana, refer to the [administration documentation](ref:administration-documentation). Only users with the organization `administrator` role can add data sources. -Administrators can also [configure the data source via YAML](ref:provisioning-data-sources) with Grafana's provisioning system. +Administrators can also [configure the data source via YAML](ref:provisioning-data-source) with Grafana's provisioning system. ## Configuring permissions diff --git a/docs/sources/datasources/mssql/configure/index.md b/docs/sources/datasources/mssql/configure/index.md index 7fc8e5920c3..3329d585159 100644 --- a/docs/sources/datasources/mssql/configure/index.md +++ b/docs/sources/datasources/mssql/configure/index.md @@ -46,9 +46,9 @@ refs: destination: /docs/grafana//administration/data-source-management/ private-data-source-connect: - pattern: /docs/grafana/ - destination: docs/grafana-cloud/connect-externally-hosted/private-data-source-connect/ + 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/ + 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 diff --git a/docs/sources/datasources/postgres/query-editor/_index.md b/docs/sources/datasources/postgres/query-editor/_index.md index 822ef681c9a..d1c4447b4b4 100644 --- a/docs/sources/datasources/postgres/query-editor/_index.md +++ b/docs/sources/datasources/postgres/query-editor/_index.md @@ -17,22 +17,27 @@ refs: - pattern: /docs/grafana/ destination: /docs/grafana//dashboards/variables/ - pattern: /docs/grafana-cloud/ - destination: /docs/grafana//dashboards/variables/ + destination: /docs/grafana-cloud/visualizations/dashboards/variables/ add-template-variables-interval: - pattern: /docs/grafana/ destination: /docs/grafana//dashboards/variables/add-template-variables/#__interval - pattern: /docs/grafana-cloud/ - destination: /docs/grafana//dashboards/variables/add-template-variables/#__interval + destination: /docs/grafana-cloud/visualizations/dashboards/variables/add-template-variables/#__interval explore: - pattern: /docs/grafana/ destination: /docs/grafana//explore/ - pattern: /docs/grafana-cloud/ - destination: /docs/grafana//explore/ + destination: /docs/grafana-cloud/visualizations/explore/ query-transform-data: - pattern: /docs/grafana/ destination: /docs/grafana//panels-visualizations/query-transform-data/ - pattern: /docs/grafana-cloud/ destination: /docs/grafana-cloud/visualizations/panels-visualizations/query-transform-data/ + configure-standard-options-display-name: + - pattern: /docs/grafana/ + destination: /docs/grafana//panels-visualizations/configure-standard-options/#display-name + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/panels-visualizations/configure-standard-options/#display-name query-editor: - pattern: /docs/grafana/ destination: /docs/grafana//panels-visualizations/query-transform-data/#query-editors @@ -53,6 +58,11 @@ refs: destination: /docs/grafana//dashboards/variables/#templates - pattern: /docs/grafana-cloud/ destination: /docs/grafana-cloud/visualizations/dashboards/variables/#templates + annotate-visualizations: + - pattern: /docs/grafana/ + destination: /docs/grafana//dashboards/build-dashboards/annotate-visualizations/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/visualizations/dashboards/build-dashboards/annotate-visualizations/ --- # PostgreSQL query editor diff --git a/docs/sources/datasources/tempo/configure-tempo-data-source.md b/docs/sources/datasources/tempo/configure-tempo-data-source.md index 84320575ed0..335536dd793 100644 --- a/docs/sources/datasources/tempo/configure-tempo-data-source.md +++ b/docs/sources/datasources/tempo/configure-tempo-data-source.md @@ -47,8 +47,8 @@ refs: provisioning-data-sources: - pattern: /docs/grafana/ destination: /docs/grafana//administration/provisioning/#data-sources - - pattern: /docs/grafana-cloud/provision - destination: /docs/grafana/next/administration/provisioning/#data-sources + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana//administration/provisioning/#data-sources explore: - pattern: /docs/grafana/ destination: /docs/grafana//explore/ From eb6b49ca62d5b6d166ca5fd56df6836b2ff3860f Mon Sep 17 00:00:00 2001 From: Collin Fingar Date: Tue, 26 Aug 2025 16:14:07 -0400 Subject: [PATCH 22/24] Saved Queries: Rename entrypoint for dashboards (#110186) --- .../panel-edit/PanelDataPane/PanelDataQueriesTab.tsx | 2 +- public/locales/en-US/grafana.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataQueriesTab.tsx b/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataQueriesTab.tsx index 4295ddb38d4..d46b7270cb5 100644 --- a/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataQueriesTab.tsx +++ b/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataQueriesTab.tsx @@ -405,7 +405,7 @@ export function PanelDataQueriesTabRendered({ model }: SceneComponentProps - Add query from library + Add from saved queries )} diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json index 928977ce31f..2706ec22fce 100644 --- a/public/locales/en-US/grafana.json +++ b/public/locales/en-US/grafana.json @@ -6373,7 +6373,7 @@ }, "dashboards": { "panel-queries": { - "add-query-from-library": "Add query from library" + "add-from-saved-queries": "Add from saved queries" }, "settings": { "variables": { From 1c13f4a0f29c34a7fec170c8ec770b50e8ee3f1b Mon Sep 17 00:00:00 2001 From: Isabel Matwawana <76437239+imatwawana@users.noreply.github.com> Date: Tue, 26 Aug 2025 16:20:27 -0400 Subject: [PATCH 23/24] Docs: Change height unit from kg to inches (#110188) --- .../panels-visualizations/visualizations/histogram/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sources/panels-visualizations/visualizations/histogram/index.md b/docs/sources/panels-visualizations/visualizations/histogram/index.md index de10e1548f8..c96747809dc 100644 --- a/docs/sources/panels-visualizations/visualizations/histogram/index.md +++ b/docs/sources/panels-visualizations/visualizations/histogram/index.md @@ -73,7 +73,7 @@ The data is converted as follows: #### Basic numerical table -| Gender | Height (kg) | Weight (lbs) | +| Gender | Height (in) | Weight (lbs) | | ------ | ----------- | ------------ | | Male | 73.8 | 242 | | Male | 68.8 | 162 | From ec38e0bd5822f8a74704543c07f197a0f1815ef0 Mon Sep 17 00:00:00 2001 From: Paul Marbach Date: Tue, 26 Aug 2025 17:25:16 -0400 Subject: [PATCH 24/24] Table: Enable tableNextGen by default (#109832) * Table: Enable tableNextGen by default * kill off tableNextGen feature flag * i18n * i18n * i18n * remove state beta from table panel * fix migration for 0 decimals migration * add explicit auto option bacjk * match impl from previous migrations code * try changing some selectors * remove timezone test from Cypress * fix PlaylistForm unit test * fix some selectors * clean up i18n, are these gridcells somehow? * return a couple of selectors caught in the dragnet to being cell instead of gridcell * fix i18n for en-US * clean up gdevs now that wrapHeaderText has no default * update role in e2e for when it is re-enabled --- .betterer.results | 42 +- .../testdata/output/v24.table-angular.json | 2 +- .../panel-table/table_kitchen_sink.json | 27 +- .../panel-table/table_markdown.json | 3 +- .../panel-table/table_sparkline_cell.json | 21 +- .../feature-toggles/index.md | 1 - .../dashboard-live-streaming.spec.ts | 1 - .../dashboard-time-zone.spec.ts | 4 +- ...eomap-spatial-operations-transform.spec.ts | 6 - .../panels-suite/panelEdit_base.spec.ts | 6 - .../panels-suite/table-kitchenSink.spec.ts | 2 +- .../panels-suite/table-markdown.spec.ts | 3 - .../panels-suite/table-sparkline.spec.ts | 2 +- .../as-admin-user/panelDataAssertion.spec.ts | 4 +- .../as-admin-user/panelEditPage.spec.ts | 6 - .../loki-table-explore-to-dash.spec.ts | 4 +- .../visualization-suggestions.spec.ts | 6 - .../dashboard-live-streaming.spec.ts | 16 - .../dashboard-time-zone.spec.ts | 263 ------------- ...eomap-spatial-operations-transform.spec.ts | 98 ----- .../panels-suite/panelEdit_base.spec.ts | 108 ------ .../visualization-suggestions.spec.ts | 27 -- .../src/types/featureToggles.gen.ts | 4 - packages/grafana-schema/src/common/table.cue | 2 +- .../panelcfg/x/TablePanelCfg_types.gen.ts | 6 + .../Table/DataLinksActionsTooltip.tsx | 2 +- pkg/services/featuremgmt/registry.go | 7 - pkg/services/featuremgmt/toggles_gen.csv | 1 - pkg/services/featuremgmt/toggles_gen.go | 4 - pkg/services/featuremgmt/toggles_gen.json | 3 +- .../app/features/plugins/built_in_plugins.ts | 10 +- .../plugins/panel/table/PaginationEditor.tsx | 14 +- .../panel/table/TableCellOptionEditor.tsx | 70 ++-- public/app/plugins/panel/table/TablePanel.tsx | 74 ++-- .../table/cells/AutoCellOptionsEditor.tsx | 27 -- .../ColorBackgroundCellOptionsEditor.tsx | 50 +-- .../cells/MarkdownCellOptionsEditor.tsx | 0 .../cells/SparklineCellOptionsEditor.tsx | 4 +- .../cells/TextWrapOptionsEditor.tsx | 0 public/app/plugins/panel/table/module.tsx | 67 +++- public/app/plugins/panel/table/panelcfg.cue | 4 + .../app/plugins/panel/table/panelcfg.gen.ts | 6 + .../table/table-new/PaginationEditor.tsx | 19 - .../plugins/panel/table/table-new/README.md | 9 - .../table/table-new/TableCellOptionEditor.tsx | 122 ------ .../panel/table/table-new/TablePanel.tsx | 236 ------------ .../__snapshots__/migrations.test.ts.snap | 82 ---- .../cells/BarGaugeCellOptionsEditor.tsx | 53 --- .../ColorBackgroundCellOptionsEditor.tsx | 65 ---- .../cells/ImageCellOptionsEditor.tsx | 43 --- .../cells/SparklineCellOptionsEditor.tsx | 94 ----- .../table/table-new/img/icn-table-panel.svg | 1 - .../panel/table/table-new/migrations.test.ts | 364 ------------------ .../panel/table/table-new/migrations.ts | 299 -------------- .../plugins/panel/table/table-new/module.tsx | 258 ------------- .../panel/table/table-new/panelcfg.cue | 59 --- .../panel/table/table-new/panelcfg.gen.ts | 68 ---- .../plugins/panel/table/table-new/plugin.json | 25 -- .../panel/table/table-new/suggestions.ts | 38 -- public/locales/en-US/grafana.json | 61 +-- 60 files changed, 253 insertions(+), 2650 deletions(-) delete mode 100644 e2e/old-arch/dashboards-suite/dashboard-live-streaming.spec.ts delete mode 100644 e2e/old-arch/dashboards-suite/dashboard-time-zone.spec.ts delete mode 100644 e2e/old-arch/panels-suite/geomap-spatial-operations-transform.spec.ts delete mode 100644 e2e/old-arch/panels-suite/panelEdit_base.spec.ts delete mode 100644 e2e/old-arch/various-suite/visualization-suggestions.spec.ts delete mode 100644 public/app/plugins/panel/table/cells/AutoCellOptionsEditor.tsx rename public/app/plugins/panel/table/{table-new => }/cells/MarkdownCellOptionsEditor.tsx (100%) rename public/app/plugins/panel/table/{table-new => }/cells/TextWrapOptionsEditor.tsx (100%) delete mode 100644 public/app/plugins/panel/table/table-new/PaginationEditor.tsx delete mode 100644 public/app/plugins/panel/table/table-new/README.md delete mode 100644 public/app/plugins/panel/table/table-new/TableCellOptionEditor.tsx delete mode 100644 public/app/plugins/panel/table/table-new/TablePanel.tsx delete mode 100644 public/app/plugins/panel/table/table-new/__snapshots__/migrations.test.ts.snap delete mode 100644 public/app/plugins/panel/table/table-new/cells/BarGaugeCellOptionsEditor.tsx delete mode 100644 public/app/plugins/panel/table/table-new/cells/ColorBackgroundCellOptionsEditor.tsx delete mode 100644 public/app/plugins/panel/table/table-new/cells/ImageCellOptionsEditor.tsx delete mode 100644 public/app/plugins/panel/table/table-new/cells/SparklineCellOptionsEditor.tsx delete mode 100644 public/app/plugins/panel/table/table-new/img/icn-table-panel.svg delete mode 100644 public/app/plugins/panel/table/table-new/migrations.test.ts delete mode 100644 public/app/plugins/panel/table/table-new/migrations.ts delete mode 100644 public/app/plugins/panel/table/table-new/module.tsx delete mode 100644 public/app/plugins/panel/table/table-new/panelcfg.cue delete mode 100644 public/app/plugins/panel/table/table-new/panelcfg.gen.ts delete mode 100644 public/app/plugins/panel/table/table-new/plugin.json delete mode 100644 public/app/plugins/panel/table/table-new/suggestions.ts diff --git a/.betterer.results b/.betterer.results index e0aca642544..ca0b5eeff6f 100644 --- a/.betterer.results +++ b/.betterer.results @@ -3953,9 +3953,6 @@ exports[`better eslint`] = { "public/app/plugins/panel/table/TableCellOptionEditor.tsx:5381": [ [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] ], - "public/app/plugins/panel/table/cells/AutoCellOptionsEditor.tsx:5381": [ - [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] - ], "public/app/plugins/panel/table/cells/BarGaugeCellOptionsEditor.tsx:5381": [ [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"] @@ -3963,52 +3960,27 @@ exports[`better eslint`] = { "public/app/plugins/panel/table/cells/ColorBackgroundCellOptionsEditor.tsx:5381": [ [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], - [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"] + [0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "2"] ], "public/app/plugins/panel/table/cells/ImageCellOptionsEditor.tsx:5381": [ [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"] ], + "public/app/plugins/panel/table/cells/MarkdownCellOptionsEditor.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], "public/app/plugins/panel/table/cells/SparklineCellOptionsEditor.tsx:5381": [ [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] ], + "public/app/plugins/panel/table/cells/TextWrapOptionsEditor.tsx:5381": [ + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] + ], "public/app/plugins/panel/table/migrations.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"], [0, 0, 0, "Unexpected any. Specify a different type.", "2"], [0, 0, 0, "Unexpected any. Specify a different type.", "3"] ], - "public/app/plugins/panel/table/table-new/TableCellOptionEditor.tsx:5381": [ - [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] - ], - "public/app/plugins/panel/table/table-new/cells/BarGaugeCellOptionsEditor.tsx:5381": [ - [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], - [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"] - ], - "public/app/plugins/panel/table/table-new/cells/ColorBackgroundCellOptionsEditor.tsx:5381": [ - [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], - [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], - [0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "2"] - ], - "public/app/plugins/panel/table/table-new/cells/ImageCellOptionsEditor.tsx:5381": [ - [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], - [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"] - ], - "public/app/plugins/panel/table/table-new/cells/MarkdownCellOptionsEditor.tsx:5381": [ - [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] - ], - "public/app/plugins/panel/table/table-new/cells/SparklineCellOptionsEditor.tsx:5381": [ - [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] - ], - "public/app/plugins/panel/table/table-new/cells/TextWrapOptionsEditor.tsx:5381": [ - [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] - ], - "public/app/plugins/panel/table/table-new/migrations.ts:5381": [ - [0, 0, 0, "Unexpected any. Specify a different type.", "0"], - [0, 0, 0, "Unexpected any. Specify a different type.", "1"], - [0, 0, 0, "Unexpected any. Specify a different type.", "2"], - [0, 0, 0, "Unexpected any. Specify a different type.", "3"] - ], "public/app/plugins/panel/text/textPanelMigrationHandler.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], diff --git a/apps/dashboard/pkg/migration/testdata/output/v24.table-angular.json b/apps/dashboard/pkg/migration/testdata/output/v24.table-angular.json index f9ccbcd0dc4..028723f153e 100644 --- a/apps/dashboard/pkg/migration/testdata/output/v24.table-angular.json +++ b/apps/dashboard/pkg/migration/testdata/output/v24.table-angular.json @@ -1331,4 +1331,4 @@ ], "refresh": "", "schemaVersion": 41 -} \ No newline at end of file +} diff --git a/devenv/dev-dashboards/panel-table/table_kitchen_sink.json b/devenv/dev-dashboards/panel-table/table_kitchen_sink.json index 8ff35adb06d..b961173d362 100644 --- a/devenv/dev-dashboards/panel-table/table_kitchen_sink.json +++ b/devenv/dev-dashboards/panel-table/table_kitchen_sink.json @@ -37,8 +37,7 @@ "wrapText": false }, "filterable": true, - "inspect": false, - "wrapHeaderText": false + "inspect": false }, "fieldMinMax": true, "links": [], @@ -557,8 +556,7 @@ "type": "color-background" }, "filterable": true, - "inspect": true, - "wrapHeaderText": false + "inspect": true }, "fieldMinMax": true, "links": [], @@ -664,8 +662,7 @@ "type": "auto" }, "inspect": false, - "minWidth": 50, - "wrapHeaderText": false + "minWidth": 50 }, "mappings": [], "thresholds": { @@ -1012,8 +1009,7 @@ "cellOptions": { "type": "auto" }, - "inspect": false, - "wrapHeaderText": false + "inspect": false }, "fieldMinMax": true, "mappings": [], @@ -1161,8 +1157,7 @@ "cellOptions": { "type": "auto" }, - "inspect": false, - "wrapHeaderText": false + "inspect": false }, "fieldMinMax": true, "mappings": [], @@ -1309,8 +1304,7 @@ "cellOptions": { "type": "auto" }, - "inspect": false, - "wrapHeaderText": false + "inspect": false }, "fieldMinMax": true, "mappings": [], @@ -1457,8 +1451,7 @@ "cellOptions": { "type": "auto" }, - "inspect": false, - "wrapHeaderText": false + "inspect": false }, "fieldMinMax": true, "mappings": [], @@ -1605,8 +1598,7 @@ "cellOptions": { "type": "auto" }, - "inspect": false, - "wrapHeaderText": false + "inspect": false }, "fieldMinMax": true, "mappings": [], @@ -1788,8 +1780,7 @@ "cellOptions": { "type": "auto" }, - "inspect": false, - "wrapHeaderText": false + "inspect": false }, "mappings": [], "thresholds": { diff --git a/devenv/dev-dashboards/panel-table/table_markdown.json b/devenv/dev-dashboards/panel-table/table_markdown.json index 06f3d7c310c..eb4a570d75e 100644 --- a/devenv/dev-dashboards/panel-table/table_markdown.json +++ b/devenv/dev-dashboards/panel-table/table_markdown.json @@ -36,8 +36,7 @@ "cellOptions": { "type": "auto" }, - "inspect": false, - "wrapHeaderText": false + "inspect": false }, "mappings": [], "thresholds": { diff --git a/devenv/dev-dashboards/panel-table/table_sparkline_cell.json b/devenv/dev-dashboards/panel-table/table_sparkline_cell.json index d36609b1678..3337c794279 100644 --- a/devenv/dev-dashboards/panel-table/table_sparkline_cell.json +++ b/devenv/dev-dashboards/panel-table/table_sparkline_cell.json @@ -36,8 +36,7 @@ "cellOptions": { "type": "sparkline" }, - "inspect": false, - "wrapHeaderText": false + "inspect": false }, "mappings": [], "thresholds": { @@ -113,8 +112,7 @@ "drawStyle": "bars", "type": "sparkline" }, - "inspect": false, - "wrapHeaderText": false + "inspect": false }, "mappings": [], "thresholds": { @@ -191,8 +189,7 @@ "drawStyle": "points", "type": "sparkline" }, - "inspect": false, - "wrapHeaderText": false + "inspect": false }, "mappings": [], "thresholds": { @@ -269,8 +266,7 @@ "drawStyle": "line", "type": "sparkline" }, - "inspect": false, - "wrapHeaderText": false + "inspect": false }, "mappings": [], "min": 0, @@ -349,8 +345,7 @@ "drawStyle": "line", "type": "sparkline" }, - "inspect": false, - "wrapHeaderText": false + "inspect": false }, "mappings": [], "max": 100, @@ -430,8 +425,7 @@ "lineInterpolation": "linear", "type": "sparkline" }, - "inspect": false, - "wrapHeaderText": false + "inspect": false }, "mappings": [], "max": 100, @@ -509,8 +503,7 @@ "cellOptions": { "type": "auto" }, - "inspect": false, - "wrapHeaderText": false + "inspect": false }, "mappings": [], "thresholds": { diff --git a/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md b/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md index c7ec80a9662..cc9297ded83 100644 --- a/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md +++ b/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md @@ -98,7 +98,6 @@ Most [generally available](https://grafana.com/docs/release-life-cycle/#general- | `canvasPanelPanZoom` | Allow pan and zoom in canvas panel | | `regressionTransformation` | Enables regression analysis transformation | | `alertingSaveStateCompressed` | Enables the compressed protobuf-based alert state storage | -| `tableNextGen` | Allows access to the new react-data-grid based table component. | | `enableSCIM` | Enables SCIM support for user and group management | | `elasticsearchCrossClusterSearch` | Enables cross cluster search in the Elasticsearch datasource | | `alertRuleRestore` | Enables the alert rule restore feature | diff --git a/e2e-playwright/dashboards-suite/dashboard-live-streaming.spec.ts b/e2e-playwright/dashboards-suite/dashboard-live-streaming.spec.ts index c8917dfae47..07aacd5b3fb 100644 --- a/e2e-playwright/dashboards-suite/dashboard-live-streaming.spec.ts +++ b/e2e-playwright/dashboards-suite/dashboard-live-streaming.spec.ts @@ -4,7 +4,6 @@ import testDashboard from '../dashboards/DashboardLiveTest.json'; test.use({ featureToggles: { - tableNextGen: true, kubernetesDashboards: process.env.KUBERNETES_DASHBOARDS === 'true', }, }); diff --git a/e2e-playwright/dashboards-suite/dashboard-time-zone.spec.ts b/e2e-playwright/dashboards-suite/dashboard-time-zone.spec.ts index 3dc86271227..271e2f42b1f 100644 --- a/e2e-playwright/dashboards-suite/dashboard-time-zone.spec.ts +++ b/e2e-playwright/dashboards-suite/dashboard-time-zone.spec.ts @@ -51,7 +51,7 @@ test.describe( .getByGrafanaSelector(selectors.components.Panels.Panel.title(title)) .getByRole('row') .nth(1) - .getByRole('cell') + .getByRole('gridcell') .first(); const time = await timeCell.textContent(); if (time) { @@ -79,7 +79,7 @@ test.describe( .getByGrafanaSelector(selectors.components.Panels.Panel.title(title)) .getByRole('row') .nth(1) - .getByRole('cell') + .getByRole('gridcell') .first(); await expect(async () => { const inUtc = timesInUtc[title]; diff --git a/e2e-playwright/panels-suite/geomap-spatial-operations-transform.spec.ts b/e2e-playwright/panels-suite/geomap-spatial-operations-transform.spec.ts index dc625ea4adc..cc05bf71b5d 100644 --- a/e2e-playwright/panels-suite/geomap-spatial-operations-transform.spec.ts +++ b/e2e-playwright/panels-suite/geomap-spatial-operations-transform.spec.ts @@ -2,12 +2,6 @@ import { test, expect } from '@grafana/plugin-e2e'; const DASHBOARD_ID = 'P2jR04WVk'; -test.use({ - featureToggles: { - tableNextGen: true, - }, -}); - test.describe( 'Panels test: Geomap spatial operations', { diff --git a/e2e-playwright/panels-suite/panelEdit_base.spec.ts b/e2e-playwright/panels-suite/panelEdit_base.spec.ts index da98e8089f5..9e12c3b7860 100644 --- a/e2e-playwright/panels-suite/panelEdit_base.spec.ts +++ b/e2e-playwright/panels-suite/panelEdit_base.spec.ts @@ -2,12 +2,6 @@ import { test, expect } from '@grafana/plugin-e2e'; const PANEL_UNDER_TEST = 'Lines 500 data points'; -test.use({ - featureToggles: { - tableNextGen: true, - }, -}); - test.describe( 'Panels test: Panel edit base', { diff --git a/e2e-playwright/panels-suite/table-kitchenSink.spec.ts b/e2e-playwright/panels-suite/table-kitchenSink.spec.ts index fc24bfd6832..5765ada1e18 100644 --- a/e2e-playwright/panels-suite/table-kitchenSink.spec.ts +++ b/e2e-playwright/panels-suite/table-kitchenSink.spec.ts @@ -4,7 +4,7 @@ import { test, expect, E2ESelectorGroups } from '@grafana/plugin-e2e'; const DASHBOARD_UID = 'dcb9f5e9-8066-4397-889e-864b99555dbb'; -test.use({ viewport: { width: 2000, height: 1080 }, featureToggles: { tableNextGen: true } }); +test.use({ viewport: { width: 2000, height: 1080 } }); // helper utils const waitForTableLoad = async (loc: Page | Locator) => { diff --git a/e2e-playwright/panels-suite/table-markdown.spec.ts b/e2e-playwright/panels-suite/table-markdown.spec.ts index b2e127fc92c..68068271fe6 100644 --- a/e2e-playwright/panels-suite/table-markdown.spec.ts +++ b/e2e-playwright/panels-suite/table-markdown.spec.ts @@ -2,9 +2,6 @@ import { test, expect } from '@grafana/plugin-e2e'; test.use({ viewport: { width: 1280, height: 1080 }, - featureToggles: { - tableNextGen: true, - }, }); test.describe( diff --git a/e2e-playwright/panels-suite/table-sparkline.spec.ts b/e2e-playwright/panels-suite/table-sparkline.spec.ts index d2ac814e2df..fa2ed681f1d 100644 --- a/e2e-playwright/panels-suite/table-sparkline.spec.ts +++ b/e2e-playwright/panels-suite/table-sparkline.spec.ts @@ -1,6 +1,6 @@ import { test, expect } from '@grafana/plugin-e2e'; -test.use({ viewport: { width: 1280, height: 1080 }, featureToggles: { tableNextGen: true } }); +test.use({ viewport: { width: 1280, height: 1080 } }); test.describe('Panels test: Table - Sparkline', { tag: ['@panels', '@table'] }, () => { test('Tests sparkline tables are successfully rendered', async ({ gotoDashboardPage, selectors, page }) => { diff --git a/e2e-playwright/plugin-e2e/plugin-e2e-api-tests/as-admin-user/panelDataAssertion.spec.ts b/e2e-playwright/plugin-e2e/plugin-e2e-api-tests/as-admin-user/panelDataAssertion.spec.ts index 0133a3e3712..e1fef705a88 100644 --- a/e2e-playwright/plugin-e2e/plugin-e2e-api-tests/as-admin-user/panelDataAssertion.spec.ts +++ b/e2e-playwright/plugin-e2e/plugin-e2e-api-tests/as-admin-user/panelDataAssertion.spec.ts @@ -38,7 +38,7 @@ test.describe( formatExpectError('Could not locate header elements in table panel') ).toContainText(['col1', 'col2']); await expect( - panelEditPage.panel.data, + panelEditPage.panel.locator.getByRole('gridcell'), formatExpectError('Could not locate headers in table panel') ).toContainText(['val1', 'val2', 'val3', 'val4']); }); @@ -58,7 +58,7 @@ test.describe( formatExpectError('Could not locate header elements in table panel') ).toContainText(['col1', 'col2']); await expect( - panelEditPage.panel.data, + panelEditPage.panel.locator.getByRole('gridcell'), formatExpectError('Could not locate data elements in table panel') ).toContainText(['val1', 'val2', 'val3', 'val4']); }); diff --git a/e2e-playwright/plugin-e2e/plugin-e2e-api-tests/as-admin-user/panelEditPage.spec.ts b/e2e-playwright/plugin-e2e/plugin-e2e-api-tests/as-admin-user/panelEditPage.spec.ts index 577a758d97e..6ea57641a7f 100644 --- a/e2e-playwright/plugin-e2e/plugin-e2e-api-tests/as-admin-user/panelEditPage.spec.ts +++ b/e2e-playwright/plugin-e2e/plugin-e2e-api-tests/as-admin-user/panelEditPage.spec.ts @@ -11,12 +11,6 @@ const STANDARD_OTIONS_CATEGORY = 'Standard options'; const DISPLAY_NAME_LABEL = 'Display name'; const REACT_TABLE_DASHBOARD = { uid: 'U_bZIMRMk' }; -test.use({ - featureToggles: { - tableNextGen: true, - }, -}); - test.describe( 'plugin-e2e-api-tests admin', { diff --git a/e2e-playwright/various-suite/loki-table-explore-to-dash.spec.ts b/e2e-playwright/various-suite/loki-table-explore-to-dash.spec.ts index 8495d43c07c..4b2e3876154 100644 --- a/e2e-playwright/various-suite/loki-table-explore-to-dash.spec.ts +++ b/e2e-playwright/various-suite/loki-table-explore-to-dash.spec.ts @@ -190,7 +190,7 @@ test.describe( await expect(panel).toBeVisible(); // Check the table cells in the panel - const panelCells = panel.locator('[role="table"] [role="cell"]'); + const panelCells = panel.locator('[role="gridcell"]'); // Should have 3 columns await expect(panelCells).toHaveCount(3); @@ -198,7 +198,7 @@ test.describe( await expect(page.getByText('"wave":-0.5877852522916832')).toBeVisible(); // Column has correct value of "targetLabelValue" - await expect(panel.locator('[role="table"] [role="cell"]').filter({ hasText: 'targetLabelValue' })).toBeVisible(); + await expect(panel.locator('[role="gridcell"]').filter({ hasText: 'targetLabelValue' })).toBeVisible(); }); } ); diff --git a/e2e-playwright/various-suite/visualization-suggestions.spec.ts b/e2e-playwright/various-suite/visualization-suggestions.spec.ts index 38c6f30d298..57ccfccc475 100644 --- a/e2e-playwright/various-suite/visualization-suggestions.spec.ts +++ b/e2e-playwright/various-suite/visualization-suggestions.spec.ts @@ -1,11 +1,5 @@ import { test, expect } from '@grafana/plugin-e2e'; -test.use({ - featureToggles: { - tableNextGen: true, - }, -}); - test.describe( 'Visualization suggestions', { diff --git a/e2e/old-arch/dashboards-suite/dashboard-live-streaming.spec.ts b/e2e/old-arch/dashboards-suite/dashboard-live-streaming.spec.ts deleted file mode 100644 index 5798b669f6f..00000000000 --- a/e2e/old-arch/dashboards-suite/dashboard-live-streaming.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -import testDashboard from '../dashboards/DashboardLiveTest.json'; -import { e2e } from '../utils'; - -describe('Dashboard Live streaming support', () => { - beforeEach(() => { - e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD')); - e2e.flows.importDashboard(testDashboard, 1000); - }); - - it('Should receive streaming data', () => { - e2e.flows.openDashboard({ uid: 'live-e2e-test', queryParams: { '__feature.tableNextGen': false } }); - cy.wait(1000); - e2e.components.Panels.Panel.title('Live').should('exist'); - e2e.components.Panels.Visualization.Table.body().find('[role="row"]').should('have.length.at.least', 5); - }); -}); diff --git a/e2e/old-arch/dashboards-suite/dashboard-time-zone.spec.ts b/e2e/old-arch/dashboards-suite/dashboard-time-zone.spec.ts deleted file mode 100644 index ce6d9950234..00000000000 --- a/e2e/old-arch/dashboards-suite/dashboard-time-zone.spec.ts +++ /dev/null @@ -1,263 +0,0 @@ -import { - addDays, - addHours, - differenceInCalendarDays, - differenceInMinutes, - format, - isBefore, - parseISO, - toDate, -} from 'date-fns'; - -import { e2e } from '../utils'; - -describe('Dashboard time zone support', () => { - beforeEach(() => { - e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD')); - }); - - it.skip('Tests dashboard time zone scenarios', () => { - e2e.flows.openDashboard({ uid: '5SdHCasdf' }); - - const fromTimeZone = 'UTC'; - const toTimeZone = 'America/Chicago'; - const offset = offsetBetweenTimeZones(toTimeZone, fromTimeZone); - - const panelsToCheck = [ - 'Random walk series', - 'Millisecond res x-axis and tooltip', - '2 yaxis and axis labels', - 'Stacking value ontop of nulls', - 'Null between points', - 'Legend Table No Scroll Visible', - ]; - - const timesInUtc: Record = {}; - - for (const title of panelsToCheck) { - e2e.components.Panels.Panel.title(title) - .should('be.visible') - .within(() => { - e2e.components.Panels.Visualization.Graph.xAxis.labels().should('be.visible'); - e2e.components.Panels.Visualization.Graph.xAxis - .labels() - .last() - .should((element) => { - timesInUtc[title] = element.text(); - }); - }); - } - - e2e.components.PageToolbar.item('Dashboard settings').click(); - - e2e.components.TimeZonePicker.containerV2() - .should('be.visible') - .within(() => { - e2e.components.Select.singleValue().should('have.text', 'Coordinated Universal Time'); - e2e.components.Select.input().should('be.visible').click(); - }); - - e2e.components.Select.option().should('be.visible').contains(toTimeZone).click(); - - // click to go back to the dashboard. - e2e.pages.Dashboard.Settings.Actions.close().click(); - e2e.components.RefreshPicker.runButtonV2().should('be.visible').click(); - - for (const title of panelsToCheck) { - e2e.components.Panels.Panel.title(title) - .should('be.visible') - .within(() => { - e2e.components.Panels.Visualization.Graph.xAxis.labels().should('be.visible'); - e2e.components.Panels.Visualization.Graph.xAxis - .labels() - .last() - .should((element) => { - const inUtc = timesInUtc[title]; - const inTz = element.text(); - const isCorrect = isTimeCorrect(inUtc, inTz, offset); - expect(isCorrect).to.be.equal(true); - }); - }); - } - }); - - it('Tests relative timezone support and overrides', () => { - // Open dashboard - e2e.flows.openDashboard({ - uid: 'd41dbaa2-a39e-4536-ab2b-caca52f1a9c8', - }); - - cy.intercept('/api/ds/query*').as('dataQuery'); - - // Switch to Browser timezone - e2e.flows.setTimeRange({ - from: 'now-6h', - to: 'now', - zone: 'Browser', - }); - // Need to wait for 2 calls as there's 2 panels - cy.wait(['@dataQuery', '@dataQuery']); - - e2e.components.Panels.Panel.title('Panel with relative time override') - .should('be.visible') - .within(() => { - cy.contains('[role="row"]', '00:00:00').should('be.visible'); - }); - - // Today so far, still in Browser timezone - e2e.flows.setTimeRange({ - from: 'now/d', - to: 'now', - }); - // Need to wait for 2 calls as there's 2 panels - cy.wait(['@dataQuery', '@dataQuery']); - - e2e.components.Panels.Panel.title('Panel with relative time override') - .should('be.visible') - .within(() => { - cy.contains('[role="row"]', '00:00:00').should('be.visible'); - }); - - e2e.components.Panels.Panel.title('Panel in timezone') - .should('be.visible') - .within(() => { - cy.contains('[role="row"]', '00:00:00').should('be.visible'); - }); - - // Test UTC timezone - e2e.flows.setTimeRange({ - from: 'now-6h', - to: 'now', - zone: 'Coordinated Universal Time', - }); - // Need to wait for 2 calls as there's 2 panels - cy.wait(['@dataQuery', '@dataQuery']); - - e2e.components.Panels.Panel.title('Panel with relative time override') - .should('be.visible') - .within(() => { - cy.contains('[role="row"]', '00:00:00').should('be.visible'); - }); - - // Today so far, still in UTC timezone - e2e.flows.setTimeRange({ - from: 'now/d', - to: 'now', - }); - // Need to wait for 2 calls as there's 2 panels - cy.wait(['@dataQuery', '@dataQuery']); - - e2e.components.Panels.Panel.title('Panel with relative time override') - .should('be.visible') - .within(() => { - cy.contains('[role="row"]', '00:00:00').should('be.visible'); - }); - - e2e.components.Panels.Panel.title('Panel in timezone') - .should('be.visible') - .within(() => { - cy.contains('[role="row"]', '00:00:00').should('be.visible'); - }); - - // Test Tokyo timezone - e2e.flows.setTimeRange({ - from: 'now-6h', - to: 'now', - zone: 'Asia/Tokyo', - }); - // Need to wait for 2 calls as there's 2 panels - cy.wait(['@dataQuery', '@dataQuery']); - - e2e.components.Panels.Panel.title('Panel with relative time override') - .should('be.visible') - .within(() => { - cy.contains('[role="row"]', '00:00:00').should('be.visible'); - }); - - // Today so far, still in Tokyo timezone - e2e.flows.setTimeRange({ - from: 'now/d', - to: 'now', - }); - // Need to wait for 2 calls as there's 2 panels - cy.wait(['@dataQuery', '@dataQuery']); - - e2e.components.Panels.Panel.title('Panel with relative time override') - .should('be.visible') - .within(() => { - cy.contains('[role="row"]', '00:00:00').should('be.visible'); - }); - - e2e.components.Panels.Panel.title('Panel in timezone') - .should('be.visible') - .within(() => { - cy.contains('[role="row"]', '00:00:00').should('be.visible'); - }); - - // Test LA timezone - e2e.flows.setTimeRange({ - from: 'now-6h', - to: 'now', - zone: 'America/Los Angeles', - }); - // Need to wait for 2 calls as there's 2 panels - cy.wait(['@dataQuery', '@dataQuery']); - - e2e.components.Panels.Panel.title('Panel with relative time override') - .should('be.visible') - .within(() => { - cy.contains('[role="row"]', '00:00:00').should('be.visible'); - }); - - // Today so far, still in LA timezone - e2e.flows.setTimeRange({ - from: 'now/d', - to: 'now', - }); - // Need to wait for 2 calls as there's 2 panels - cy.wait(['@dataQuery', '@dataQuery']); - - e2e.components.Panels.Panel.title('Panel with relative time override') - .should('be.visible') - .within(() => { - cy.contains('[role="row"]', '00:00:00').should('be.visible'); - }); - - e2e.components.Panels.Panel.title('Panel in timezone') - .should('be.visible') - .within(() => { - cy.contains('[role="row"]', '00:00:00').should('be.visible'); - }); - }); -}); - -const isTimeCorrect = (inUtc: string, inTz: string, offset: number): boolean => { - if (inUtc === inTz) { - // we need to catch issues when timezone isn't changed for some reason like https://github.com/grafana/grafana/issues/35504 - return false; - } - - const reference = format(new Date(), 'yyyy-LL-dd'); - - const utcDate = toDate(parseISO(`${reference} ${inUtc}`)); - const utcDateWithOffset = addHours(toDate(parseISO(`${reference} ${inUtc}`)), offset); - const dayDifference = differenceInCalendarDays(utcDate, utcDateWithOffset); // if the utcDate +/- offset is the day before/after then we need to adjust reference - const dayOffset = isBefore(utcDateWithOffset, utcDate) ? dayDifference * -1 : dayDifference; - const tzDate = addDays(toDate(parseISO(`${reference} ${inTz}`)), dayOffset); // adjust tzDate with any dayOffset - const diff = Math.abs(differenceInMinutes(utcDate, tzDate)); // use Math.abs if tzDate is in future - - return diff <= Math.abs(offset * 60); -}; - -const offsetBetweenTimeZones = (timeZone1: string, timeZone2: string, when: Date = new Date()): number => { - const t1 = convertDateToAnotherTimeZone(when, timeZone1); - const t2 = convertDateToAnotherTimeZone(when, timeZone2); - return (t1.getTime() - t2.getTime()) / (1000 * 60 * 60); -}; - -const convertDateToAnotherTimeZone = (date: Date, timeZone: string): Date => { - const dateString = date.toLocaleString('en-US', { - timeZone: timeZone, - }); - return new Date(dateString); -}; diff --git a/e2e/old-arch/panels-suite/geomap-spatial-operations-transform.spec.ts b/e2e/old-arch/panels-suite/geomap-spatial-operations-transform.spec.ts deleted file mode 100644 index 7c8d3d4f197..00000000000 --- a/e2e/old-arch/panels-suite/geomap-spatial-operations-transform.spec.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { e2e } from '../utils'; - -const DASHBOARD_ID = 'P2jR04WVk'; - -describe.skip('Geomap spatial operations', () => { - beforeEach(() => { - e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD')); - }); - - it.skip('Tests location auto option', () => { - e2e.flows.openDashboard({ uid: DASHBOARD_ID, queryParams: { '__feature.tableNextGen': false, editPanel: 1 } }); - e2e.components.Tab.title('Transform data').should('be.visible').click(); - e2e.components.Transforms.addTransformationButton().scrollIntoView().should('be.visible').click(); - - e2e.components.TransformTab.newTransform('Spatial operations').scrollIntoView().should('be.visible').click(); - e2e.components.Transforms.SpatialOperations.actionLabel().type('Prepare spatial field{enter}'); - e2e.components.Transforms.SpatialOperations.locationLabel().should('be.visible'); - e2e.components.Transforms.SpatialOperations.locationLabel().type('Auto{enter}'); - - e2e.components.PanelEditor.toggleTableView().click({ force: true }); - e2e.components.Panels.Visualization.Table.header() - .should('be.visible') - .within(() => { - cy.contains('Point').should('be.visible'); - }); - }); - - it('Tests location coords option', () => { - e2e.flows.openDashboard({ uid: DASHBOARD_ID, queryParams: { '__feature.tableNextGen': false, editPanel: 1 } }); - e2e.components.Tab.title('Transform data').should('be.visible').click(); - e2e.components.Transforms.addTransformationButton().scrollIntoView().should('be.visible').click(); - - e2e.components.TransformTab.newTransform('Spatial operations').scrollIntoView().should('be.visible').click(); - e2e.components.Transforms.SpatialOperations.actionLabel().type('Prepare spatial field{enter}'); - e2e.components.Transforms.SpatialOperations.locationLabel().should('be.visible'); - e2e.components.Transforms.SpatialOperations.locationLabel().type('Coords{enter}'); - - e2e.components.Transforms.SpatialOperations.location.coords.latitudeFieldLabel().should('be.visible'); - e2e.components.Transforms.SpatialOperations.location.coords.latitudeFieldLabel().type('Lat{enter}'); - - e2e.components.Transforms.SpatialOperations.location.coords.longitudeFieldLabel().should('be.visible'); - e2e.components.Transforms.SpatialOperations.location.coords.longitudeFieldLabel().type('Lng{enter}'); - - e2e.components.PanelEditor.toggleTableView().click({ force: true }); - e2e.components.Panels.Visualization.Table.header() - .should('be.visible') - .within(() => { - cy.contains('Point').should('be.visible'); - }); - }); - - it('Tests geoshash field column appears in table view', () => { - e2e.flows.openDashboard({ uid: DASHBOARD_ID, queryParams: { '__feature.tableNextGen': false, editPanel: 1 } }); - e2e.components.Tab.title('Transform data').should('be.visible').click(); - e2e.components.Transforms.addTransformationButton().scrollIntoView().should('be.visible').click(); - - e2e.components.TransformTab.newTransform('Spatial operations').scrollIntoView().should('be.visible').click(); - e2e.components.Transforms.SpatialOperations.actionLabel().type('Prepare spatial field{enter}'); - e2e.components.Transforms.SpatialOperations.locationLabel().should('be.visible'); - e2e.components.Transforms.SpatialOperations.locationLabel().type('Geohash{enter}'); - - e2e.components.Transforms.SpatialOperations.location.geohash - .geohashFieldLabel() - .should('be.visible') - .type('State{enter}'); - - e2e.components.PanelEditor.toggleTableView().click({ force: true }); - e2e.components.Panels.Visualization.Table.header() - .should('be.visible') - .within(() => { - cy.contains('State 1').should('be.visible'); - }); - }); - - it('Tests location lookup option', () => { - e2e.flows.openDashboard({ uid: DASHBOARD_ID, queryParams: { '__feature.tableNextGen': false, editPanel: 1 } }); - e2e.components.Tab.title('Transform data').should('be.visible').click(); - e2e.components.Transforms.addTransformationButton().scrollIntoView().should('be.visible').click(); - - e2e.components.TransformTab.newTransform('Spatial operations').scrollIntoView().should('be.visible').click(); - e2e.components.Transforms.SpatialOperations.actionLabel().type('Prepare spatial field{enter}'); - e2e.components.Transforms.SpatialOperations.locationLabel().should('be.visible'); - e2e.components.Transforms.SpatialOperations.locationLabel().type('Lookup{enter}'); - - e2e.components.Transforms.SpatialOperations.location.lookup.lookupFieldLabel().should('be.visible'); - e2e.components.Transforms.SpatialOperations.location.lookup.lookupFieldLabel().type('State{enter}'); - - e2e.components.Transforms.SpatialOperations.location.lookup.gazetteerFieldLabel().should('be.visible'); - e2e.components.Transforms.SpatialOperations.location.lookup.gazetteerFieldLabel().type('USA States{enter}'); - - e2e.components.PanelEditor.toggleTableView().click({ force: true }); - e2e.components.Panels.Visualization.Table.header() - .should('be.visible') - .within(() => { - cy.contains('Geometry').should('be.visible'); - }); - }); -}); diff --git a/e2e/old-arch/panels-suite/panelEdit_base.spec.ts b/e2e/old-arch/panels-suite/panelEdit_base.spec.ts deleted file mode 100644 index 8691382f90f..00000000000 --- a/e2e/old-arch/panels-suite/panelEdit_base.spec.ts +++ /dev/null @@ -1,108 +0,0 @@ -import { e2e } from '../utils'; - -const PANEL_UNDER_TEST = 'Lines 500 data points'; - -describe('Panel edit tests', () => { - beforeEach(() => { - e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD')); - }); - - it('Tests various Panel edit scenarios', () => { - cy.intercept({ - pathname: '/api/ds/query', - }).as('query'); - e2e.flows.openDashboard({ uid: 'TkZXxlNG3', queryParams: { '__feature.tableNextGen': false } }); - cy.wait('@query'); - - e2e.flows.openPanelMenuItem(e2e.flows.PanelMenuItems.Edit, PANEL_UNDER_TEST); - - // New panel editor opens when navigating from Panel menu - e2e.components.PanelEditor.General.content().should('be.visible'); - - // Queries tab is rendered and open by default - e2e.components.PanelEditor.DataPane.content() - .should('be.visible') - .within(() => { - e2e.components.Tab.title('Query').should('be.visible'); - // data should be the active tab - e2e.components.Tab.active().within((li: JQuery) => { - expect(li.text()).equals('Query1'); // there's already a query so therefore Query + 1 - }); - e2e.components.QueryTab.content().should('be.visible'); - e2e.components.TransformTab.content().should('not.exist'); - e2e.components.AlertTab.content().should('not.exist'); - e2e.components.PanelAlertTabContent.content().should('not.exist'); - - // Bottom pane tabs - // Can change to Transform tab - e2e.components.Tab.title('Transform data').should('be.visible').click(); - e2e.components.Tab.active().within((li: JQuery) => { - expect(li.text()).equals('Transform data0'); // there's no transform so therefore Transform + 0 - }); - e2e.components.Transforms.addTransformationButton().scrollIntoView().should('be.visible'); - e2e.components.QueryTab.content().should('not.exist'); - e2e.components.AlertTab.content().should('not.exist'); - e2e.components.PanelAlertTabContent.content().should('not.exist'); - - // Can change to Alerts tab (graph panel is the default vis so the alerts tab should be rendered) - e2e.components.Tab.title('Alert').should('be.visible').click(); - e2e.components.Tab.active().should('have.text', 'Alert0'); // there's no alert so therefore Alert + 0 - - // Needs to be disabled until Grafana EE turns unified alerting on by default - // e2e.components.AlertTab.content().should('not.exist'); - - e2e.components.QueryTab.content().should('not.exist'); - e2e.components.TransformTab.content().should('not.exist'); - - // Needs to be disabled until Grafana EE turns unified alerting on by default - // e2e.components.PanelAlertTabContent.content().should('exist'); - // e2e.components.PanelAlertTabContent.content().should('be.visible'); - - e2e.components.Tab.title('Query').should('be.visible').click(); - }); - - // Panel sidebar is rendered open by default - e2e.components.PanelEditor.OptionsPane.content().should('be.visible'); - - // close options pane - e2e.components.PanelEditor.toggleVizOptions().click(); - e2e.components.PanelEditor.OptionsPane.content().should('not.exist'); - - // open options pane - e2e.components.PanelEditor.toggleVizOptions().should('be.visible').click(); - e2e.components.PanelEditor.OptionsPane.content().should('be.visible'); - - // Check that Time series is chosen - e2e.components.PanelEditor.toggleVizPicker().click(); - e2e.components.PluginVisualization.item('Time series').should('be.visible'); - e2e.components.PluginVisualization.current().should((e) => expect(e).to.contain('Time series')); - - // Check that table view works - e2e.components.Panels.Panel.loadingBar().should('not.exist'); - e2e.components.PanelEditor.toggleTableView().click({ force: true }); - e2e.components.Panels.Visualization.Table.header() - .should('be.visible') - .within(() => { - cy.contains('A-series').should('be.visible'); - }); - - // Change to Text panel - e2e.components.PluginVisualization.item('Text').scrollIntoView().should('be.visible').click(); - e2e.components.PanelEditor.toggleVizPicker().should((e) => expect(e).to.contain('Text')); - - // Data pane should not be rendered - e2e.components.PanelEditor.DataPane.content().should('not.exist'); - - // Change to Table panel - e2e.components.PanelEditor.toggleVizPicker().click(); - e2e.components.PluginVisualization.item('Table').scrollIntoView().should('be.visible').click(); - e2e.components.PanelEditor.toggleVizPicker().should((e) => expect(e).to.contain('Table')); - - // Data pane should be rendered - e2e.components.PanelEditor.DataPane.content().should('be.visible'); - - // Field & Overrides tabs (need to switch to React based vis, i.e. Table) - e2e.components.PanelEditor.OptionsPane.fieldLabel('Table Show table header').should('be.visible'); - e2e.components.PanelEditor.OptionsPane.fieldLabel('Table Column width').should('be.visible'); - }); -}); diff --git a/e2e/old-arch/various-suite/visualization-suggestions.spec.ts b/e2e/old-arch/various-suite/visualization-suggestions.spec.ts deleted file mode 100644 index c5dfb03ef93..00000000000 --- a/e2e/old-arch/various-suite/visualization-suggestions.spec.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { e2e } from '../utils'; - -describe('Visualization suggestions', () => { - beforeEach(() => { - e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD')); - }); - - it('Should be shown and clickable', () => { - e2e.flows.openDashboard({ uid: 'aBXrJ0R7z', queryParams: { '__feature.tableNextGen': false, editPanel: 9 } }); - - // Try visualization suggestions - e2e.components.PanelEditor.toggleVizPicker().click(); - e2e.components.RadioButton.container().filter(':contains("Suggestions")').click(); - - // Verify we see suggestions - e2e.components.VisualizationPreview.card('Line chart').should('be.visible'); - - // Verify search works - cy.get('[placeholder="Search for..."]').type('Table'); - // Should no longer see line chart - e2e.components.VisualizationPreview.card('Line chart').should('not.exist'); - - // Select a visualisation - e2e.components.VisualizationPreview.card('Table').click(); - e2e.components.Panels.Visualization.Table.header().should('be.visible'); - }); -}); diff --git a/packages/grafana-data/src/types/featureToggles.gen.ts b/packages/grafana-data/src/types/featureToggles.gen.ts index 3f3ccc676d7..57074b39883 100644 --- a/packages/grafana-data/src/types/featureToggles.gen.ts +++ b/packages/grafana-data/src/types/featureToggles.gen.ts @@ -616,10 +616,6 @@ export interface FeatureToggles { */ newFiltersUI?: boolean; /** - * Allows access to the new react-data-grid based table component. - */ - tableNextGen?: boolean; - /** * Uses Prometheus rules as the primary source of truth for ruler-enabled data sources */ alertingPrometheusRulesPrimary?: boolean; diff --git a/packages/grafana-schema/src/common/table.cue b/packages/grafana-schema/src/common/table.cue index 27229571a19..0d2b369329b 100644 --- a/packages/grafana-schema/src/common/table.cue +++ b/packages/grafana-schema/src/common/table.cue @@ -119,7 +119,7 @@ TableCellTooltipOptions: { // The name of the field to get the tooltip content from field: string // placement of the tooltip - placement?: TableCellTooltipPlacement & (*"auto" | _) + placement?: TableCellTooltipPlacement } // Field options for each field within a table (e.g 10, "The String", 64.20, etc.) diff --git a/packages/grafana-schema/src/raw/composable/table/panelcfg/x/TablePanelCfg_types.gen.ts b/packages/grafana-schema/src/raw/composable/table/panelcfg/x/TablePanelCfg_types.gen.ts index fe36790fe55..1aa6ed61a7e 100644 --- a/packages/grafana-schema/src/raw/composable/table/panelcfg/x/TablePanelCfg_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/table/panelcfg/x/TablePanelCfg_types.gen.ts @@ -25,6 +25,12 @@ export interface Options { * Represents the index of the selected frame */ frameIndex: number; + /** + * Defines the number of columns to freeze on the left side of the table + */ + frozenColumns?: { + left?: number; + }; /** * Controls whether the panel should show the header */ diff --git a/packages/grafana-ui/src/components/Table/DataLinksActionsTooltip.tsx b/packages/grafana-ui/src/components/Table/DataLinksActionsTooltip.tsx index d0c9a479042..df29e9b44ed 100644 --- a/packages/grafana-ui/src/components/Table/DataLinksActionsTooltip.tsx +++ b/packages/grafana-ui/src/components/Table/DataLinksActionsTooltip.tsx @@ -82,7 +82,7 @@ export const DataLinksActionsTooltip = ({ links, actions, value, coords, onToolt return ( <> - {/* TODO: we can remove `value` from this component when tableNextGen is fully rolled out */} + {/* TODO: we can remove `value` from this component when TableRT is fully deprecated */} {value}
await import(/* webpackChunkName: "graphitePlugin" */ 'app/plugins/datasource/graphite/module'); const cloudwatchPlugin = async () => @@ -56,13 +54,7 @@ const stateTimelinePanel = async () => await import(/* webpackChunkName: "stateTimelinePanel" */ 'app/plugins/panel/state-timeline/module'); const statusHistoryPanel = async () => await import(/* webpackChunkName: "statusHistoryPanel" */ 'app/plugins/panel/status-history/module'); -const tablePanel = async () => { - if (config.featureToggles.tableNextGen) { - return await import(/* webpackChunkName: "tableNewPanel" */ 'app/plugins/panel/table/table-new/module'); - } else { - return await import(/* webpackChunkName: "tablePanel" */ 'app/plugins/panel/table/module'); - } -}; +const tablePanel = async () => await import(/* webpackChunkName: "tablePanel" */ 'app/plugins/panel/table/module'); const textPanel = async () => await import(/* webpackChunkName: "textPanel" */ 'app/plugins/panel/text/module'); const timeseriesPanel = async () => await import(/* webpackChunkName: "timeseriesPanel" */ 'app/plugins/panel/timeseries/module'); diff --git a/public/app/plugins/panel/table/PaginationEditor.tsx b/public/app/plugins/panel/table/PaginationEditor.tsx index 0bfa20b565b..ccd9451ee3f 100644 --- a/public/app/plugins/panel/table/PaginationEditor.tsx +++ b/public/app/plugins/panel/table/PaginationEditor.tsx @@ -1,15 +1,19 @@ import * as React from 'react'; import { StandardEditorProps } from '@grafana/data'; +import { selectors } from '@grafana/e2e-selectors'; import { Switch } from '@grafana/ui'; -export function PaginationEditor({ onChange, value, context }: StandardEditorProps) { +export function PaginationEditor({ onChange, value }: StandardEditorProps) { const changeValue = (event: React.FormEvent | undefined) => { - if (event?.currentTarget.checked) { - context.options.footer.show = false; - } onChange(event?.currentTarget.checked); }; - return ; + return ( + + ); } diff --git a/public/app/plugins/panel/table/TableCellOptionEditor.tsx b/public/app/plugins/panel/table/TableCellOptionEditor.tsx index c0fd5e0e5ea..b70723695cb 100644 --- a/public/app/plugins/panel/table/TableCellOptionEditor.tsx +++ b/public/app/plugins/panel/table/TableCellOptionEditor.tsx @@ -2,15 +2,17 @@ import { css } from '@emotion/css'; import { merge } from 'lodash'; import { useState } from 'react'; -import { GrafanaTheme2, SelectableValue } from '@grafana/data'; -import { TableCellOptions } from '@grafana/schema'; -import { Field, Select, TableCellDisplayMode, useStyles2 } from '@grafana/ui'; +import { GrafanaTheme2 } from '@grafana/data'; +import { t } from '@grafana/i18n'; +import { TableCellOptions, TableWrapTextOptions } from '@grafana/schema'; +import { Combobox, ComboboxOption, Field, TableCellDisplayMode, useStyles2 } from '@grafana/ui'; -import { AutoCellOptionsEditor } from './cells/AutoCellOptionsEditor'; import { BarGaugeCellOptionsEditor } from './cells/BarGaugeCellOptionsEditor'; import { ColorBackgroundCellOptionsEditor } from './cells/ColorBackgroundCellOptionsEditor'; import { ImageCellOptionsEditor } from './cells/ImageCellOptionsEditor'; +import { MarkdownCellOptionsEditor } from './cells/MarkdownCellOptionsEditor'; import { SparklineCellOptionsEditor } from './cells/SparklineCellOptionsEditor'; +import { TextWrapOptionsEditor } from './cells/TextWrapOptionsEditor'; // The props that any cell type editor are expected // to handle. In this case the generic type should @@ -25,18 +27,48 @@ interface Props { onChange: (v: TableCellOptions) => void; } +const TEXT_WRAP_CELL_TYPES = new Set([ + TableCellDisplayMode.Auto, + TableCellDisplayMode.Sparkline, + TableCellDisplayMode.ColorText, + TableCellDisplayMode.ColorBackground, + TableCellDisplayMode.DataLinks, + TableCellDisplayMode.Pill, +]); + +function isTextWrapCellType(value: TableCellOptions): value is TableCellOptions & TableWrapTextOptions { + return TEXT_WRAP_CELL_TYPES.has(value.type); +} + export const TableCellOptionEditor = ({ value, onChange }: Props) => { const cellType = value.type; const styles = useStyles2(getStyles); - const currentMode = cellDisplayModeOptions.find((o) => o.value!.type === cellType)!; + const cellDisplayModeOptions: Array> = [ + { value: TableCellDisplayMode.Auto, label: t('table.cell-types.auto', 'Auto') }, + { value: TableCellDisplayMode.ColorText, label: t('table.cell-types.color-text', 'Colored text') }, + { + value: TableCellDisplayMode.ColorBackground, + label: t('table.cell-types.color-background', 'Colored background'), + }, + { value: TableCellDisplayMode.DataLinks, label: t('table.cell-types.data-links', 'Data links') }, + { value: TableCellDisplayMode.Gauge, label: t('table.cell-types.gauge', 'Gauge') }, + { value: TableCellDisplayMode.Sparkline, label: t('table.cell-types.sparkline', 'Sparkline') }, + { value: TableCellDisplayMode.JSONView, label: t('table.cell-types.json', 'JSON View') }, + { value: TableCellDisplayMode.Pill, label: t('table.cell-types.pill', 'Pill') }, + { value: TableCellDisplayMode.Markdown, label: t('table.cell-types.markdown', 'Markdown + HTML') }, + { value: TableCellDisplayMode.Image, label: t('table.cell-types.image', 'Image') }, + { value: TableCellDisplayMode.Actions, label: t('table.cell-types.actions', 'Actions') }, + ]; + const currentMode = cellDisplayModeOptions.find((o) => o.value === cellType)!; + let [settingCache, setSettingCache] = useState>({}); // Update display mode on change - const onCellTypeChange = (v: SelectableValue) => { - if (v.value !== undefined) { + const onCellTypeChange = (v: ComboboxOption) => { + if (v !== null) { // Set the new type of cell starting // with default settings - value = v.value; + value = { type: v.value }; // When changing cell type see if there were previously stored // settings and merge those with the changed value @@ -60,11 +92,9 @@ export const TableCellOptionEditor = ({ value, onChange }: Props) => { return (
- -
-
- ); -} - -function getCurrentFrameIndex(frames: DataFrame[], options: Options) { - return options.frameIndex > 0 && options.frameIndex < frames.length ? options.frameIndex : 0; -} - -// placeholder function; assuming the values are already interpolated -const replaceVars: InterpolateFunction = (value: string) => value; - -const getCellActions = ( - dataFrame: DataFrame, - field: Field, - rowIndex: number, - replaceVariables: InterpolateFunction | undefined -): Array> => { - const numActions = field.config.actions?.length ?? 0; - - if (numActions > 0) { - const actions = getActions( - dataFrame, - field, - field.state!.scopedVars!, - replaceVariables ?? replaceVars, - field.config.actions ?? [], - { valueRowIndex: rowIndex } - ); - - if (actions.length === 1) { - return actions; - } else { - const actionsOut: Array> = []; - const actionLookup = new Set(); - - actions.forEach((action) => { - const key = action.title; - - if (!actionLookup.has(key)) { - actionsOut.push(action); - actionLookup.add(key); - } - }); - - return actionsOut; - } - } - - return []; -}; - -const tableStyles = { - wrapper: css({ - display: 'flex', - flexDirection: 'column', - justifyContent: 'space-between', - height: '100%', - }), - selectWrapper: css({ - padding: '8px 8px 0px 8px', - }), -}; diff --git a/public/app/plugins/panel/table/table-new/__snapshots__/migrations.test.ts.snap b/public/app/plugins/panel/table/table-new/__snapshots__/migrations.test.ts.snap deleted file mode 100644 index e51e742e7d5..00000000000 --- a/public/app/plugins/panel/table/table-new/__snapshots__/migrations.test.ts.snap +++ /dev/null @@ -1,82 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Table Migrations migrates transform out to core transforms 1`] = ` -{ - "fieldConfig": { - "defaults": { - "custom": {}, - }, - "overrides": [], - }, - "transformations": [ - { - "id": "seriesToColumns", - "options": { - "reducers": [], - }, - }, - ], -} -`; - -exports[`Table Migrations migrates transform out to core transforms 2`] = ` -{ - "fieldConfig": { - "defaults": { - "custom": {}, - }, - "overrides": [], - }, - "transformations": [ - { - "id": "seriesToRows", - "options": { - "reducers": [], - }, - }, - ], -} -`; - -exports[`Table Migrations migrates transform out to core transforms 3`] = ` -{ - "fieldConfig": { - "defaults": { - "custom": {}, - }, - "overrides": [], - }, - "transformations": [ - { - "id": "reduce", - "options": { - "includeTimeField": false, - "reducers": [ - "mean", - "max", - "lastNotNull", - ], - }, - }, - ], -} -`; - -exports[`Table Migrations migrates transform out to core transforms 4`] = ` -{ - "fieldConfig": { - "defaults": { - "custom": {}, - }, - "overrides": [], - }, - "transformations": [ - { - "id": "merge", - "options": { - "reducers": [], - }, - }, - ], -} -`; diff --git a/public/app/plugins/panel/table/table-new/cells/BarGaugeCellOptionsEditor.tsx b/public/app/plugins/panel/table/table-new/cells/BarGaugeCellOptionsEditor.tsx deleted file mode 100644 index 4e29eaf9360..00000000000 --- a/public/app/plugins/panel/table/table-new/cells/BarGaugeCellOptionsEditor.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import { SelectableValue } from '@grafana/data'; -import { t } from '@grafana/i18n'; -import { BarGaugeDisplayMode, BarGaugeValueMode, TableBarGaugeCellOptions } from '@grafana/schema'; -import { Field, RadioButtonGroup, Stack } from '@grafana/ui'; - -import { TableCellEditorProps } from '../TableCellOptionEditor'; - -type Props = TableCellEditorProps; - -export function BarGaugeCellOptionsEditor({ cellOptions, onChange }: Props) { - // Set the display mode on change - - const onCellOptionsChange = (v: BarGaugeDisplayMode) => { - cellOptions.mode = v; - onChange(cellOptions); - }; - - const onValueModeChange = (v: BarGaugeValueMode) => { - cellOptions.valueDisplayMode = v; - onChange(cellOptions); - }; - - return ( - - - - - - - - - ); -} - -const barGaugeOpts: SelectableValue[] = [ - { value: BarGaugeDisplayMode.Basic, label: 'Basic' }, - { value: BarGaugeDisplayMode.Gradient, label: 'Gradient' }, - { value: BarGaugeDisplayMode.Lcd, label: 'Retro LCD' }, -]; - -const valueModes: SelectableValue[] = [ - { value: BarGaugeValueMode.Color, label: 'Value color' }, - { value: BarGaugeValueMode.Text, label: 'Text color' }, - { value: BarGaugeValueMode.Hidden, label: 'Hidden' }, -]; diff --git a/public/app/plugins/panel/table/table-new/cells/ColorBackgroundCellOptionsEditor.tsx b/public/app/plugins/panel/table/table-new/cells/ColorBackgroundCellOptionsEditor.tsx deleted file mode 100644 index 33d85539ec4..00000000000 --- a/public/app/plugins/panel/table/table-new/cells/ColorBackgroundCellOptionsEditor.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import { SelectableValue } from '@grafana/data'; -import { selectors } from '@grafana/e2e-selectors'; -import { t } from '@grafana/i18n'; -import { TableCellBackgroundDisplayMode, TableColoredBackgroundCellOptions } from '@grafana/schema'; -import { Field, RadioButtonGroup, Switch } from '@grafana/ui'; - -import { TableCellEditorProps } from '../TableCellOptionEditor'; - -import { TextWrapOptionsEditor } from './TextWrapOptionsEditor'; - -const colorBackgroundOpts: Array> = [ - { value: TableCellBackgroundDisplayMode.Basic, label: 'Basic' }, - { value: TableCellBackgroundDisplayMode.Gradient, label: 'Gradient' }, -]; -export const ColorBackgroundCellOptionsEditor = ({ - cellOptions, - onChange, -}: TableCellEditorProps) => { - // Set the display mode on change - const onCellOptionsChange = (v: TableCellBackgroundDisplayMode) => { - cellOptions.mode = v; - onChange(cellOptions); - }; - const onColorRowChange = () => { - cellOptions.applyToRow = !cellOptions.applyToRow; - onChange(cellOptions); - }; - - return ( - <> - - - - - - - - - { - cellOptions.wrapText = updatedCellOptions.wrapText; - onChange(cellOptions); - }} - /> - - ); -}; diff --git a/public/app/plugins/panel/table/table-new/cells/ImageCellOptionsEditor.tsx b/public/app/plugins/panel/table/table-new/cells/ImageCellOptionsEditor.tsx deleted file mode 100644 index 75233f4fefe..00000000000 --- a/public/app/plugins/panel/table/table-new/cells/ImageCellOptionsEditor.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import { FormEvent } from 'react'; - -import { t } from '@grafana/i18n'; -import { TableImageCellOptions } from '@grafana/schema'; -import { Field, Input } from '@grafana/ui'; - -import { TableCellEditorProps } from '../TableCellOptionEditor'; - -export const ImageCellOptionsEditor = ({ cellOptions, onChange }: TableCellEditorProps) => { - const onAltChange = (e: FormEvent) => { - cellOptions.alt = e.currentTarget.value; - onChange(cellOptions); - }; - - const onTitleChange = (e: FormEvent) => { - cellOptions.title = e.currentTarget.value; - onChange(cellOptions); - }; - - return ( - <> - - - - - - - - - ); -}; diff --git a/public/app/plugins/panel/table/table-new/cells/SparklineCellOptionsEditor.tsx b/public/app/plugins/panel/table/table-new/cells/SparklineCellOptionsEditor.tsx deleted file mode 100644 index 69ad1c0f0b5..00000000000 --- a/public/app/plugins/panel/table/table-new/cells/SparklineCellOptionsEditor.tsx +++ /dev/null @@ -1,94 +0,0 @@ -import { css } from '@emotion/css'; -import { useMemo } from 'react'; - -import { createFieldConfigRegistry, SetFieldConfigOptionsArgs } from '@grafana/data'; -import { GraphFieldConfig, TableSparklineCellOptions } from '@grafana/schema'; -import { Field, useStyles2, Stack } from '@grafana/ui'; -import { defaultSparklineCellConfig } from '@grafana/ui/internal'; - -import { getGraphFieldConfig } from '../../../timeseries/config'; -import { TableCellEditorProps } from '../TableCellOptionEditor'; - -type OptionKey = keyof TableSparklineCellOptions; - -const optionIds: Array = [ - 'hideValue', - 'drawStyle', - 'lineInterpolation', - 'barAlignment', - 'lineWidth', - 'fillOpacity', - 'gradientMode', - 'lineStyle', - 'spanNulls', - 'showPoints', - 'pointSize', -]; - -function getChartCellConfig(cfg: GraphFieldConfig): SetFieldConfigOptionsArgs { - const graphFieldConfig = getGraphFieldConfig(cfg); - return { - ...graphFieldConfig, - useCustomConfig: (builder) => { - graphFieldConfig.useCustomConfig?.(builder); - builder.addBooleanSwitch({ - path: 'hideValue', - name: 'Hide value', - }); - }, - }; -} - -export const SparklineCellOptionsEditor = (props: TableCellEditorProps) => { - const { cellOptions, onChange } = props; - - const registry = useMemo(() => { - const config = getChartCellConfig(defaultSparklineCellConfig); - return createFieldConfigRegistry(config, 'ChartCell'); - }, []); - - const style = useStyles2(getStyles); - - const values = { ...defaultSparklineCellConfig, ...cellOptions }; - - return ( - - {registry.list(optionIds.map((id) => `custom.${id}`)).map((item) => { - if (item.showIf && !item.showIf(values)) { - return null; - } - const Editor = item.editor; - const path = item.path; - - return ( - - onChange({ ...cellOptions, [path]: val })} - value={(isOptionKey(path, values) ? values[path] : undefined) ?? item.defaultValue} - item={item} - context={{ data: [] }} - /> - - ); - })} - - ); -}; - -// jumping through hoops to avoid using "any" -function isOptionKey(key: string, options: TableSparklineCellOptions): key is OptionKey { - return key in options; -} - -const getStyles = () => ({ - field: css({ - width: '100%', - - // @TODO don't show "scheme" option for custom gradient mode. - // it needs thresholds to work, which are not supported - // for area chart cell right now - "[title='Use color scheme to define gradient']": { - display: 'none', - }, - }), -}); diff --git a/public/app/plugins/panel/table/table-new/img/icn-table-panel.svg b/public/app/plugins/panel/table/table-new/img/icn-table-panel.svg deleted file mode 100644 index 21846a8d38f..00000000000 --- a/public/app/plugins/panel/table/table-new/img/icn-table-panel.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/app/plugins/panel/table/table-new/migrations.test.ts b/public/app/plugins/panel/table/table-new/migrations.test.ts deleted file mode 100644 index a40120c8857..00000000000 --- a/public/app/plugins/panel/table/table-new/migrations.test.ts +++ /dev/null @@ -1,364 +0,0 @@ -import { createDataFrame, FieldType, PanelModel } from '@grafana/data'; - -import { migrateFromParentRowIndexToNestedFrames, tablePanelChangedHandler } from './migrations'; - -describe('Table Migrations', () => { - it('migrates transform out to core transforms', () => { - const toColumns = { - angular: { - columns: [], - styles: [], - transform: 'timeseries_to_columns', - options: {}, - }, - }; - const toRows = { - angular: { - columns: [], - styles: [], - transform: 'timeseries_to_rows', - options: {}, - }, - }; - const aggregations = { - angular: { - columns: [ - { - text: 'Avg', - value: 'avg', - $$hashKey: 'object:82', - }, - { - text: 'Max', - value: 'max', - $$hashKey: 'object:83', - }, - { - text: 'Current', - value: 'current', - $$hashKey: 'object:84', - }, - ], - styles: [], - transform: 'timeseries_aggregations', - options: {}, - }, - }; - const table = { - angular: { - columns: [], - styles: [], - transform: 'table', - options: {}, - }, - }; - - const columnsPanel = {} as PanelModel; - tablePanelChangedHandler(columnsPanel, 'table-old', toColumns); - expect(columnsPanel).toMatchSnapshot(); - const rowsPanel = {} as PanelModel; - tablePanelChangedHandler(rowsPanel, 'table-old', toRows); - expect(rowsPanel).toMatchSnapshot(); - const aggregationsPanel = {} as PanelModel; - tablePanelChangedHandler(aggregationsPanel, 'table-old', aggregations); - expect(aggregationsPanel).toMatchSnapshot(); - const tablePanel = {} as PanelModel; - tablePanelChangedHandler(tablePanel, 'table-old', table); - expect(tablePanel).toMatchSnapshot(); - }); - - it('migrates styles to field config overrides and defaults', () => { - const oldStyles = { - angular: { - columns: [], - styles: [ - { - alias: 'Time', - align: 'auto', - dateFormat: 'YYYY-MM-DD HH:mm:ss', - pattern: 'Time', - type: 'date', - $$hashKey: 'object:195', - }, - { - alias: '', - align: 'left', - colorMode: 'cell', - colors: ['rgba(245, 54, 54, 0.9)', 'rgba(237, 129, 40, 0.89)', 'rgba(50, 172, 45, 0.97)'], - dateFormat: 'YYYY-MM-DD HH:mm:ss', - decimals: 2, - mappingType: 1, - pattern: 'ColorCell', - thresholds: ['5', '10'], - type: 'number', - unit: 'currencyUSD', - $$hashKey: 'object:196', - }, - { - alias: '', - align: 'auto', - colorMode: 'value', - colors: ['rgba(245, 54, 54, 0.9)', 'rgba(237, 129, 40, 0.89)', 'rgba(50, 172, 45, 0.97)'], - dateFormat: 'YYYY-MM-DD HH:mm:ss', - decimals: 2, - link: true, - linkTargetBlank: true, - linkTooltip: '', - linkUrl: 'http://www.grafana.com', - mappingType: 1, - pattern: 'ColorValue', - thresholds: ['5', '10'], - type: 'number', - unit: 'Bps', - $$hashKey: 'object:197', - }, - { - unit: 'short', - type: 'number', - alias: '', - decimals: 2, - colors: ['rgba(245, 54, 54, 0.9)', 'rgba(237, 129, 40, 0.89)', 'rgba(50, 172, 45, 0.97)'], - colorMode: null, - pattern: '/.*/', - thresholds: [], - align: 'right', - }, - ], - }, - }; - - const panel = {} as PanelModel; - tablePanelChangedHandler(panel, 'table-old', oldStyles); - expect(panel).toMatchInlineSnapshot(` - { - "fieldConfig": { - "defaults": { - "custom": { - "align": "right", - }, - "decimals": 2, - "displayName": "", - "unit": "short", - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "Time", - }, - "properties": [ - { - "id": "displayName", - "value": "Time", - }, - { - "id": "unit", - "value": "time: YYYY-MM-DD HH:mm:ss", - }, - { - "id": "custom.align", - "value": null, - }, - ], - }, - { - "matcher": { - "id": "byName", - "options": "ColorCell", - }, - "properties": [ - { - "id": "unit", - "value": "currencyUSD", - }, - { - "id": "decimals", - "value": 2, - }, - { - "id": "custom.cellOptions", - "value": { - "type": "color-background", - }, - }, - { - "id": "custom.align", - "value": "left", - }, - { - "id": "thresholds", - "value": { - "mode": "absolute", - "steps": [ - { - "color": "rgba(245, 54, 54, 0.9)", - "value": -Infinity, - }, - { - "color": "rgba(237, 129, 40, 0.89)", - "value": 5, - }, - { - "color": "rgba(50, 172, 45, 0.97)", - "value": 10, - }, - ], - }, - }, - ], - }, - { - "matcher": { - "id": "byName", - "options": "ColorValue", - }, - "properties": [ - { - "id": "unit", - "value": "Bps", - }, - { - "id": "decimals", - "value": 2, - }, - { - "id": "links", - "value": [ - { - "targetBlank": true, - "title": "", - "url": "http://www.grafana.com", - }, - ], - }, - { - "id": "custom.cellOptions", - "value": { - "type": "color-text", - }, - }, - { - "id": "custom.align", - "value": null, - }, - { - "id": "thresholds", - "value": { - "mode": "absolute", - "steps": [ - { - "color": "rgba(245, 54, 54, 0.9)", - "value": -Infinity, - }, - { - "color": "rgba(237, 129, 40, 0.89)", - "value": 5, - }, - { - "color": "rgba(50, 172, 45, 0.97)", - "value": 10, - }, - ], - }, - }, - ], - }, - ], - }, - "transformations": [], - } - `); - }); - - it('migrates hidden fields to override', () => { - const oldStyles = { - angular: { - columns: [], - styles: [ - { - dateFormat: 'YYYY-MM-DD HH:mm:ss', - pattern: 'time', - type: 'hidden', - }, - ], - }, - }; - - const panel = {} as PanelModel; - tablePanelChangedHandler(panel, 'table-old', oldStyles); - expect(panel.fieldConfig.overrides).toEqual([ - { - matcher: { - id: 'byName', - options: 'time', - }, - properties: [ - { - id: 'custom.hidden', - value: true, - }, - ], - }, - ]); - }); - - it('migrates DataFrame[] from format using meta.custom.parentRowIndex to format using FieldType.nestedFrames', () => { - const mainFrame = (refId: string) => { - return createDataFrame({ - refId, - fields: [ - { - name: 'field', - type: FieldType.string, - config: {}, - values: ['a', 'b', 'c'], - }, - ], - meta: { - preferredVisualisationType: 'table', - }, - }); - }; - - const subFrame = (index: number) => { - return createDataFrame({ - refId: 'B', - fields: [ - { - name: `field_${index}`, - type: FieldType.string, - config: {}, - values: [`${index}_subA`, 'subB', 'subC'], - }, - ], - meta: { - preferredVisualisationType: 'table', - custom: { - parentRowIndex: index, - }, - }, - }); - }; - - const oldFormat = [mainFrame('A'), mainFrame('B'), subFrame(0), subFrame(1)]; - const newFormat = migrateFromParentRowIndexToNestedFrames(oldFormat); - expect(newFormat.length).toBe(2); - expect(newFormat[0].refId).toBe('A'); - expect(newFormat[1].refId).toBe('B'); - expect(newFormat[0].fields.length).toBe(1); - expect(newFormat[1].fields.length).toBe(2); - expect(newFormat[0].fields[0].name).toBe('field'); - expect(newFormat[1].fields[0].name).toBe('field'); - expect(newFormat[1].fields[1].name).toBe('nested'); - expect(newFormat[1].fields[1].type).toBe(FieldType.nestedFrames); - expect(newFormat[1].fields[1].values.length).toBe(2); - expect(newFormat[1].fields[1].values[0][0].refId).toBe('B'); - expect(newFormat[1].fields[1].values[1][0].refId).toBe('B'); - expect(newFormat[1].fields[1].values[0][0].length).toBe(3); - expect(newFormat[1].fields[1].values[0][0].length).toBe(3); - expect(newFormat[1].fields[1].values[0][0].fields[0].name).toBe('field_0'); - expect(newFormat[1].fields[1].values[1][0].fields[0].name).toBe('field_1'); - expect(newFormat[1].fields[1].values[0][0].fields[0].values[0]).toBe('0_subA'); - expect(newFormat[1].fields[1].values[1][0].fields[0].values[0]).toBe('1_subA'); - }); -}); diff --git a/public/app/plugins/panel/table/table-new/migrations.ts b/public/app/plugins/panel/table/table-new/migrations.ts deleted file mode 100644 index aa4c635682c..00000000000 --- a/public/app/plugins/panel/table/table-new/migrations.ts +++ /dev/null @@ -1,299 +0,0 @@ -import { omitBy, isNil, isNumber, defaultTo, groupBy } from 'lodash'; - -import { - PanelModel, - FieldMatcherID, - ConfigOverrideRule, - ThresholdsMode, - ThresholdsConfig, - FieldConfig, - DataFrame, - FieldType, -} from '@grafana/data'; -import { ReduceTransformerOptions } from '@grafana/data/internal'; - -import { Options } from './panelcfg.gen'; - -/** - * At 7.0, the `table` panel was swapped from an angular implementation to a react one. - * The models do not match, so this process will delegate to the old implementation when - * a saved table configuration exists. - */ -export const tableMigrationHandler = (panel: PanelModel): Partial => { - // Table was saved as an angular table, lets just swap to the 'table-old' panel - if (!panel.pluginVersion && 'columns' in panel) { - console.log('Was angular table', panel); - } - - // Nothing changed - return panel.options; -}; - -const transformsMap = { - timeseries_to_rows: 'seriesToRows', - timeseries_to_columns: 'seriesToColumns', - timeseries_aggregations: 'reduce', - table: 'merge', -}; - -const columnsMap = { - avg: 'mean', - min: 'min', - max: 'max', - total: 'sum', - current: 'lastNotNull', - count: 'count', -}; - -const colorModeMap = { - cell: 'color-background', - row: 'color-background', - value: 'color-text', -}; - -type Transformations = keyof typeof transformsMap; - -type Transformation = { - id: string; - options: ReduceTransformerOptions; -}; - -type Columns = keyof typeof columnsMap; - -type Column = { - value: Columns; - text: string; -}; - -type ColorModes = keyof typeof colorModeMap; - -const generateThresholds = (thresholds: string[], colors: string[]) => { - return [-Infinity, ...thresholds].map((threshold, idx) => ({ - color: colors[idx], - value: isNumber(threshold) ? threshold : parseInt(threshold, 10), - })); -}; - -const migrateTransformations = ( - panel: PanelModel>, - oldOpts: { columns: any; transform: Transformations } -) => { - const transformations: Transformation[] = panel.transformations ?? []; - if (Object.keys(transformsMap).includes(oldOpts.transform)) { - const opts: ReduceTransformerOptions = { - reducers: [], - }; - if (oldOpts.transform === 'timeseries_aggregations') { - opts.includeTimeField = false; - opts.reducers = oldOpts.columns.map((column: Column) => columnsMap[column.value]); - } - transformations.push({ - id: transformsMap[oldOpts.transform], - options: opts, - }); - } - return transformations; -}; - -type Style = { - unit: string; - type: string; - alias: string; - decimals: number; - colors: string[]; - colorMode: ColorModes; - pattern: string; - thresholds: string[]; - align?: string; - dateFormat: string; - link: boolean; - linkTargetBlank?: boolean; - linkTooltip?: string; - linkUrl?: string; -}; - -const migrateTableStyleToOverride = (style: Style) => { - const fieldMatcherId = /^\/.*\/$/.test(style.pattern) ? FieldMatcherID.byRegexp : FieldMatcherID.byName; - const override: ConfigOverrideRule = { - matcher: { - id: fieldMatcherId, - options: style.pattern, - }, - properties: [], - }; - - if (style.alias) { - override.properties.push({ - id: 'displayName', - value: style.alias, - }); - } - - if (style.unit) { - override.properties.push({ - id: 'unit', - value: style.unit, - }); - } - - if (style.decimals) { - override.properties.push({ - id: 'decimals', - value: style.decimals, - }); - } - - if (style.type === 'date') { - override.properties.push({ - id: 'unit', - value: `time: ${style.dateFormat}`, - }); - } - - if (style.type === 'hidden') { - override.properties.push({ - id: 'custom.hidden', - value: true, - }); - } - - if (style.link) { - override.properties.push({ - id: 'links', - value: [ - { - title: defaultTo(style.linkTooltip, ''), - url: defaultTo(style.linkUrl, ''), - targetBlank: defaultTo(style.linkTargetBlank, false), - }, - ], - }); - } - - if (style.colorMode) { - override.properties.push({ - id: 'custom.cellOptions', - value: { - type: colorModeMap[style.colorMode], - }, - }); - } - - if (style.align) { - override.properties.push({ - id: 'custom.align', - value: style.align === 'auto' ? null : style.align, - }); - } - - if (style.thresholds?.length) { - override.properties.push({ - id: 'thresholds', - value: { - mode: ThresholdsMode.Absolute, - steps: generateThresholds(style.thresholds, style.colors), - }, - }); - } - - return override; -}; - -const migrateDefaults = (prevDefaults: Style) => { - let defaults: FieldConfig = { - custom: {}, - }; - if (prevDefaults) { - defaults = omitBy( - { - unit: prevDefaults.unit, - decimals: prevDefaults.decimals, - displayName: prevDefaults.alias, - custom: { - align: prevDefaults.align === 'auto' ? null : prevDefaults.align, - }, - }, - isNil - ); - - if (prevDefaults.thresholds.length) { - const thresholds: ThresholdsConfig = { - mode: ThresholdsMode.Absolute, - steps: generateThresholds(prevDefaults.thresholds, prevDefaults.colors), - }; - defaults.thresholds = thresholds; - } - - if (prevDefaults.colorMode) { - defaults.custom.cellOptions = { - type: colorModeMap[prevDefaults.colorMode], - }; - } - } - return defaults; -}; - -/** - * This is called when the panel changes from another panel - */ -export const tablePanelChangedHandler = ( - panel: PanelModel>, - prevPluginId: string, - prevOptions: any -) => { - // Changing from angular table panel - if (prevPluginId === 'table-old' && prevOptions.angular) { - const oldOpts = prevOptions.angular; - const transformations = migrateTransformations(panel, oldOpts); - const prevDefaults = oldOpts.styles.find((style: any) => style.pattern === '/.*/'); - const defaults = migrateDefaults(prevDefaults); - const overrides = oldOpts.styles.filter((style: any) => style.pattern !== '/.*/').map(migrateTableStyleToOverride); - - panel.transformations = transformations; - panel.fieldConfig = { - defaults, - overrides, - }; - } - - return {}; -}; - -const getMainFrames = (frames: DataFrame[] | null) => { - return frames?.filter((df) => df.meta?.custom?.parentRowIndex === undefined) || [frames?.[0]]; -}; - -/** - * In 9.3 meta.custom.parentRowIndex was introduced to support sub-tables. - * In 10.2 meta.custom.parentRowIndex was deprecated in favor of FieldType.nestedFrames, which supports multiple nested frames. - * Migrate DataFrame[] from using meta.custom.parentRowIndex to using FieldType.nestedFrames - */ -export const migrateFromParentRowIndexToNestedFrames = (frames: DataFrame[] | null) => { - const migratedFrames: DataFrame[] = []; - const mainFrames = getMainFrames(frames).filter( - (frame: DataFrame | undefined): frame is DataFrame => !!frame && frame.length !== 0 - ); - - mainFrames?.forEach((frame) => { - const subFrames = frames?.filter((df) => frame.refId === df.refId && df.meta?.custom?.parentRowIndex !== undefined); - const subFramesGrouped = groupBy(subFrames, (frame: DataFrame) => frame.meta?.custom?.parentRowIndex); - const subFramesByIndex = Object.keys(subFramesGrouped).map((key) => subFramesGrouped[key]); - const migratedFrame = { ...frame }; - - if (subFrames && subFrames.length > 0) { - migratedFrame.fields.push({ - name: 'nested', - type: FieldType.nestedFrames, - config: {}, - values: subFramesByIndex, - }); - } - migratedFrames.push(migratedFrame); - }); - - return migratedFrames; -}; - -export const hasDeprecatedParentRowIndex = (frames: DataFrame[] | null) => { - return frames?.some((df) => df.meta?.custom?.parentRowIndex !== undefined); -}; diff --git a/public/app/plugins/panel/table/table-new/module.tsx b/public/app/plugins/panel/table/table-new/module.tsx deleted file mode 100644 index 0e17f6c1072..00000000000 --- a/public/app/plugins/panel/table/table-new/module.tsx +++ /dev/null @@ -1,258 +0,0 @@ -import { - FieldOverrideContext, - FieldType, - getFieldDisplayName, - PanelPlugin, - ReducerID, - standardEditorsRegistry, - identityOverrideProcessor, - FieldConfigProperty, -} from '@grafana/data'; -import { t } from '@grafana/i18n'; -import { - TableCellOptions, - TableCellDisplayMode, - defaultTableFieldOptions, - TableCellHeight, - TableCellTooltipPlacement, -} from '@grafana/schema'; - -import { PaginationEditor } from './PaginationEditor'; -import { TableCellOptionEditor } from './TableCellOptionEditor'; -import { TablePanel } from './TablePanel'; -import { tableMigrationHandler, tablePanelChangedHandler } from './migrations'; -import { Options, defaultOptions, FieldConfig } from './panelcfg.gen'; -import { TableSuggestionsSupplier } from './suggestions'; - -export const plugin = new PanelPlugin(TablePanel) - .setPanelChangeHandler(tablePanelChangedHandler) - .setMigrationHandler(tableMigrationHandler) - .useFieldConfig({ - standardOptions: { - [FieldConfigProperty.Actions]: { - hideFromDefaults: false, - }, - }, - useCustomConfig: (builder) => { - const category = [t('table-new.category-table', 'Table')]; - const cellCategory = [t('table-new.category-cell-options', 'Cell options')]; - builder - .addNumberInput({ - path: 'minWidth', - name: t('table-new.name-min-column-width', 'Minimum column width'), - category, - description: t('table-new.description-min-column-width', 'The minimum width for column auto resizing'), - settings: { - placeholder: '150', - min: 50, - max: 500, - }, - shouldApply: () => true, - defaultValue: defaultTableFieldOptions.minWidth, - }) - .addNumberInput({ - path: 'width', - name: t('table-new.name-column-width', 'Column width'), - category, - settings: { - placeholder: t('table-new.placeholder-column-width', 'auto'), - min: 20, - }, - shouldApply: () => true, - defaultValue: defaultTableFieldOptions.width, - }) - .addRadio({ - path: 'align', - name: t('table-new.name-column-alignment', 'Column alignment'), - category, - settings: { - options: [ - { label: t('table-new.column-alignment-options.label-auto', 'Auto'), value: 'auto' }, - { label: t('table-new.column-alignment-options.label-left', 'Left'), value: 'left' }, - { label: t('table-new.column-alignment-options.label-center', 'Center'), value: 'center' }, - { label: t('table-new.column-alignment-options.label-right', 'Right'), value: 'right' }, - ], - }, - defaultValue: defaultTableFieldOptions.align, - }) - .addCustomEditor({ - id: 'cellOptions', - path: 'cellOptions', - name: t('table-new.name-cell-type', 'Cell type'), - editor: TableCellOptionEditor, - override: TableCellOptionEditor, - defaultValue: defaultTableFieldOptions.cellOptions, - process: identityOverrideProcessor, - category: cellCategory, - shouldApply: () => true, - }) - .addBooleanSwitch({ - path: 'inspect', - name: t('table-new.name-cell-value-inspect', 'Cell value inspect'), - description: t('table-new.description-cell-value-inspect', 'Enable cell value inspection in a modal window'), - defaultValue: false, - category: cellCategory, - showIf: (cfg) => { - return ( - cfg.cellOptions.type === TableCellDisplayMode.Auto || - cfg.cellOptions.type === TableCellDisplayMode.JSONView || - cfg.cellOptions.type === TableCellDisplayMode.ColorText || - cfg.cellOptions.type === TableCellDisplayMode.ColorBackground - ); - }, - }) - .addBooleanSwitch({ - path: 'filterable', - name: t('table-new.name-column-filter', 'Column filter'), - category, - description: t('table-new.description-column-filter', 'Enables/disables field filters in table'), - defaultValue: defaultTableFieldOptions.filterable, - }) - .addBooleanSwitch({ - path: 'wrapHeaderText', - name: t('table.name-wrap-header-text', 'Wrap header text'), - description: t('table.description-wrap-header-text', 'Enables text wrapping for column headers'), - category, - defaultValue: defaultTableFieldOptions.wrapHeaderText, - }) - .addBooleanSwitch({ - path: 'hidden', - name: t('table-new.name-hide-in-table', 'Hide in table'), - category, - defaultValue: undefined, - hideFromDefaults: true, - }) - .addFieldNamePicker({ - path: 'tooltip.field', - name: t('table-new.name-tooltip-from-field', 'Tooltip from field'), - description: t( - 'table-new.description-tooltip-from-field', - 'Render a cell from a field (hidden or visible) in a tooltip' - ), - category: cellCategory, - }) - .addSelect({ - path: 'tooltip.placement', - name: t('table-new.name-tooltip-placement', 'Tooltip placement'), - category: cellCategory, - settings: { - options: [ - { - label: t('table-new.tooltip-placement-options.label-auto', 'Auto'), - value: TableCellTooltipPlacement.Auto, - }, - { - label: t('table-new.tooltip-placement-options.label-top', 'Top'), - value: TableCellTooltipPlacement.Top, - }, - { - label: t('table-new.tooltip-placement-options.label-right', 'Right'), - value: TableCellTooltipPlacement.Right, - }, - { - label: t('table-new.tooltip-placement-options.label-bottom', 'Bottom'), - value: TableCellTooltipPlacement.Bottom, - }, - { - label: t('table-new.tooltip-placement-options.label-left', 'Left'), - value: TableCellTooltipPlacement.Left, - }, - ], - }, - defaultValue: 'auto', - showIf: (cfg) => cfg.tooltip?.field !== undefined, - }); - }, - }) - .setPanelOptions((builder) => { - const footerCategory = [t('table-new.category-table-footer', 'Table footer')]; - const category = [t('table-new.category-table', 'Table')]; - builder - .addBooleanSwitch({ - path: 'showHeader', - name: t('table-new.name-show-table-header', 'Show table header'), - category, - defaultValue: defaultOptions.showHeader, - }) - .addNumberInput({ - path: 'frozenColumns.left', - name: t('table-new.name-frozen-columns', 'Frozen columns'), - description: t('table-new.description-frozen-columns', 'Columns are frozen from the left side of the table'), - settings: { - placeholder: 'none', - }, - category, - }) - .addRadio({ - path: 'cellHeight', - name: t('table-new.name-cell-height', 'Cell height'), - category, - defaultValue: defaultOptions.cellHeight, - settings: { - options: [ - { value: TableCellHeight.Sm, label: t('table-new.cell-height-options.label-small', 'Small') }, - { value: TableCellHeight.Md, label: t('table-new.cell-height-options.label-medium', 'Medium') }, - { value: TableCellHeight.Lg, label: t('table-new.cell-height-options.label-large', 'Large') }, - ], - }, - }) - .addBooleanSwitch({ - path: 'footer.show', - category: footerCategory, - name: t('table-new.name-show-table-footer', 'Show table footer'), - defaultValue: defaultOptions.footer?.show, - }) - .addCustomEditor({ - id: 'footer.reducer', - category: footerCategory, - path: 'footer.reducer', - name: t('table-new.name-calculation', 'Calculation'), - description: t('table-new.description-calculation', 'Choose a reducer function / calculation'), - editor: standardEditorsRegistry.get('stats-picker').editor, - defaultValue: [ReducerID.sum], - showIf: (cfg) => cfg.footer?.show, - }) - .addBooleanSwitch({ - path: 'footer.countRows', - category: footerCategory, - name: t('table-new.name-count-rows', 'Count rows'), - description: t('table-new.description-count-rows', 'Display a single count for all data rows'), - defaultValue: defaultOptions.footer?.countRows, - showIf: (cfg) => cfg.footer?.reducer?.length === 1 && cfg.footer?.reducer[0] === ReducerID.count, - }) - .addMultiSelect({ - path: 'footer.fields', - category: footerCategory, - name: t('table-new.name-fields', 'Fields'), - description: t('table-new.description-fields', 'Select the fields that should be calculated'), - settings: { - allowCustomValue: false, - options: [], - placeholder: t('table-new.placeholder-fields', 'All Numeric Fields'), - getOptions: async (context: FieldOverrideContext) => { - const options = []; - if (context && context.data && context.data.length > 0) { - const frame = context.data[0]; - for (const field of frame.fields) { - if (field.type === FieldType.number) { - const name = getFieldDisplayName(field, frame, context.data); - const value = field.name; - options.push({ value, label: name }); - } - } - } - return options; - }, - }, - defaultValue: '', - showIf: (cfg) => cfg.footer?.show && !(cfg.footer?.countRows && cfg.footer?.reducer.includes(ReducerID.count)), - }) - .addCustomEditor({ - id: 'footer.enablePagination', - path: 'footer.enablePagination', - name: t('table-new.name-enable-pagination', 'Enable pagination'), - category, - editor: PaginationEditor, - }); - }) - .setSuggestionsSupplier(new TableSuggestionsSupplier()); diff --git a/public/app/plugins/panel/table/table-new/panelcfg.cue b/public/app/plugins/panel/table/table-new/panelcfg.cue deleted file mode 100644 index 1bfbd053cf2..00000000000 --- a/public/app/plugins/panel/table/table-new/panelcfg.cue +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2021 Grafana Labs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package grafanaplugin - -import ( - ui "github.com/grafana/grafana/packages/grafana-schema/src/common" -) - -composableKinds: PanelCfg: { - maturity: "experimental" - lineage: { - schemas: [{ - version: [0, 0] - schema: { - Options: { - // Represents the index of the selected frame - frameIndex: number | *0 - // Controls whether the panel should show the header - showHeader: bool | *true - // Controls whether the header should show icons for the column types - showTypeIcons?: bool | *false - // Used to control row sorting - sortBy?: [...ui.TableSortByFieldState] - // Controls footer options - footer?: ui.TableFooterOptions | *{ - // Controls whether the footer should be shown - show: false - // Controls whether the footer should show the total number of rows on Count calculation - countRows: false - // Represents the selected calculations - reducer: [] - } - // Controls the height of the rows - cellHeight?: ui.TableCellHeight & (*"sm" | _) - // Defines the number of columns to freeze on the left side of the table - frozenColumns?: { - left?: number | *0 - } - } @cuetsy(kind="interface") - FieldConfig: { - ui.TableFieldOptions - } @cuetsy(kind="interface") - } - }] - lenses: [] - } -} diff --git a/public/app/plugins/panel/table/table-new/panelcfg.gen.ts b/public/app/plugins/panel/table/table-new/panelcfg.gen.ts deleted file mode 100644 index bed8165671a..00000000000 --- a/public/app/plugins/panel/table/table-new/panelcfg.gen.ts +++ /dev/null @@ -1,68 +0,0 @@ -// Code generated - EDITING IS FUTILE. DO NOT EDIT. -// -// Generated by: -// public/app/plugins/gen.go -// Using jennies: -// TSTypesJenny -// PluginTsTypesJenny -// -// Run 'make gen-cue' from repository root to regenerate. - -import * as ui from '@grafana/schema'; - -export interface Options { - /** - * Controls the height of the rows - */ - cellHeight?: ui.TableCellHeight; - /** - * Controls footer options - */ - footer?: ui.TableFooterOptions; - /** - * Represents the index of the selected frame - */ - frameIndex: number; - /** - * number of columns on the left side of the table that should be frozen - */ - frozenColumns?: { - left?: number; - }; - /** - * Controls whether the panel should show the header - */ - showHeader: boolean; - /** - * Controls whether the header should show icons for the column types - */ - showTypeIcons?: boolean; - /** - * Used to control row sorting - */ - sortBy?: Array; -} - -export const defaultOptions: Partial = { - cellHeight: ui.TableCellHeight.Sm, - footer: { - /** - * Controls whether the footer should be shown - */ - show: false, - /** - * Controls whether the footer should show the total number of rows on Count calculation - */ - countRows: false, - /** - * Represents the selected calculations - */ - reducer: [], - }, - frameIndex: 0, - showHeader: true, - showTypeIcons: false, - sortBy: [], -}; - -export interface FieldConfig extends ui.TableFieldOptions {} diff --git a/public/app/plugins/panel/table/table-new/plugin.json b/public/app/plugins/panel/table/table-new/plugin.json deleted file mode 100644 index 36fb974d930..00000000000 --- a/public/app/plugins/panel/table/table-new/plugin.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "type": "panel", - "name": "Table", - "id": "table", - "state": "beta", - - "info": { - "description": "Supports many column styles", - "author": { - "name": "Grafana Labs", - "url": "https://grafana.com" - }, - "logos": { - "small": "img/icn-table-panel.svg", - "large": "img/icn-table-panel.svg" - }, - "links": [ - { "name": "Raise issue", "url": "https://github.com/grafana/grafana/issues/new" }, - { - "name": "Documentation", - "url": "https://grafana.com/docs/grafana/latest/panels-visualizations/visualizations/table/" - } - ] - } -} diff --git a/public/app/plugins/panel/table/table-new/suggestions.ts b/public/app/plugins/panel/table/table-new/suggestions.ts deleted file mode 100644 index bcfead0fe31..00000000000 --- a/public/app/plugins/panel/table/table-new/suggestions.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { VisualizationSuggestionsBuilder } from '@grafana/data'; -import { TableFieldOptions } from '@grafana/schema'; -import icnTablePanelSvg from 'app/plugins/panel/table/img/icn-table-panel.svg'; -import { SuggestionName } from 'app/types/suggestions'; - -import { Options } from './panelcfg.gen'; - -export class TableSuggestionsSupplier { - getSuggestionsForData(builder: VisualizationSuggestionsBuilder) { - const list = builder.getListAppender({ - name: SuggestionName.Table, - pluginId: 'table', - options: {}, - fieldConfig: { - defaults: { - custom: {}, - }, - overrides: [], - }, - cardOptions: { - previewModifier: (s) => { - s.fieldConfig!.defaults.custom!.minWidth = 50; - }, - }, - }); - - // If there are not data suggest table anyway but use icon instead of real preview - if (builder.dataSummary.fieldCount === 0) { - list.append({ - cardOptions: { - imgSrc: icnTablePanelSvg, - }, - }); - } else { - list.append({}); - } - } -} diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json index 2706ec22fce..278716c8265 100644 --- a/public/locales/en-US/grafana.json +++ b/public/locales/en-US/grafana.json @@ -12708,10 +12708,6 @@ "login": "Login" }, "table": { - "auto-cell-options-editor": { - "description-wrap-text": "If selected text will be wrapped to the width of text in the configured column", - "label-wrap-text": "Wrap text" - }, "bar-gauge-cell-options-editor": { "label-gauge-display-mode": "Gauge display mode", "label-value-display": "Value display" @@ -12739,13 +12735,8 @@ }, "color-background-cell-options-editor": { "description-apply-to-entire-row": "If selected the entire row will be colored as this cell would be.", - "description-wrap-text": "If selected text will be wrapped to the width of text in the configured column", - "label": { - "text-alpha": "Alpha" - }, "label-apply-to-entire-row": "Apply to entire row", - "label-background-display-mode": "Background display mode", - "wrap-text": "Wrap text" + "label-background-display-mode": "Background display mode" }, "column-alignment-options": { "label-auto": "Auto", @@ -12763,7 +12754,9 @@ "description-column-filter": "Enables/disables field filters in table", "description-count-rows": "Display a single count for all data rows", "description-fields": "Select the fields that should be calculated", + "description-frozen-columns": "Columns are frozen from the left side of the table", "description-min-column-width": "The minimum width for column auto resizing", + "description-tooltip-from-field": "Render a cell from a field (hidden or visible) in a tooltip", "description-wrap-header-text": "Enables text wrapping for column headers", "image-cell-options-editor": { "description-alt-text": "Alternative text that will be displayed if an image can't be displayed or for users who use a screen reader", @@ -12789,50 +12782,6 @@ "name-column-filter": "Column filter", "name-column-width": "Column width", "name-count-rows": "Count rows", - "name-enable-paginations": "Enable pagination", - "name-fields": "Fields", - "name-hide-in-table": "Hide in table", - "name-min-column-width": "Minimum column width", - "name-show-table-footer": "Show table footer", - "name-show-table-header": "Show table header", - "name-wrap-header-text": "Wrap header text", - "placeholder-column-width": "auto", - "placeholder-fields": "All Numeric Fields", - "text-wrap-options": { - "label-wrap-text": "Wrap text" - } - }, - "table-new": { - "category-cell-options": "Cell options", - "category-table": "Table", - "category-table-footer": "Table footer", - "cell-height-options": { - "label-large": "Large", - "label-medium": "Medium", - "label-small": "Small" - }, - "column-alignment-options": { - "label-auto": "Auto", - "label-center": "Center", - "label-left": "Left", - "label-right": "Right" - }, - "description-calculation": "Choose a reducer function / calculation", - "description-cell-value-inspect": "Enable cell value inspection in a modal window", - "description-column-filter": "Enables/disables field filters in table", - "description-count-rows": "Display a single count for all data rows", - "description-fields": "Select the fields that should be calculated", - "description-frozen-columns": "Columns are frozen from the left side of the table", - "description-min-column-width": "The minimum width for column auto resizing", - "description-tooltip-from-field": "Render a cell from a field (hidden or visible) in a tooltip", - "name-calculation": "Calculation", - "name-cell-height": "Cell height", - "name-cell-type": "Cell type", - "name-cell-value-inspect": "Cell value inspect", - "name-column-alignment": "Column alignment", - "name-column-filter": "Column filter", - "name-column-width": "Column width", - "name-count-rows": "Count rows", "name-enable-pagination": "Enable pagination", "name-fields": "Fields", "name-frozen-columns": "Frozen columns", @@ -12842,8 +12791,12 @@ "name-show-table-header": "Show table header", "name-tooltip-from-field": "Tooltip from field", "name-tooltip-placement": "Tooltip placement", + "name-wrap-header-text": "Wrap header text", "placeholder-column-width": "auto", "placeholder-fields": "All Numeric Fields", + "text-wrap-options": { + "label-wrap-text": "Wrap text" + }, "tooltip-placement-options": { "label-auto": "Auto", "label-bottom": "Bottom",