diff --git a/apps/dashvalidator/Makefile b/apps/dashvalidator/Makefile index d064a3861c4..230bfd4149a 100644 --- a/apps/dashvalidator/Makefile +++ b/apps/dashvalidator/Makefile @@ -1,102 +1,9 @@ -# FIXME: This Makefile was generated by `grafana-app-sdk project init`, -# With all possible pre-made make targets, and should be customized to your project based on your needs. +include ../sdk.mk -SOURCES := $(shell find . -type f -name "*.go") -MOD_FILES := go.mod go.sum -VENDOR := vendor -COVOUT := coverage.out - -OPERATOR_DOCKERIMAGE := "dashvalidator" - -.PHONY: all -all: deps lint test build - -.PHONY: deps -deps: $(VENDOR) - -.PHONY: lint -lint: - golangci-lint run --max-same-issues=0 --max-issues-per-linter=0 - -.PHONY: test -test: - go test -count=1 -cover -covermode=atomic -coverprofile=$(COVOUT) ./... - -.PHONY: coverage -coverage: test - go tool cover -html=$(COVOUT) - -.PHONY: build -build: build/plugin build/operator - -.PHONY: build/plugin -build/plugin: build/plugin-backend build/plugin-frontend - -.PHONY: build/plugin-frontend -build/plugin-frontend: -ifeq ("$(wildcard plugin/src/plugin.json)","plugin/src/plugin.json") - @cd plugin && yarn install && yarn build -else - @echo "No plugin.json found, skipping frontend build" -endif - -.PHONY: build/plugin-backend -build/plugin-backend: -ifeq ("$(wildcard plugin/Magefile.go)","plugin/Magefile.go") - @cd plugin && mage -v -else - @echo "No Magefile.go found, skipping backend build" -endif - -.PHONY: build/operator -build/operator: - docker build -t $(OPERATOR_DOCKERIMAGE) -f cmd/operator/Dockerfile . - -.PHONY: compile/operator -compile/operator: - @go build -o "target/operator" cmd/operator/*.go - -.PHONY: generate -generate: - @grafana-app-sdk generate --source kinds --format cue --crdmanifest - -.PHONY: local/up -local/up: local/generate - @local/scripts/cluster.sh create "local/generated/k3d-config.json" - @cd local && tilt up - -.PHONY: local/generate -local/generate: - @grafana-app-sdk project local generate - -.PHONY: local/down -local/down: - @cd local && tilt down - -.PHONY: local/deploy_plugin -local/deploy_plugin: - -tilt disable grafana - @mkdir -p local/mounted-files/plugin/dist - cp -R plugin/dist/* local/mounted-files/plugin/dist/ - -tilt enable grafana - -.PHONY: local/push_operator -local/push_operator: - # Tag the docker image as part of localhost, which is what the generated k8s uses to avoid confusion with the real operator image - @docker tag "$(OPERATOR_DOCKERIMAGE):latest" "localhost/$(OPERATOR_DOCKERIMAGE):latest" - @local/scripts/push_image.sh "localhost/$(OPERATOR_DOCKERIMAGE):latest" - -.PHONY: local/clean -local/clean: local/down - @local/scripts/cluster.sh delete - -.PHONY: clean -clean: - @rm -f $(COVOUT) - @rm -rf $(VENDOR) - -.PHONY: $(VENDOR) -$(VENDOR): $(SOURCES) $(MOD_FILES) - @go mod tidy - @go mod vendor - @touch $@ +.PHONY: generate # Run Grafana App SDK code generation +generate: install-app-sdk update-app-sdk + @$(APP_SDK_BIN) generate \ + --source=./kinds/ \ + --gogenpath=./pkg/apis \ + --grouping=group \ + --defencoding=none \ No newline at end of file diff --git a/apps/dashvalidator/go.mod b/apps/dashvalidator/go.mod index 812c0fad781..0288b0665b9 100644 --- a/apps/dashvalidator/go.mod +++ b/apps/dashvalidator/go.mod @@ -1,3 +1,74 @@ module github.com/grafana/grafana/apps/dashvalidator -go 1.22 +go 1.24.0 + +require ( + github.com/grafana/grafana-app-sdk v0.48.7 + k8s.io/apimachinery v0.34.3 + k8s.io/kube-openapi v0.0.0-20251125145642-4e65d59e963e +) + +require ( + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/emicklei/go-restful/v3 v3.13.0 // indirect + github.com/fxamacker/cbor/v2 v2.9.0 // indirect + github.com/getkin/kin-openapi v0.133.0 // indirect + github.com/go-logr/logr v1.4.3 // indirect + github.com/go-openapi/jsonpointer v0.22.4 // indirect + github.com/go-openapi/jsonreference v0.21.4 // indirect + github.com/go-openapi/swag v0.25.4 // indirect + github.com/go-openapi/swag/cmdutils v0.25.4 // indirect + github.com/go-openapi/swag/conv v0.25.4 // indirect + github.com/go-openapi/swag/fileutils v0.25.4 // indirect + github.com/go-openapi/swag/jsonname v0.25.4 // indirect + github.com/go-openapi/swag/jsonutils v0.25.4 // indirect + github.com/go-openapi/swag/loading v0.25.4 // indirect + github.com/go-openapi/swag/mangling v0.25.4 // indirect + github.com/go-openapi/swag/netutils v0.25.4 // indirect + github.com/go-openapi/swag/stringutils v0.25.4 // indirect + github.com/go-openapi/swag/typeutils v0.25.4 // indirect + github.com/go-openapi/swag/yamlutils v0.25.4 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/google/gnostic-models v0.7.1 // indirect + github.com/grafana/grafana-app-sdk/logging v0.48.5 // 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.1 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect + github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 // indirect + github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 // indirect + github.com/perimeterx/marshmallow v1.1.5 // indirect + github.com/prometheus/client_golang v1.23.2 // indirect + github.com/prometheus/client_model v0.6.2 // indirect + github.com/prometheus/common v0.67.4 // indirect + github.com/prometheus/procfs v0.19.2 // indirect + github.com/woodsbury/decimal128 v1.4.0 // indirect + github.com/x448/float16 v0.8.4 // indirect + go.opentelemetry.io/otel v1.39.0 // indirect + go.opentelemetry.io/otel/trace v1.39.0 // indirect + go.yaml.in/yaml/v2 v2.4.3 // indirect + go.yaml.in/yaml/v3 v3.0.4 // indirect + golang.org/x/net v0.48.0 // indirect + golang.org/x/oauth2 v0.34.0 // indirect + golang.org/x/sys v0.39.0 // indirect + golang.org/x/term v0.38.0 // indirect + golang.org/x/text v0.32.0 // indirect + golang.org/x/time v0.14.0 // indirect + google.golang.org/protobuf v1.36.11 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + k8s.io/client-go v0.34.3 // indirect + k8s.io/klog/v2 v2.130.1 // indirect + k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 // indirect + sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect + sigs.k8s.io/randfill v1.0.0 // indirect + sigs.k8s.io/structured-merge-diff/v6 v6.3.1 // indirect + sigs.k8s.io/yaml v1.6.0 // indirect +) diff --git a/apps/dashvalidator/go.sum b/apps/dashvalidator/go.sum index 8b137891791..8b21968b91b 100644 --- a/apps/dashvalidator/go.sum +++ b/apps/dashvalidator/go.sum @@ -1 +1,209 @@ - +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emicklei/go-restful/v3 v3.13.0 h1:C4Bl2xDndpU6nJ4bc1jXd+uTmYPVUwkD6bFY/oTyCes= +github.com/emicklei/go-restful/v3 v3.13.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= +github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= +github.com/getkin/kin-openapi v0.133.0 h1:pJdmNohVIJ97r4AUFtEXRXwESr8b0bD721u/Tz6k8PQ= +github.com/getkin/kin-openapi v0.133.0/go.mod h1:boAciF6cXk5FhPqe/NQeBTeenbjqU4LhWBf09ILVvWE= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-openapi/jsonpointer v0.22.4 h1:dZtK82WlNpVLDW2jlA1YCiVJFVqkED1MegOUy9kR5T4= +github.com/go-openapi/jsonpointer v0.22.4/go.mod h1:elX9+UgznpFhgBuaMQ7iu4lvvX1nvNsesQ3oxmYTw80= +github.com/go-openapi/jsonreference v0.21.4 h1:24qaE2y9bx/q3uRK/qN+TDwbok1NhbSmGjjySRCHtC8= +github.com/go-openapi/jsonreference v0.21.4/go.mod h1:rIENPTjDbLpzQmQWCj5kKj3ZlmEh+EFVbz3RTUh30/4= +github.com/go-openapi/swag v0.25.4 h1:OyUPUFYDPDBMkqyxOTkqDYFnrhuhi9NR6QVUvIochMU= +github.com/go-openapi/swag v0.25.4/go.mod h1:zNfJ9WZABGHCFg2RnY0S4IOkAcVTzJ6z2Bi+Q4i6qFQ= +github.com/go-openapi/swag/cmdutils v0.25.4 h1:8rYhB5n6WawR192/BfUu2iVlxqVR9aRgGJP6WaBoW+4= +github.com/go-openapi/swag/cmdutils v0.25.4/go.mod h1:pdae/AFo6WxLl5L0rq87eRzVPm/XRHM3MoYgRMvG4A0= +github.com/go-openapi/swag/conv v0.25.4 h1:/Dd7p0LZXczgUcC/Ikm1+YqVzkEeCc9LnOWjfkpkfe4= +github.com/go-openapi/swag/conv v0.25.4/go.mod h1:3LXfie/lwoAv0NHoEuY1hjoFAYkvlqI/Bn5EQDD3PPU= +github.com/go-openapi/swag/fileutils v0.25.4 h1:2oI0XNW5y6UWZTC7vAxC8hmsK/tOkWXHJQH4lKjqw+Y= +github.com/go-openapi/swag/fileutils v0.25.4/go.mod h1:cdOT/PKbwcysVQ9Tpr0q20lQKH7MGhOEb6EwmHOirUk= +github.com/go-openapi/swag/jsonname v0.25.4 h1:bZH0+MsS03MbnwBXYhuTttMOqk+5KcQ9869Vye1bNHI= +github.com/go-openapi/swag/jsonname v0.25.4/go.mod h1:GPVEk9CWVhNvWhZgrnvRA6utbAltopbKwDu8mXNUMag= +github.com/go-openapi/swag/jsonutils v0.25.4 h1:VSchfbGhD4UTf4vCdR2F4TLBdLwHyUDTd1/q4i+jGZA= +github.com/go-openapi/swag/jsonutils v0.25.4/go.mod h1:7OYGXpvVFPn4PpaSdPHJBtF0iGnbEaTk8AvBkoWnaAY= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4 h1:IACsSvBhiNJwlDix7wq39SS2Fh7lUOCJRmx/4SN4sVo= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4/go.mod h1:Mt0Ost9l3cUzVv4OEZG+WSeoHwjWLnarzMePNDAOBiM= +github.com/go-openapi/swag/loading v0.25.4 h1:jN4MvLj0X6yhCDduRsxDDw1aHe+ZWoLjW+9ZQWIKn2s= +github.com/go-openapi/swag/loading v0.25.4/go.mod h1:rpUM1ZiyEP9+mNLIQUdMiD7dCETXvkkC30z53i+ftTE= +github.com/go-openapi/swag/mangling v0.25.4 h1:2b9kBJk9JvPgxr36V23FxJLdwBrpijI26Bx5JH4Hp48= +github.com/go-openapi/swag/mangling v0.25.4/go.mod h1:6dxwu6QyORHpIIApsdZgb6wBk/DPU15MdyYj/ikn0Hg= +github.com/go-openapi/swag/netutils v0.25.4 h1:Gqe6K71bGRb3ZQLusdI8p/y1KLgV4M/k+/HzVSqT8H0= +github.com/go-openapi/swag/netutils v0.25.4/go.mod h1:m2W8dtdaoX7oj9rEttLyTeEFFEBvnAx9qHd5nJEBzYg= +github.com/go-openapi/swag/stringutils v0.25.4 h1:O6dU1Rd8bej4HPA3/CLPciNBBDwZj9HiEpdVsb8B5A8= +github.com/go-openapi/swag/stringutils v0.25.4/go.mod h1:GTsRvhJW5xM5gkgiFe0fV3PUlFm0dr8vki6/VSRaZK0= +github.com/go-openapi/swag/typeutils v0.25.4 h1:1/fbZOUN472NTc39zpa+YGHn3jzHWhv42wAJSN91wRw= +github.com/go-openapi/swag/typeutils v0.25.4/go.mod h1:Ou7g//Wx8tTLS9vG0UmzfCsjZjKhpjxayRKTHXf2pTE= +github.com/go-openapi/swag/yamlutils v0.25.4 h1:6jdaeSItEUb7ioS9lFoCZ65Cne1/RZtPBZ9A56h92Sw= +github.com/go-openapi/swag/yamlutils v0.25.4/go.mod h1:MNzq1ulQu+yd8Kl7wPOut/YHAAU/H6hL91fF+E2RFwc= +github.com/go-openapi/testify/enable/yaml/v2 v2.0.2 h1:0+Y41Pz1NkbTHz8NngxTuAXxEodtNSI1WG1c/m5Akw4= +github.com/go-openapi/testify/enable/yaml/v2 v2.0.2/go.mod h1:kme83333GCtJQHXQ8UKX3IBZu6z8T5Dvy5+CW3NLUUg= +github.com/go-openapi/testify/v2 v2.0.2 h1:X999g3jeLcoY8qctY/c/Z8iBHTbwLz7R2WXd6Ub6wls= +github.com/go-openapi/testify/v2 v2.0.2/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54= +github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= +github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/google/gnostic-models v0.7.1 h1:SisTfuFKJSKM5CPZkffwi6coztzzeYUhc3v4yxLWH8c= +github.com/google/gnostic-models v0.7.1/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grafana/grafana-app-sdk v0.48.7 h1:9mF7nqkqP0QUYYDlznoOt+GIyjzj45wGfUHB32u2ZMo= +github.com/grafana/grafana-app-sdk v0.48.7/go.mod h1:DWsaaH39ZMHwSOSoUBaeW8paMrRaYsjRYlLwCJYd78k= +github.com/grafana/grafana-app-sdk/logging v0.48.5 h1:vWiTZrsSscbC5IQq2heWXm0dXhvg40nXIeUTCdE9qsc= +github.com/grafana/grafana-app-sdk/logging v0.48.5/go.mod h1:Gh/nBWnspK3oDNWtiM5qUF/fardHzOIEez+SPI3JeHA= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/mailru/easyjson v0.9.1 h1:LbtsOm5WAswyWbvTEOqhypdPeZzHavpZx96/n553mR8= +github.com/mailru/easyjson v0.9.1/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/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/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= +github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= +github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= +github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= +github.com/prometheus/common v0.67.4 h1:yR3NqWO1/UyO1w2PhUvXlGQs/PtFmoveVO0KZ4+Lvsc= +github.com/prometheus/common v0.67.4/go.mod h1:gP0fq6YjjNCLssJCQp0yk4M8W6ikLURwkdd/YKtTbyI= +github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws= +github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= +github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/woodsbury/decimal128 v1.4.0 h1:xJATj7lLu4f2oObouMt2tgGiElE5gO6mSWUjQsBgUlc= +github.com/woodsbury/decimal128 v1.4.0/go.mod h1:BP46FUrVjVhdTbKT+XuQh2xfQaGki9LMIRJSFuh6THU= +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.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= +go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= +go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= +go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= +go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= +golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= +golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw= +golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= +golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q= +golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg= +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.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= +golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= +golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= +golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= +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.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/evanphx/json-patch.v4 v4.13.0 h1:czT3CmqEaQ1aanPc5SdlgQrrEIb8w/wwCvWWnfEbYzo= +gopkg.in/evanphx/json-patch.v4 v4.13.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +k8s.io/api v0.34.3 h1:D12sTP257/jSH2vHV2EDYrb16bS7ULlHpdNdNhEw2S4= +k8s.io/api v0.34.3/go.mod h1:PyVQBF886Q5RSQZOim7DybQjAbVs8g7gwJNhGtY5MBk= +k8s.io/apimachinery v0.34.3 h1:/TB+SFEiQvN9HPldtlWOTp0hWbJ+fjU+wkxysf/aQnE= +k8s.io/apimachinery v0.34.3/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= +k8s.io/client-go v0.34.3 h1:wtYtpzy/OPNYf7WyNBTj3iUA0XaBHVqhv4Iv3tbrF5A= +k8s.io/client-go v0.34.3/go.mod h1:OxxeYagaP9Kdf78UrKLa3YZixMCfP6bgPwPwNBQBzpM= +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-20251125145642-4e65d59e963e h1:iW9ChlU0cU16w8MpVYjXk12dqQ4BPFBEgif+ap7/hqQ= +k8s.io/kube-openapi v0.0.0-20251125145642-4e65d59e963e/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ= +k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 h1:SjGebBtkBqHFOli+05xYbK8YF1Dzkbzn+gDM4X9T4Ck= +k8s.io/utils v0.0.0-20251002143259-bc988d571ff4/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg= +sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= +sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= +sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +sigs.k8s.io/structured-merge-diff/v6 v6.3.1 h1:JrhdFMqOd/+3ByqlP2I45kTOZmTRLBUm5pvRjeheg7E= +sigs.k8s.io/structured-merge-diff/v6 v6.3.1/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= +sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= +sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= diff --git a/apps/dashvalidator/kinds/manifest.cue b/apps/dashvalidator/kinds/manifest.cue index 728b95b89b7..81d16e59745 100644 --- a/apps/dashvalidator/kinds/manifest.cue +++ b/apps/dashvalidator/kinds/manifest.cue @@ -8,7 +8,7 @@ manifest: { // By default, an app's API group is LOWER(REPLACE(appName, '-', '')).ext.grafana.com, // but there are cases where this needs to be changed. // Keep in mind that changing this after an app is deployed can cause problems with clients and/or kind data. - // groupOverride: foo.ext.grafana.app + groupOverride: "dashvalidator.grafana.app" // versions is a map of versions supported by your app. Version names should follow the format "v" or // "v(alpha|beta)". Each version contains the kinds your app manages for that version. diff --git a/apps/dashvalidator/local/Tiltfile b/apps/dashvalidator/local/Tiltfile deleted file mode 100644 index 51b74ee5ff3..00000000000 --- a/apps/dashvalidator/local/Tiltfile +++ /dev/null @@ -1,77 +0,0 @@ - -# version_settings() enforces a minimum Tilt version -# https://docs.tilt.dev/api.html#api.version_settings -version_settings(constraint='>=0.22.2') - -def name(c): - return c['metadata']['name'] - -def namespace(c): - if 'namespace' in c['metadata']: - return c['metadata']['namespace'] - return '' - -def decode(yaml): - resources = decode_yaml_stream(yaml) - - # workaround a bug in decode_yaml_stream where it returns duplicates - # This bug has been fixed in Tilt v0.17.3+ - filtered = [] - names = {} - for r in resources: - if r == None: - continue - - n = '%s:%s:%s' % (name(r), r['kind'], namespace(r)) - if n in names: - continue - - names[n] = True - filtered.append(r) - - return filtered - -def get_label(o, lbl): - if 'labels' in o['metadata'] and lbl in o['metadata']['labels']: - return o['metadata']['labels'][lbl] - return '' - -def find_overlapping(o, yamls): - for elem in yamls: - if name(o) == name(elem) and o['kind'] == elem['kind'] and namespace(o) == namespace(elem): - return elem - return None - -# Parse all YAML files in our "generated" directory -yaml_objects = [] -for filename in listdir('generated'): - if filename.lower().endswith(('.yaml', '.yml')): - decoded = decode(read_file(filename)) - yaml_objects += decoded -# Next, allow for additional yamls which can optionally override the generated yamls -for filename in listdir('additional'): - if filename.lower().endswith(('.yaml', '.yml')): - decoded = decode(read_file(filename)) - for o in decoded: - present = find_overlapping(o, yaml_objects) - if present != None: - yaml_objects.remove(present) - yaml_objects += decoded - -bundle = encode_yaml_stream(yaml_objects) - -# k8s_yaml automatically creates resources in Tilt for the entities -# and will inject any images referenced in the Tiltfile when deploying -# https://docs.tilt.dev/api.html#api.k8s_yaml -k8s_yaml(bundle) - -# Group CRD's together -crds = [r for r in yaml_objects if (r['kind'] == 'CustomResourceDefinition')] -if len(crds) > 0: - k8s_resource(new_name='CustomResourceDefinitions', objects=[('%s' % name(r)) for r in yaml_objects if (r['kind'] == 'CustomResourceDefinition')], resource_deps=['uncategorized']) - -# Make webhooks dependent on all services starting -services = [('%s' % name(r)) for r in yaml_objects if r['kind'] == 'Service'] -webhooks = [r for r in yaml_objects if (r['kind'] == 'ValidatingWebhookConfiguration' or r['kind'] == 'MutatingWebhookConfiguration')] -if len(webhooks) > 0: - k8s_resource(new_name='Webhooks', objects=[('%s' % name(r)) for r in webhooks], resource_deps=services) diff --git a/apps/dashvalidator/local/config.yaml b/apps/dashvalidator/local/config.yaml deleted file mode 100644 index eee2686f4fe..00000000000 --- a/apps/dashvalidator/local/config.yaml +++ /dev/null @@ -1,47 +0,0 @@ -# Port used to bind services to localhost, for example, grafana will be available at http://grafana.k3d.localhost:9999 -port: 9999 -# Port used for the kubernetes APIServer on localhost -kubePort: 8556 -# Pre-configured datasources to install on grafana. For custom-configuration datasources, use `datasourceConfigs` -datasources: - - cortex - - tempo - - loki -# Plugin JSON data, as key/value pairs -pluginJson: - foo: bar -# Plugin Secure JSON data. By default, the `kubeconfig` and `kubenamespace` values will be added in the generated YAML. -# You can overwrite those values by specifying them here instead. -pluginSecureJson: - baz: foo -# Standalone operator docker image. Leave this empty to not deploy an operator -operatorImage: 'dashvalidator:latest' -# Governs whether the local setup generates kubernetes manifests for varying kinds of webhooks attached to your operator and each CRD -webhooks: - # The port the operator exposes an HTTPS server with the webhook endpoint(s) on - port: 8443 -# Non-standard or additional datasources you want to automatically include in grafana's provisioned list -# The actual datasources need to be set up manually (arbitrary kubernetes yamls can be added to the local setup via the 'additional' folder), -# but you can predefine the connection details so they'll be added to the local grafana. -datasourceConfigs: -# Here is an example cortex config -# - access: proxy -# editable: false -# name: "my-cortex-datasource" -# type: prometheus -# uid: "my-cortex-datasource" -# url: "http://cortex.default.svc.cluster.local:9009/api/prom" -# -# Toggle the generating of the grafana deployments, if you want to control these elsewhere -generateGrafanaDeployment: true - -# which grafana image to use -grafanaImage: grafana/grafana-enterprise:main - -# Install plugins from other sources (URLS). See https://grafana.com/docs/grafana/latest/setup-grafana/configure-docker/#install-plugins-from-other-sources -grafanaInstallPlugins: '' - -# You can mount additional volumes from the local disk (aside from the already-mounted ./local/mounted-files) by specifying them here -additionalVolumeMounts: -# - sourcePath: "./local/other" # Paths starting with a ./ or no leading slash are relative to the project root. Start with / for an absolute path -# mountPath: "/tmp/k3d/other" # The destination volume path. Best practice is to use /tmp/k3d as the starting point. The default volume mount for ./local/mounted-files is /tmp/k3d/mounted-files diff --git a/apps/dashvalidator/local/scripts/cluster.sh b/apps/dashvalidator/local/scripts/cluster.sh deleted file mode 100755 index 34652f160a8..00000000000 --- a/apps/dashvalidator/local/scripts/cluster.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env bash -set -eufo pipefail - -CLUSTER_NAME="dashvalidator" - -create_cluster() { - K3D_CONFIG="${1:-generated/k3d-config.json}" - - if ! k3d cluster list "${CLUSTER_NAME}" >/dev/null 2>&1; then - # Array of extra options to add to the k3d cluster create command - EXTRA_K3D_OPTS=() - - # Bug in k3d for btrfs filesystems workaround, see https://k3d.io/v5.2.2/faq/faq/#issues-with-btrfs - # Apple is APFS/HFS and stat has a different API, so we might as well skip that - if [[ "${OSTYPE}" != "darwin*" ]]; then - ROOTFS="$(stat -f --format="%T" "/")" - if [[ "${ROOTFS}" == "btrfs" ]]; then - EXTRA_K3D_OPTS+=("-v" "/dev/mapper:/dev/mapper") - fi - fi - - k3d cluster create "${CLUSTER_NAME}" --config "${K3D_CONFIG}" ${EXTRA_K3D_OPTS[@]+"${EXTRA_K3D_OPTS[@]}"} - else - echo "Cluster already exists" - fi -} - -delete_cluster() { - k3d cluster delete "${CLUSTER_NAME}" -} - -if [ $# -lt 1 ]; then - echo "Usage: ./cluster.sh [create|delete]" - exit 1 -fi - -if [ $1 == "create" ]; then - create_cluster $2 -elif [ $1 == "delete" ]; then - delete_cluster -else - echo "Unknown argument ${1}" -fi diff --git a/apps/dashvalidator/local/scripts/push_image.sh b/apps/dashvalidator/local/scripts/push_image.sh deleted file mode 100755 index 97abb7105de..00000000000 --- a/apps/dashvalidator/local/scripts/push_image.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env bash -set -eufo pipefail - -CLUSTER_NAME="dashvalidator" -IMAGE="$1" - -if [[ "$IMAGE" == "" ]]; then - echo "usage: push_image.sh " - exit 1 -fi - -k3d image import "${IMAGE}" -c "${CLUSTER_NAME}" diff --git a/apps/dashvalidator/pkg/apis/dashvalidator/v1alpha1/constants.go b/apps/dashvalidator/pkg/apis/dashvalidator/v1alpha1/constants.go new file mode 100644 index 00000000000..040048c51b3 --- /dev/null +++ b/apps/dashvalidator/pkg/apis/dashvalidator/v1alpha1/constants.go @@ -0,0 +1,18 @@ +package v1alpha1 + +import "k8s.io/apimachinery/pkg/runtime/schema" + +const ( + // APIGroup is the API group used by all kinds in this package + APIGroup = "dashvalidator.grafana.app" + // APIVersion is the API version used by all kinds in this package + APIVersion = "v1alpha1" +) + +var ( + // GroupVersion is a schema.GroupVersion consisting of the Group and Version constants for this package + GroupVersion = schema.GroupVersion{ + Group: APIGroup, + Version: APIVersion, + } +) diff --git a/apps/dashvalidator/pkg/apis/dashvalidator/v1alpha1/dashboardcompatibilityscore_client_gen.go b/apps/dashvalidator/pkg/apis/dashvalidator/v1alpha1/dashboardcompatibilityscore_client_gen.go new file mode 100644 index 00000000000..64fe3c944f3 --- /dev/null +++ b/apps/dashvalidator/pkg/apis/dashvalidator/v1alpha1/dashboardcompatibilityscore_client_gen.go @@ -0,0 +1,99 @@ +package v1alpha1 + +import ( + "context" + + "github.com/grafana/grafana-app-sdk/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +type DashboardCompatibilityScoreClient struct { + client *resource.TypedClient[*DashboardCompatibilityScore, *DashboardCompatibilityScoreList] +} + +func NewDashboardCompatibilityScoreClient(client resource.Client) *DashboardCompatibilityScoreClient { + return &DashboardCompatibilityScoreClient{ + client: resource.NewTypedClient[*DashboardCompatibilityScore, *DashboardCompatibilityScoreList](client, DashboardCompatibilityScoreKind()), + } +} + +func NewDashboardCompatibilityScoreClientFromGenerator(generator resource.ClientGenerator) (*DashboardCompatibilityScoreClient, error) { + c, err := generator.ClientFor(DashboardCompatibilityScoreKind()) + if err != nil { + return nil, err + } + return NewDashboardCompatibilityScoreClient(c), nil +} + +func (c *DashboardCompatibilityScoreClient) Get(ctx context.Context, identifier resource.Identifier) (*DashboardCompatibilityScore, error) { + return c.client.Get(ctx, identifier) +} + +func (c *DashboardCompatibilityScoreClient) List(ctx context.Context, namespace string, opts resource.ListOptions) (*DashboardCompatibilityScoreList, error) { + return c.client.List(ctx, namespace, opts) +} + +func (c *DashboardCompatibilityScoreClient) ListAll(ctx context.Context, namespace string, opts resource.ListOptions) (*DashboardCompatibilityScoreList, error) { + resp, err := c.client.List(ctx, namespace, resource.ListOptions{ + ResourceVersion: opts.ResourceVersion, + Limit: opts.Limit, + LabelFilters: opts.LabelFilters, + FieldSelectors: opts.FieldSelectors, + }) + if err != nil { + return nil, err + } + for resp.GetContinue() != "" { + page, err := c.client.List(ctx, namespace, resource.ListOptions{ + Continue: resp.GetContinue(), + ResourceVersion: opts.ResourceVersion, + Limit: opts.Limit, + LabelFilters: opts.LabelFilters, + FieldSelectors: opts.FieldSelectors, + }) + if err != nil { + return nil, err + } + resp.SetContinue(page.GetContinue()) + resp.SetResourceVersion(page.GetResourceVersion()) + resp.SetItems(append(resp.GetItems(), page.GetItems()...)) + } + return resp, nil +} + +func (c *DashboardCompatibilityScoreClient) Create(ctx context.Context, obj *DashboardCompatibilityScore, opts resource.CreateOptions) (*DashboardCompatibilityScore, error) { + // Make sure apiVersion and kind are set + obj.APIVersion = GroupVersion.Identifier() + obj.Kind = DashboardCompatibilityScoreKind().Kind() + return c.client.Create(ctx, obj, opts) +} + +func (c *DashboardCompatibilityScoreClient) Update(ctx context.Context, obj *DashboardCompatibilityScore, opts resource.UpdateOptions) (*DashboardCompatibilityScore, error) { + return c.client.Update(ctx, obj, opts) +} + +func (c *DashboardCompatibilityScoreClient) Patch(ctx context.Context, identifier resource.Identifier, req resource.PatchRequest, opts resource.PatchOptions) (*DashboardCompatibilityScore, error) { + return c.client.Patch(ctx, identifier, req, opts) +} + +func (c *DashboardCompatibilityScoreClient) UpdateStatus(ctx context.Context, identifier resource.Identifier, newStatus DashboardCompatibilityScoreStatus, opts resource.UpdateOptions) (*DashboardCompatibilityScore, error) { + return c.client.Update(ctx, &DashboardCompatibilityScore{ + TypeMeta: metav1.TypeMeta{ + Kind: DashboardCompatibilityScoreKind().Kind(), + APIVersion: GroupVersion.Identifier(), + }, + ObjectMeta: metav1.ObjectMeta{ + ResourceVersion: opts.ResourceVersion, + Namespace: identifier.Namespace, + Name: identifier.Name, + }, + Status: newStatus, + }, resource.UpdateOptions{ + Subresource: "status", + ResourceVersion: opts.ResourceVersion, + }) +} + +func (c *DashboardCompatibilityScoreClient) Delete(ctx context.Context, identifier resource.Identifier, opts resource.DeleteOptions) error { + return c.client.Delete(ctx, identifier, opts) +} diff --git a/apps/dashvalidator/pkg/apis/dashvalidator/v1alpha1/dashboardcompatibilityscore_codec_gen.go b/apps/dashvalidator/pkg/apis/dashvalidator/v1alpha1/dashboardcompatibilityscore_codec_gen.go new file mode 100644 index 00000000000..ccdf37d4c32 --- /dev/null +++ b/apps/dashvalidator/pkg/apis/dashvalidator/v1alpha1/dashboardcompatibilityscore_codec_gen.go @@ -0,0 +1,28 @@ +// +// Code generated by grafana-app-sdk. DO NOT EDIT. +// + +package v1alpha1 + +import ( + "encoding/json" + "io" + + "github.com/grafana/grafana-app-sdk/resource" +) + +// DashboardCompatibilityScoreJSONCodec is an implementation of resource.Codec for kubernetes JSON encoding +type DashboardCompatibilityScoreJSONCodec struct{} + +// Read reads JSON-encoded bytes from `reader` and unmarshals them into `into` +func (*DashboardCompatibilityScoreJSONCodec) Read(reader io.Reader, into resource.Object) error { + return json.NewDecoder(reader).Decode(into) +} + +// Write writes JSON-encoded bytes into `writer` marshaled from `from` +func (*DashboardCompatibilityScoreJSONCodec) Write(writer io.Writer, from resource.Object) error { + return json.NewEncoder(writer).Encode(from) +} + +// Interface compliance checks +var _ resource.Codec = &DashboardCompatibilityScoreJSONCodec{} diff --git a/apps/dashvalidator/pkg/apis/dashvalidator/v1alpha1/dashboardcompatibilityscore_metadata_gen.go b/apps/dashvalidator/pkg/apis/dashvalidator/v1alpha1/dashboardcompatibilityscore_metadata_gen.go new file mode 100644 index 00000000000..32246320752 --- /dev/null +++ b/apps/dashvalidator/pkg/apis/dashvalidator/v1alpha1/dashboardcompatibilityscore_metadata_gen.go @@ -0,0 +1,31 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +package v1alpha1 + +import ( + time "time" +) + +// metadata contains embedded CommonMetadata and can be extended with custom string fields +// TODO: use CommonMetadata instead of redefining here; currently needs to be defined here +// without external reference as using the CommonMetadata reference breaks thema codegen. +type DashboardCompatibilityScoreMetadata struct { + UpdateTimestamp time.Time `json:"updateTimestamp"` + CreatedBy string `json:"createdBy"` + Uid string `json:"uid"` + CreationTimestamp time.Time `json:"creationTimestamp"` + DeletionTimestamp *time.Time `json:"deletionTimestamp,omitempty"` + Finalizers []string `json:"finalizers"` + ResourceVersion string `json:"resourceVersion"` + Generation int64 `json:"generation"` + UpdatedBy string `json:"updatedBy"` + Labels map[string]string `json:"labels"` +} + +// NewDashboardCompatibilityScoreMetadata creates a new DashboardCompatibilityScoreMetadata object. +func NewDashboardCompatibilityScoreMetadata() *DashboardCompatibilityScoreMetadata { + return &DashboardCompatibilityScoreMetadata{ + Finalizers: []string{}, + Labels: map[string]string{}, + } +} diff --git a/apps/dashvalidator/pkg/apis/dashvalidator/v1alpha1/dashboardcompatibilityscore_object_gen.go b/apps/dashvalidator/pkg/apis/dashvalidator/v1alpha1/dashboardcompatibilityscore_object_gen.go new file mode 100644 index 00000000000..3c0f190f4f3 --- /dev/null +++ b/apps/dashvalidator/pkg/apis/dashvalidator/v1alpha1/dashboardcompatibilityscore_object_gen.go @@ -0,0 +1,326 @@ +// +// Code generated by grafana-app-sdk. DO NOT EDIT. +// + +package v1alpha1 + +import ( + "fmt" + "github.com/grafana/grafana-app-sdk/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" + "time" +) + +// +k8s:openapi-gen=true +type DashboardCompatibilityScore struct { + metav1.TypeMeta `json:",inline" yaml:",inline"` + metav1.ObjectMeta `json:"metadata" yaml:"metadata"` + + // Spec is the spec of the DashboardCompatibilityScore + Spec DashboardCompatibilityScoreSpec `json:"spec" yaml:"spec"` + + Status DashboardCompatibilityScoreStatus `json:"status" yaml:"status"` +} + +func NewDashboardCompatibilityScore() *DashboardCompatibilityScore { + return &DashboardCompatibilityScore{ + Spec: *NewDashboardCompatibilityScoreSpec(), + Status: *NewDashboardCompatibilityScoreStatus(), + } +} + +func (o *DashboardCompatibilityScore) GetSpec() any { + return o.Spec +} + +func (o *DashboardCompatibilityScore) SetSpec(spec any) error { + cast, ok := spec.(DashboardCompatibilityScoreSpec) + if !ok { + return fmt.Errorf("cannot set spec type %#v, not of type Spec", spec) + } + o.Spec = cast + return nil +} + +func (o *DashboardCompatibilityScore) GetSubresources() map[string]any { + return map[string]any{ + "status": o.Status, + } +} + +func (o *DashboardCompatibilityScore) GetSubresource(name string) (any, bool) { + switch name { + case "status": + return o.Status, true + default: + return nil, false + } +} + +func (o *DashboardCompatibilityScore) SetSubresource(name string, value any) error { + switch name { + case "status": + cast, ok := value.(DashboardCompatibilityScoreStatus) + if !ok { + return fmt.Errorf("cannot set status type %#v, not of type DashboardCompatibilityScoreStatus", value) + } + o.Status = cast + return nil + default: + return fmt.Errorf("subresource '%s' does not exist", name) + } +} + +func (o *DashboardCompatibilityScore) GetStaticMetadata() resource.StaticMetadata { + gvk := o.GroupVersionKind() + return resource.StaticMetadata{ + Name: o.ObjectMeta.Name, + Namespace: o.ObjectMeta.Namespace, + Group: gvk.Group, + Version: gvk.Version, + Kind: gvk.Kind, + } +} + +func (o *DashboardCompatibilityScore) SetStaticMetadata(metadata resource.StaticMetadata) { + o.Name = metadata.Name + o.Namespace = metadata.Namespace + o.SetGroupVersionKind(schema.GroupVersionKind{ + Group: metadata.Group, + Version: metadata.Version, + Kind: metadata.Kind, + }) +} + +func (o *DashboardCompatibilityScore) GetCommonMetadata() resource.CommonMetadata { + dt := o.DeletionTimestamp + var deletionTimestamp *time.Time + if dt != nil { + deletionTimestamp = &dt.Time + } + // Legacy ExtraFields support + extraFields := make(map[string]any) + if o.Annotations != nil { + extraFields["annotations"] = o.Annotations + } + if o.ManagedFields != nil { + extraFields["managedFields"] = o.ManagedFields + } + if o.OwnerReferences != nil { + extraFields["ownerReferences"] = o.OwnerReferences + } + return resource.CommonMetadata{ + UID: string(o.UID), + ResourceVersion: o.ResourceVersion, + Generation: o.Generation, + Labels: o.Labels, + CreationTimestamp: o.CreationTimestamp.Time, + DeletionTimestamp: deletionTimestamp, + Finalizers: o.Finalizers, + UpdateTimestamp: o.GetUpdateTimestamp(), + CreatedBy: o.GetCreatedBy(), + UpdatedBy: o.GetUpdatedBy(), + ExtraFields: extraFields, + } +} + +func (o *DashboardCompatibilityScore) SetCommonMetadata(metadata resource.CommonMetadata) { + o.UID = types.UID(metadata.UID) + o.ResourceVersion = metadata.ResourceVersion + o.Generation = metadata.Generation + o.Labels = metadata.Labels + o.CreationTimestamp = metav1.NewTime(metadata.CreationTimestamp) + if metadata.DeletionTimestamp != nil { + dt := metav1.NewTime(*metadata.DeletionTimestamp) + o.DeletionTimestamp = &dt + } else { + o.DeletionTimestamp = nil + } + o.Finalizers = metadata.Finalizers + if o.Annotations == nil { + o.Annotations = make(map[string]string) + } + if !metadata.UpdateTimestamp.IsZero() { + o.SetUpdateTimestamp(metadata.UpdateTimestamp) + } + if metadata.CreatedBy != "" { + o.SetCreatedBy(metadata.CreatedBy) + } + if metadata.UpdatedBy != "" { + o.SetUpdatedBy(metadata.UpdatedBy) + } + // Legacy support for setting Annotations, ManagedFields, and OwnerReferences via ExtraFields + if metadata.ExtraFields != nil { + if annotations, ok := metadata.ExtraFields["annotations"]; ok { + if cast, ok := annotations.(map[string]string); ok { + o.Annotations = cast + } + } + if managedFields, ok := metadata.ExtraFields["managedFields"]; ok { + if cast, ok := managedFields.([]metav1.ManagedFieldsEntry); ok { + o.ManagedFields = cast + } + } + if ownerReferences, ok := metadata.ExtraFields["ownerReferences"]; ok { + if cast, ok := ownerReferences.([]metav1.OwnerReference); ok { + o.OwnerReferences = cast + } + } + } +} + +func (o *DashboardCompatibilityScore) GetCreatedBy() string { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + return o.ObjectMeta.Annotations["grafana.com/createdBy"] +} + +func (o *DashboardCompatibilityScore) SetCreatedBy(createdBy string) { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + o.ObjectMeta.Annotations["grafana.com/createdBy"] = createdBy +} + +func (o *DashboardCompatibilityScore) GetUpdateTimestamp() time.Time { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + parsed, _ := time.Parse(time.RFC3339, o.ObjectMeta.Annotations["grafana.com/updateTimestamp"]) + return parsed +} + +func (o *DashboardCompatibilityScore) SetUpdateTimestamp(updateTimestamp time.Time) { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + o.ObjectMeta.Annotations["grafana.com/updateTimestamp"] = updateTimestamp.Format(time.RFC3339) +} + +func (o *DashboardCompatibilityScore) GetUpdatedBy() string { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + return o.ObjectMeta.Annotations["grafana.com/updatedBy"] +} + +func (o *DashboardCompatibilityScore) SetUpdatedBy(updatedBy string) { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + o.ObjectMeta.Annotations["grafana.com/updatedBy"] = updatedBy +} + +func (o *DashboardCompatibilityScore) Copy() resource.Object { + return resource.CopyObject(o) +} + +func (o *DashboardCompatibilityScore) DeepCopyObject() runtime.Object { + return o.Copy() +} + +func (o *DashboardCompatibilityScore) DeepCopy() *DashboardCompatibilityScore { + cpy := &DashboardCompatibilityScore{} + o.DeepCopyInto(cpy) + return cpy +} + +func (o *DashboardCompatibilityScore) DeepCopyInto(dst *DashboardCompatibilityScore) { + dst.TypeMeta.APIVersion = o.TypeMeta.APIVersion + dst.TypeMeta.Kind = o.TypeMeta.Kind + o.ObjectMeta.DeepCopyInto(&dst.ObjectMeta) + o.Spec.DeepCopyInto(&dst.Spec) + o.Status.DeepCopyInto(&dst.Status) +} + +// Interface compliance compile-time check +var _ resource.Object = &DashboardCompatibilityScore{} + +// +k8s:openapi-gen=true +type DashboardCompatibilityScoreList struct { + metav1.TypeMeta `json:",inline" yaml:",inline"` + metav1.ListMeta `json:"metadata" yaml:"metadata"` + Items []DashboardCompatibilityScore `json:"items" yaml:"items"` +} + +func (o *DashboardCompatibilityScoreList) DeepCopyObject() runtime.Object { + return o.Copy() +} + +func (o *DashboardCompatibilityScoreList) Copy() resource.ListObject { + cpy := &DashboardCompatibilityScoreList{ + TypeMeta: o.TypeMeta, + Items: make([]DashboardCompatibilityScore, len(o.Items)), + } + o.ListMeta.DeepCopyInto(&cpy.ListMeta) + for i := 0; i < len(o.Items); i++ { + if item, ok := o.Items[i].Copy().(*DashboardCompatibilityScore); ok { + cpy.Items[i] = *item + } + } + return cpy +} + +func (o *DashboardCompatibilityScoreList) GetItems() []resource.Object { + items := make([]resource.Object, len(o.Items)) + for i := 0; i < len(o.Items); i++ { + items[i] = &o.Items[i] + } + return items +} + +func (o *DashboardCompatibilityScoreList) SetItems(items []resource.Object) { + o.Items = make([]DashboardCompatibilityScore, len(items)) + for i := 0; i < len(items); i++ { + o.Items[i] = *items[i].(*DashboardCompatibilityScore) + } +} + +func (o *DashboardCompatibilityScoreList) DeepCopy() *DashboardCompatibilityScoreList { + cpy := &DashboardCompatibilityScoreList{} + o.DeepCopyInto(cpy) + return cpy +} + +func (o *DashboardCompatibilityScoreList) DeepCopyInto(dst *DashboardCompatibilityScoreList) { + resource.CopyObjectInto(dst, o) +} + +// Interface compliance compile-time check +var _ resource.ListObject = &DashboardCompatibilityScoreList{} + +// Copy methods for all subresource types + +// DeepCopy creates a full deep copy of Spec +func (s *DashboardCompatibilityScoreSpec) DeepCopy() *DashboardCompatibilityScoreSpec { + cpy := &DashboardCompatibilityScoreSpec{} + s.DeepCopyInto(cpy) + return cpy +} + +// DeepCopyInto deep copies Spec into another Spec object +func (s *DashboardCompatibilityScoreSpec) DeepCopyInto(dst *DashboardCompatibilityScoreSpec) { + resource.CopyObjectInto(dst, s) +} + +// DeepCopy creates a full deep copy of DashboardCompatibilityScoreStatus +func (s *DashboardCompatibilityScoreStatus) DeepCopy() *DashboardCompatibilityScoreStatus { + cpy := &DashboardCompatibilityScoreStatus{} + s.DeepCopyInto(cpy) + return cpy +} + +// DeepCopyInto deep copies DashboardCompatibilityScoreStatus into another DashboardCompatibilityScoreStatus object +func (s *DashboardCompatibilityScoreStatus) DeepCopyInto(dst *DashboardCompatibilityScoreStatus) { + resource.CopyObjectInto(dst, s) +} diff --git a/apps/dashvalidator/pkg/apis/dashvalidator/v1alpha1/dashboardcompatibilityscore_schema_gen.go b/apps/dashvalidator/pkg/apis/dashvalidator/v1alpha1/dashboardcompatibilityscore_schema_gen.go new file mode 100644 index 00000000000..9edfd1bd318 --- /dev/null +++ b/apps/dashvalidator/pkg/apis/dashvalidator/v1alpha1/dashboardcompatibilityscore_schema_gen.go @@ -0,0 +1,34 @@ +// +// Code generated by grafana-app-sdk. DO NOT EDIT. +// + +package v1alpha1 + +import ( + "github.com/grafana/grafana-app-sdk/resource" +) + +// schema is unexported to prevent accidental overwrites +var ( + schemaDashboardCompatibilityScore = resource.NewSimpleSchema("dashvalidator.grafana.app", "v1alpha1", NewDashboardCompatibilityScore(), &DashboardCompatibilityScoreList{}, resource.WithKind("DashboardCompatibilityScore"), + resource.WithPlural("dashboardcompatibilityscores"), resource.WithScope(resource.NamespacedScope)) + kindDashboardCompatibilityScore = resource.Kind{ + Schema: schemaDashboardCompatibilityScore, + Codecs: map[resource.KindEncoding]resource.Codec{ + resource.KindEncodingJSON: &DashboardCompatibilityScoreJSONCodec{}, + }, + } +) + +// Kind returns a resource.Kind for this Schema with a JSON codec +func DashboardCompatibilityScoreKind() resource.Kind { + return kindDashboardCompatibilityScore +} + +// Schema returns a resource.SimpleSchema representation of DashboardCompatibilityScore +func DashboardCompatibilityScoreSchema() *resource.SimpleSchema { + return schemaDashboardCompatibilityScore +} + +// Interface compliance checks +var _ resource.Schema = kindDashboardCompatibilityScore diff --git a/apps/dashvalidator/pkg/apis/dashvalidator/v1alpha1/dashboardcompatibilityscore_spec_gen.go b/apps/dashvalidator/pkg/apis/dashvalidator/v1alpha1/dashboardcompatibilityscore_spec_gen.go new file mode 100644 index 00000000000..615adb20c6b --- /dev/null +++ b/apps/dashvalidator/pkg/apis/dashvalidator/v1alpha1/dashboardcompatibilityscore_spec_gen.go @@ -0,0 +1,48 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +package v1alpha1 + +// DataSourceMapping specifies a datasource to validate dashboard queries against. +// Maps logical datasource references in the dashboard to actual datasource instances. +// +k8s:openapi-gen=true +type DashboardCompatibilityScoreDataSourceMapping struct { + // Unique identifier of the datasource instance. + // Example: "prometheus-prod-us-west" + Uid string `json:"uid"` + // Type of datasource plugin. + // MVP: Only "prometheus" supported. + // Future: "mysql", "postgres", "elasticsearch", etc. + Type string `json:"type"` + // Optional human-readable name for display in results. + // If not provided, UID will be used in error messages. + // Example: "Production Prometheus (US-West)" + Name *string `json:"name,omitempty"` +} + +// NewDashboardCompatibilityScoreDataSourceMapping creates a new DashboardCompatibilityScoreDataSourceMapping object. +func NewDashboardCompatibilityScoreDataSourceMapping() *DashboardCompatibilityScoreDataSourceMapping { + return &DashboardCompatibilityScoreDataSourceMapping{} +} + +// +k8s:openapi-gen=true +type DashboardCompatibilityScoreSpec struct { + // Complete dashboard JSON object to validate. + // Must be a v1 dashboard schema (contains "panels" array). + // v2 dashboards (with "elements" structure) are not yet supported. + DashboardJson map[string]interface{} `json:"dashboardJson"` + // Array of datasources to validate against. + // The validator will check dashboard queries against each datasource + // and provide per-datasource compatibility results. + // + // MVP: Only single datasource supported (array length = 1), Prometheus type only. + // Future: Will support multiple datasources for dashboards with mixed queries. + DatasourceMappings []DashboardCompatibilityScoreDataSourceMapping `json:"datasourceMappings"` +} + +// NewDashboardCompatibilityScoreSpec creates a new DashboardCompatibilityScoreSpec object. +func NewDashboardCompatibilityScoreSpec() *DashboardCompatibilityScoreSpec { + return &DashboardCompatibilityScoreSpec{ + DashboardJson: map[string]interface{}{}, + DatasourceMappings: []DashboardCompatibilityScoreDataSourceMapping{}, + } +} diff --git a/apps/dashvalidator/pkg/apis/dashvalidator/v1alpha1/dashboardcompatibilityscore_status_gen.go b/apps/dashvalidator/pkg/apis/dashvalidator/v1alpha1/dashboardcompatibilityscore_status_gen.go new file mode 100644 index 00000000000..e0259dbd6bc --- /dev/null +++ b/apps/dashvalidator/pkg/apis/dashvalidator/v1alpha1/dashboardcompatibilityscore_status_gen.go @@ -0,0 +1,151 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +package v1alpha1 + +// DataSourceResult contains validation results for a single datasource. +// Provides aggregate statistics and per-query breakdown of compatibility. +// +k8s:openapi-gen=true +type DashboardCompatibilityScoreDataSourceResult struct { + // Datasource UID that was validated (matches DataSourceMapping.uid) + Uid string `json:"uid"` + // Datasource type (matches DataSourceMapping.type) + Type string `json:"type"` + // Optional display name (matches DataSourceMapping.name if provided) + Name *string `json:"name,omitempty"` + // Total number of queries in the dashboard targeting this datasource. + // Includes all panel targets/queries that reference this datasource. + TotalQueries int64 `json:"totalQueries"` + // Number of queries successfully validated. + // May be less than totalQueries if some queries couldn't be parsed. + CheckedQueries int64 `json:"checkedQueries"` + // Total number of unique metrics/identifiers referenced across all queries. + // For Prometheus: metric names extracted from PromQL expressions. + // For SQL datasources: table and column names. + TotalMetrics int64 `json:"totalMetrics"` + // Number of metrics that exist in the datasource schema. + // foundMetrics <= totalMetrics + FoundMetrics int64 `json:"foundMetrics"` + // Array of metric names that were referenced but don't exist. + // Useful for debugging why a dashboard shows "no data". + // Example for Prometheus: ["http_requests_total", "api_latency_seconds"] + MissingMetrics []string `json:"missingMetrics"` + // Per-query breakdown showing which specific queries have issues. + // One entry per query target (refId: "A", "B", "C", etc.) in each panel. + // Allows pinpointing exactly which panel/query needs fixing. + QueryBreakdown []DashboardCompatibilityScoreQueryBreakdown `json:"queryBreakdown"` + // Overall compatibility score for this datasource (0-100). + // Calculated as: (foundMetrics / totalMetrics) * 100 + // Used to calculate the global compatibilityScore in status. + CompatibilityScore float64 `json:"compatibilityScore"` +} + +// NewDashboardCompatibilityScoreDataSourceResult creates a new DashboardCompatibilityScoreDataSourceResult object. +func NewDashboardCompatibilityScoreDataSourceResult() *DashboardCompatibilityScoreDataSourceResult { + return &DashboardCompatibilityScoreDataSourceResult{ + MissingMetrics: []string{}, + QueryBreakdown: []DashboardCompatibilityScoreQueryBreakdown{}, + } +} + +// QueryBreakdown provides compatibility details for a single query within a panel. +// Granular per-query results allow users to identify exactly which queries need fixing. +// +// Note: A panel can have multiple queries (refId: "A", "B", "C", etc.), +// so there may be multiple QueryBreakdown entries for the same panelID. +// +k8s:openapi-gen=true +type DashboardCompatibilityScoreQueryBreakdown struct { + // Human-readable panel title for context. + // Example: "CPU Usage", "Request Rate" + PanelTitle string `json:"panelTitle"` + // Numeric panel ID from dashboard JSON. + // Used to correlate with dashboard structure. + PanelID int64 `json:"panelID"` + // Query identifier within the panel. + // Values: "A", "B", "C", etc. (from panel.targets[].refId) + // Uniquely identifies which query in a multi-query panel this refers to. + QueryRefId string `json:"queryRefId"` + // Number of unique metrics referenced in this specific query. + // For Prometheus: metrics extracted from the PromQL expr. + // Example: rate(http_requests_total[5m]) references 1 metric. + TotalMetrics int64 `json:"totalMetrics"` + // Number of those metrics that exist in the datasource. + // foundMetrics <= totalMetrics + FoundMetrics int64 `json:"foundMetrics"` + // Array of missing metric names specific to this query. + // Helps identify exactly which part of a query expression will fail. + // Empty array means query is fully compatible. + MissingMetrics []string `json:"missingMetrics"` + // Compatibility percentage for this individual query (0-100). + // Calculated as: (foundMetrics / totalMetrics) * 100 + // 100 = query will work perfectly, 0 = query will return no data. + CompatibilityScore float64 `json:"compatibilityScore"` +} + +// NewDashboardCompatibilityScoreQueryBreakdown creates a new DashboardCompatibilityScoreQueryBreakdown object. +func NewDashboardCompatibilityScoreQueryBreakdown() *DashboardCompatibilityScoreQueryBreakdown { + return &DashboardCompatibilityScoreQueryBreakdown{ + MissingMetrics: []string{}, + } +} + +// +k8s:openapi-gen=true +type DashboardCompatibilityScorestatusOperatorState struct { + // lastEvaluation is the ResourceVersion last evaluated + LastEvaluation string `json:"lastEvaluation"` + // state describes the state of the lastEvaluation. + // It is limited to three possible states for machine evaluation. + State DashboardCompatibilityScoreStatusOperatorStateState `json:"state"` + // descriptiveState is an optional more descriptive state field which has no requirements on format + DescriptiveState *string `json:"descriptiveState,omitempty"` + // details contains any extra information that is operator-specific + Details map[string]interface{} `json:"details,omitempty"` +} + +// NewDashboardCompatibilityScorestatusOperatorState creates a new DashboardCompatibilityScorestatusOperatorState object. +func NewDashboardCompatibilityScorestatusOperatorState() *DashboardCompatibilityScorestatusOperatorState { + return &DashboardCompatibilityScorestatusOperatorState{} +} + +// +k8s:openapi-gen=true +type DashboardCompatibilityScoreStatus struct { + // Overall compatibility score across all datasources (0-100). + // Calculated as: (total found metrics / total referenced metrics) * 100 + // + // Score interpretation: + // - 100: Perfect compatibility, all queries will work + // - 80-99: Excellent, minor missing metrics + // - 50-79: Fair, significant missing metrics + // - 0-49: Poor, most queries will fail + CompatibilityScore float64 `json:"compatibilityScore"` + // Per-datasource validation results. + // Array length matches spec.datasourceMappings. + // Each element contains detailed metrics and query-level breakdown. + DatasourceResults []DashboardCompatibilityScoreDataSourceResult `json:"datasourceResults"` + // ISO 8601 timestamp of when validation was last performed. + // Example: "2024-01-15T10:30:00Z" + LastChecked *string `json:"lastChecked,omitempty"` + // operatorStates is a map of operator ID to operator state evaluations. + // Any operator which consumes this kind SHOULD add its state evaluation information to this field. + OperatorStates map[string]DashboardCompatibilityScorestatusOperatorState `json:"operatorStates,omitempty"` + // Human-readable summary of validation result. + // Examples: "All queries compatible", "3 missing metrics found" + Message *string `json:"message,omitempty"` + // additionalFields is reserved for future use + AdditionalFields map[string]interface{} `json:"additionalFields,omitempty"` +} + +// NewDashboardCompatibilityScoreStatus creates a new DashboardCompatibilityScoreStatus object. +func NewDashboardCompatibilityScoreStatus() *DashboardCompatibilityScoreStatus { + return &DashboardCompatibilityScoreStatus{ + DatasourceResults: []DashboardCompatibilityScoreDataSourceResult{}, + } +} + +// +k8s:openapi-gen=true +type DashboardCompatibilityScoreStatusOperatorStateState string + +const ( + DashboardCompatibilityScoreStatusOperatorStateStateSuccess DashboardCompatibilityScoreStatusOperatorStateState = "success" + DashboardCompatibilityScoreStatusOperatorStateStateInProgress DashboardCompatibilityScoreStatusOperatorStateState = "in_progress" + DashboardCompatibilityScoreStatusOperatorStateStateFailed DashboardCompatibilityScoreStatusOperatorStateState = "failed" +) diff --git a/apps/dashvalidator/pkg/apis/manifestdata/dashvalidator_manifest.go b/apps/dashvalidator/pkg/apis/manifestdata/dashvalidator_manifest.go new file mode 100644 index 00000000000..acc1d9959c7 --- /dev/null +++ b/apps/dashvalidator/pkg/apis/manifestdata/dashvalidator_manifest.go @@ -0,0 +1,124 @@ +// +// This file is generated by grafana-app-sdk +// DO NOT EDIT +// + +package manifestdata + +import ( + "encoding/json" + "fmt" + "strings" + + "github.com/grafana/grafana-app-sdk/app" + "github.com/grafana/grafana-app-sdk/resource" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/kube-openapi/pkg/spec3" + "k8s.io/kube-openapi/pkg/validation/spec" + + v1alpha1 "github.com/grafana/grafana/apps/dashvalidator/pkg/apis/dashvalidator/v1alpha1" +) + +var ( + rawSchemaDashboardCompatibilityScorev1alpha1 = []byte(`{"DashboardCompatibilityScore":{"properties":{"spec":{"$ref":"#/components/schemas/spec"},"status":{"$ref":"#/components/schemas/status"}},"required":["spec"]},"DataSourceMapping":{"additionalProperties":false,"description":"DataSourceMapping specifies a datasource to validate dashboard queries against.\nMaps logical datasource references in the dashboard to actual datasource instances.","properties":{"name":{"description":"Optional human-readable name for display in results.\nIf not provided, UID will be used in error messages.\nExample: \"Production Prometheus (US-West)\"","type":"string"},"type":{"description":"Type of datasource plugin.\nMVP: Only \"prometheus\" supported.\nFuture: \"mysql\", \"postgres\", \"elasticsearch\", etc.","type":"string"},"uid":{"description":"Unique identifier of the datasource instance.\nExample: \"prometheus-prod-us-west\"","type":"string"}},"required":["uid","type"],"type":"object"},"DataSourceResult":{"additionalProperties":false,"description":"DataSourceResult contains validation results for a single datasource.\nProvides aggregate statistics and per-query breakdown of compatibility.","properties":{"checkedQueries":{"description":"Number of queries successfully validated.\nMay be less than totalQueries if some queries couldn't be parsed.","type":"integer"},"compatibilityScore":{"description":"Overall compatibility score for this datasource (0-100).\nCalculated as: (foundMetrics / totalMetrics) * 100\nUsed to calculate the global compatibilityScore in status.","type":"number"},"foundMetrics":{"description":"Number of metrics that exist in the datasource schema.\nfoundMetrics \u003c= totalMetrics","type":"integer"},"missingMetrics":{"description":"Array of metric names that were referenced but don't exist.\nUseful for debugging why a dashboard shows \"no data\".\nExample for Prometheus: [\"http_requests_total\", \"api_latency_seconds\"]","items":{"type":"string"},"type":"array"},"name":{"description":"Optional display name (matches DataSourceMapping.name if provided)","type":"string"},"queryBreakdown":{"description":"Per-query breakdown showing which specific queries have issues.\nOne entry per query target (refId: \"A\", \"B\", \"C\", etc.) in each panel.\nAllows pinpointing exactly which panel/query needs fixing.","items":{"$ref":"#/components/schemas/QueryBreakdown"},"type":"array"},"totalMetrics":{"description":"Total number of unique metrics/identifiers referenced across all queries.\nFor Prometheus: metric names extracted from PromQL expressions.\nFor SQL datasources: table and column names.","type":"integer"},"totalQueries":{"description":"Total number of queries in the dashboard targeting this datasource.\nIncludes all panel targets/queries that reference this datasource.","type":"integer"},"type":{"description":"Datasource type (matches DataSourceMapping.type)","type":"string"},"uid":{"description":"Datasource UID that was validated (matches DataSourceMapping.uid)","type":"string"}},"required":["uid","type","totalQueries","checkedQueries","totalMetrics","foundMetrics","missingMetrics","queryBreakdown","compatibilityScore"],"type":"object"},"OperatorState":{"additionalProperties":false,"properties":{"descriptiveState":{"description":"descriptiveState is an optional more descriptive state field which has no requirements on format","type":"string"},"details":{"additionalProperties":{"additionalProperties":{},"type":"object"},"description":"details contains any extra information that is operator-specific","type":"object"},"lastEvaluation":{"description":"lastEvaluation is the ResourceVersion last evaluated","type":"string"},"state":{"description":"state describes the state of the lastEvaluation.\nIt is limited to three possible states for machine evaluation.","enum":["success","in_progress","failed"],"type":"string"}},"required":["lastEvaluation","state"],"type":"object"},"QueryBreakdown":{"additionalProperties":false,"description":"QueryBreakdown provides compatibility details for a single query within a panel.\nGranular per-query results allow users to identify exactly which queries need fixing.\n\nNote: A panel can have multiple queries (refId: \"A\", \"B\", \"C\", etc.),\nso there may be multiple QueryBreakdown entries for the same panelID.","properties":{"compatibilityScore":{"description":"Compatibility percentage for this individual query (0-100).\nCalculated as: (foundMetrics / totalMetrics) * 100\n100 = query will work perfectly, 0 = query will return no data.","type":"number"},"foundMetrics":{"description":"Number of those metrics that exist in the datasource.\nfoundMetrics \u003c= totalMetrics","type":"integer"},"missingMetrics":{"description":"Array of missing metric names specific to this query.\nHelps identify exactly which part of a query expression will fail.\nEmpty array means query is fully compatible.","items":{"type":"string"},"type":"array"},"panelID":{"description":"Numeric panel ID from dashboard JSON.\nUsed to correlate with dashboard structure.","type":"integer"},"panelTitle":{"description":"Human-readable panel title for context.\nExample: \"CPU Usage\", \"Request Rate\"","type":"string"},"queryRefId":{"description":"Query identifier within the panel.\nValues: \"A\", \"B\", \"C\", etc. (from panel.targets[].refId)\nUniquely identifies which query in a multi-query panel this refers to.","type":"string"},"totalMetrics":{"description":"Number of unique metrics referenced in this specific query.\nFor Prometheus: metrics extracted from the PromQL expr.\nExample: rate(http_requests_total[5m]) references 1 metric.","type":"integer"}},"required":["panelTitle","panelID","queryRefId","totalMetrics","foundMetrics","missingMetrics","compatibilityScore"],"type":"object"},"spec":{"additionalProperties":false,"properties":{"dashboardJson":{"additionalProperties":{"additionalProperties":{},"type":"object"},"description":"Complete dashboard JSON object to validate.\nMust be a v1 dashboard schema (contains \"panels\" array).\nv2 dashboards (with \"elements\" structure) are not yet supported.","type":"object"},"datasourceMappings":{"description":"Array of datasources to validate against.\nThe validator will check dashboard queries against each datasource\nand provide per-datasource compatibility results.\n\nMVP: Only single datasource supported (array length = 1), Prometheus type only.\nFuture: Will support multiple datasources for dashboards with mixed queries.","items":{"$ref":"#/components/schemas/DataSourceMapping"},"type":"array"}},"required":["dashboardJson","datasourceMappings"],"type":"object"},"status":{"additionalProperties":false,"properties":{"additionalFields":{"additionalProperties":{"additionalProperties":{},"type":"object"},"description":"additionalFields is reserved for future use","type":"object"},"compatibilityScore":{"description":"Overall compatibility score across all datasources (0-100).\nCalculated as: (total found metrics / total referenced metrics) * 100\n\nScore interpretation:\n- 100: Perfect compatibility, all queries will work\n- 80-99: Excellent, minor missing metrics\n- 50-79: Fair, significant missing metrics\n- 0-49: Poor, most queries will fail","type":"number"},"datasourceResults":{"description":"Per-datasource validation results.\nArray length matches spec.datasourceMappings.\nEach element contains detailed metrics and query-level breakdown.","items":{"$ref":"#/components/schemas/DataSourceResult"},"type":"array"},"lastChecked":{"description":"ISO 8601 timestamp of when validation was last performed.\nExample: \"2024-01-15T10:30:00Z\"","type":"string"},"message":{"description":"Human-readable summary of validation result.\nExamples: \"All queries compatible\", \"3 missing metrics found\"","type":"string"},"operatorStates":{"additionalProperties":{"$ref":"#/components/schemas/OperatorState"},"description":"operatorStates is a map of operator ID to operator state evaluations.\nAny operator which consumes this kind SHOULD add its state evaluation information to this field.","type":"object"}},"required":["compatibilityScore","datasourceResults"],"type":"object"}}`) + versionSchemaDashboardCompatibilityScorev1alpha1 app.VersionSchema + _ = json.Unmarshal(rawSchemaDashboardCompatibilityScorev1alpha1, &versionSchemaDashboardCompatibilityScorev1alpha1) +) + +var appManifestData = app.ManifestData{ + AppName: "dashvalidator", + Group: "dashvalidator.grafana.app", + PreferredVersion: "v1alpha1", + Versions: []app.ManifestVersion{ + { + Name: "v1alpha1", + Served: true, + Kinds: []app.ManifestVersionKind{ + { + Kind: "DashboardCompatibilityScore", + Plural: "DashboardCompatibilityScores", + Scope: "Namespaced", + Conversion: false, + Schema: &versionSchemaDashboardCompatibilityScorev1alpha1, + }, + }, + Routes: app.ManifestVersionRoutes{ + Namespaced: map[string]spec3.PathProps{}, + Cluster: map[string]spec3.PathProps{}, + Schemas: map[string]spec.Schema{}, + }, + }, + }, +} + +func LocalManifest() app.Manifest { + return app.NewEmbeddedManifest(appManifestData) +} + +func RemoteManifest() app.Manifest { + return app.NewAPIServerManifest("dashvalidator") +} + +var kindVersionToGoType = map[string]resource.Kind{ + "DashboardCompatibilityScore/v1alpha1": v1alpha1.DashboardCompatibilityScoreKind(), +} + +// ManifestGoTypeAssociator returns the associated resource.Kind instance for a given Kind and Version, if one exists. +// If there is no association for the provided Kind and Version, exists will return false. +func ManifestGoTypeAssociator(kind, version string) (goType resource.Kind, exists bool) { + goType, exists = kindVersionToGoType[fmt.Sprintf("%s/%s", kind, version)] + return goType, exists +} + +var customRouteToGoResponseType = map[string]any{} + +// ManifestCustomRouteResponsesAssociator returns the associated response go type for a given kind, version, custom route path, and method, if one exists. +// kind may be empty for custom routes which are not kind subroutes. Leading slashes are removed from subroute paths. +// If there is no association for the provided kind, version, custom route path, and method, exists will return false. +// Resource routes (those without a kind) should prefix their route with "/" if the route is namespaced (otherwise the route is assumed to be cluster-scope) +func ManifestCustomRouteResponsesAssociator(kind, version, path, verb string) (goType any, exists bool) { + if len(path) > 0 && path[0] == '/' { + path = path[1:] + } + goType, exists = customRouteToGoResponseType[fmt.Sprintf("%s|%s|%s|%s", version, kind, path, strings.ToUpper(verb))] + return goType, exists +} + +var customRouteToGoParamsType = map[string]runtime.Object{} + +func ManifestCustomRouteQueryAssociator(kind, version, path, verb string) (goType runtime.Object, exists bool) { + if len(path) > 0 && path[0] == '/' { + path = path[1:] + } + goType, exists = customRouteToGoParamsType[fmt.Sprintf("%s|%s|%s|%s", version, kind, path, strings.ToUpper(verb))] + return goType, exists +} + +var customRouteToGoRequestBodyType = map[string]any{} + +func ManifestCustomRouteRequestBodyAssociator(kind, version, path, verb string) (goType any, exists bool) { + if len(path) > 0 && path[0] == '/' { + path = path[1:] + } + goType, exists = customRouteToGoRequestBodyType[fmt.Sprintf("%s|%s|%s|%s", version, kind, path, strings.ToUpper(verb))] + return goType, exists +} + +type GoTypeAssociator struct{} + +func NewGoTypeAssociator() *GoTypeAssociator { + return &GoTypeAssociator{} +} + +func (g *GoTypeAssociator) KindToGoType(kind, version string) (goType resource.Kind, exists bool) { + return ManifestGoTypeAssociator(kind, version) +} +func (g *GoTypeAssociator) CustomRouteReturnGoType(kind, version, path, verb string) (goType any, exists bool) { + return ManifestCustomRouteResponsesAssociator(kind, version, path, verb) +} +func (g *GoTypeAssociator) CustomRouteQueryGoType(kind, version, path, verb string) (goType runtime.Object, exists bool) { + return ManifestCustomRouteQueryAssociator(kind, version, path, verb) +} +func (g *GoTypeAssociator) CustomRouteRequestBodyGoType(kind, version, path, verb string) (goType any, exists bool) { + return ManifestCustomRouteRequestBodyAssociator(kind, version, path, verb) +} diff --git a/apps/dashvalidator/pkg/app/app.go b/apps/dashvalidator/pkg/app/app.go new file mode 100644 index 00000000000..7812b41d405 --- /dev/null +++ b/apps/dashvalidator/pkg/app/app.go @@ -0,0 +1,89 @@ +package app + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + + "github.com/grafana/grafana-app-sdk/app" + "github.com/grafana/grafana-app-sdk/logging" + "github.com/grafana/grafana-app-sdk/resource" + "github.com/grafana/grafana-app-sdk/simple" + "k8s.io/apimachinery/pkg/runtime/schema" + + validatorv1alpha1 "github.com/grafana/grafana/apps/dashvalidator/pkg/apis/dashvalidator/v1alpha1" + "github.com/grafana/grafana/pkg/services/datasources" + "github.com/grafana/grafana/pkg/services/pluginsintegration/plugincontext" +) + +type DashValidatorConfig struct { + DatasourceSvc datasources.DataSourceService + PluginCtx *plugincontext.Provider +} + +func New(cfg app.Config) (app.App, error) { + specificConfig, ok := cfg.SpecificConfig.(*DashValidatorConfig) + if !ok { + return nil, fmt.Errorf("invalid config type: expected DashValidatorConfig") + } + + log := logging.DefaultLogger.With("app", "dashvalidator") + + // configure our app + simpleConfig := simple.AppConfig{ + Name: "dashvalidator", + KubeConfig: cfg.KubeConfig, + + //Define our custom route + VersionedCustomRoutes: map[string]simple.AppVersionRouteHandlers{ + "v1alpha1": { + { + Namespaced: true, + Path: "check", + Method: "POST", + }: handleCheckRoute(log, specificConfig.DatasourceSvc, specificConfig.PluginCtx), + }, + }, + } + + a, err := simple.NewApp(simpleConfig) + if err != nil { + return nil, fmt.Errorf("failed to create app: %w", err) + } + + return a, nil +} + +// custom route handler to check dashboard compatibility +func handleCheckRoute( + log logging.Logger, + datasourceSvc datasources.DataSourceService, + pluginCtx *plugincontext.Provider, +) func(context.Context, app.CustomRouteResponseWriter, *app.CustomRouteRequest) error { + + return func(ctx context.Context, w app.CustomRouteResponseWriter, r *app.CustomRouteRequest) error { + logger := log.WithContext(ctx) + logger.Info("Received compatibility check request") + + // TODO validation logic here + + // for now a simple response + + w.WriteHeader(http.StatusOK) + return json.NewEncoder(w).Encode(map[string]interface{}{ + "message": "Handler working!", + }) + } +} + +func GetKinds() map[schema.GroupVersion][]resource.Kind { + gv := schema.GroupVersion{ + Group: "dashvalidator.grafana.com", + Version: "v1alpha1", + } + + return map[schema.GroupVersion][]resource.Kind{ + gv: {validatorv1alpha1.DashboardCompatibilityScoreKind()}, + } +} diff --git a/go.work b/go.work index 884f393673e..4114bf30976 100644 --- a/go.work +++ b/go.work @@ -14,6 +14,7 @@ use ( ./apps/collections ./apps/correlations ./apps/dashboard + ./apps/dashvalidator ./apps/example ./apps/folder ./apps/iam