Chore: Add grafana-apiserver (#70721)

* add grafana-apiserver
* remove watchset & move provisioning and http server to background
services
* remove scheme
* otel fixes (#70874)
* remove module ProvideRegistry test
* use certgenerator from apiserver package
* Control collector/pdata from going to v1.0.0-rc8 (as Tempo 1.5.1 would have it)
This commit is contained in:
Todd Treece 2023-07-14 15:22:10 -04:00 committed by GitHub
parent 8ced4343f3
commit 52121b7165
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 690 additions and 838 deletions

2
.github/CODEOWNERS vendored
View File

@ -99,6 +99,7 @@
/pkg/server/ @grafana/backend-platform
/pkg/services/annotations/ @grafana/backend-platform
/pkg/services/apikey/ @grafana/grafana-authnz-team
/pkg/services/certgenerator @grafana/grafana-app-platform-squad
/pkg/services/cleanup/ @grafana/backend-platform
/pkg/services/contexthandler/ @grafana/backend-platform
/pkg/services/correlations/ @grafana/backend-platform
@ -108,6 +109,7 @@
/pkg/services/dashboardversion/ @grafana/backend-platform
/pkg/services/encryption/ @grafana/backend-platform
/pkg/services/folder/ @grafana/backend-platform
/pkg/services/grafana-apiserver @grafana/grafana-app-platform-squad
/pkg/services/hooks/ @grafana/backend-platform
/pkg/services/kmsproviders/ @grafana/backend-platform
/pkg/services/licensing/ @grafana/backend-platform

View File

@ -82,6 +82,11 @@ linters = ["stylecheck"]
text = "ST1001"
# Enable when appropriate
# http.CloseNotifier has been deprecated since Go 1.11 and an alternative has been available since Go 1.7: We currently need it in pkg/web/response_writer.go.
[[issues.exclude-rules]]
linters = ["staticcheck"]
text = "SA1019: http.CloseNotifier"
# strings.Title has been deprecated since Go 1.18 and an alternative has been available since Go 1.0: The rule Title uses for word boundaries does not handle Unicode punctuation properly.
# Use golang.org/x/text/cases instead.
[[issues.exclude-rules]]

View File

@ -128,6 +128,7 @@ Experimental features might be changed or removed without prior notice.
| `transformationsRedesign` | Enables the transformations redesign |
| `mlExpressions` | Enable support for Machine Learning in server-side expressions |
| `disableTraceQLStreaming` | Disables the option to stream the response of TraceQL queries of the Tempo data source |
| `grafanaAPIServer` | Enable Kubernetes API Server for Grafana resources |
## Development feature toggles

107
go.mod
View File

@ -13,13 +13,19 @@ replace github.com/denisenkom/go-mssqldb => github.com/grafana/go-mssqldb v0.9.2
replace github.com/docker/docker => github.com/moby/moby v23.0.4+incompatible
// contains openapi encoder fixes. remove ASAP
replace cuelang.org/go => github.com/sdboyer/cue v0.5.0-beta.2.0.20230712135403-bdc4772ae055
replace cuelang.org/go => github.com/sdboyer/cue v0.5.0-beta.2.0.20230712135403-bdc4772ae055 // @grafana/grafana-as-code
// For some insane reason, client-go seems to have a broken v12.0.0 tag on it that forces us to
// hoist a replace statement.
replace k8s.io/client-go => k8s.io/client-go v0.25.3
// TODO: following otel replaces to pin the libraries so k8s.io/apiserver doesn't downgrade us inadvertantly
// will need bumps as we upgrade otel in Grafana
replace go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp => go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.42.0 // @grafana/backend-platform
require k8s.io/apimachinery v0.26.2 // @grafana/backend-platform
replace go.opentelemetry.io/otel => go.opentelemetry.io/otel v1.16.0 // @grafana/backend-platform
replace go.opentelemetry.io/otel/trace => go.opentelemetry.io/otel/trace v1.16.0 // @grafana/backend-platform
replace go.opentelemetry.io/otel/metric => go.opentelemetry.io/otel/metric v1.16.0 // @grafana/backend-platform
replace go.opentelemetry.io/collector/pdata => go.opentelemetry.io/collector/pdata v0.50.0 // @grafana/backend-platform
// Override Prometheus version because Prometheus v2.X is tagged as v0.X for Go modules purposes and Go assumes
// that v1.Y is higher than v0.X, so when we resolve dependencies if any dependency imports v1.Y we'd
@ -99,16 +105,16 @@ require (
github.com/vectordotdev/go-datemath v0.1.1-0.20220323213446-f3954d0b18ae // @grafana/backend-platform
github.com/yalue/merged_fs v1.2.2 // @grafana/grafana-as-code
github.com/yudai/gojsondiff v1.0.0 // @grafana/backend-platform
go.opentelemetry.io/collector/model v0.46.0 // @grafana/backend-platform
go.opentelemetry.io/collector/model v0.50.0 // @grafana/backend-platform
go.opentelemetry.io/collector/pdata v1.0.0-rc8 // @grafana/backend-platform
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.37.0 // @grafana/grafana-operator-experience-squad
go.opentelemetry.io/otel v1.14.0 // @grafana/alerting-squad-backend
go.opentelemetry.io/otel/exporters/jaeger v1.10.0 // @grafana/backend-platform
go.opentelemetry.io/otel/sdk v1.14.0 // @grafana/backend-platform
go.opentelemetry.io/otel/trace v1.14.0 // @grafana/backend-platform
go.opentelemetry.io/otel/sdk v1.16.0 // @grafana/backend-platform
go.opentelemetry.io/otel/trace v1.16.0 // @grafana/backend-platform
golang.org/x/crypto v0.11.0 // @grafana/backend-platform
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // @grafana/alerting-squad-backend
golang.org/x/net v0.10.0 // @grafana/grafana-bi-squad
golang.org/x/oauth2 v0.7.0 // @grafana/grafana-authnz-team
golang.org/x/oauth2 v0.8.0 // @grafana/grafana-authnz-team
golang.org/x/sync v0.3.0 // @grafana/alerting-squad-backend
golang.org/x/time v0.3.0 // @grafana/backend-platform
golang.org/x/tools v0.7.0 // @grafana/grafana-as-code
@ -121,7 +127,7 @@ require (
gopkg.in/mail.v2 v2.3.1 // @grafana/backend-platform
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // @grafana/alerting-squad-backend
xorm.io/builder v0.3.6 // indirect
xorm.io/builder v0.3.6 // indirect; @grafana/backend-platform
xorm.io/core v0.7.3 // @grafana/backend-platform
xorm.io/xorm v0.8.2 // @grafana/alerting-squad-backend
)
@ -134,14 +140,13 @@ require (
github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect
github.com/Azure/go-autorest/logger v0.2.1 // indirect
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
github.com/FZambia/eagle v0.0.2 // indirect
github.com/FZambia/eagle v0.1.0 // indirect
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect
github.com/andybalholm/brotli v1.0.4 // @grafana/partner-datasources
github.com/apache/arrow/go/arrow v0.0.0-20211112161151-bc219186db40 // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
github.com/centrifugal/protocol v0.10.0 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/cheekybits/genny v1.0.0 // indirect
github.com/cockroachdb/apd/v2 v2.0.2 // indirect
@ -164,7 +169,7 @@ require (
github.com/go-openapi/validate v0.22.1 // indirect
github.com/golang-jwt/jwt/v4 v4.5.0 // @grafana/backend-platform
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe // indirect
github.com/golang/glog v1.0.0 // indirect
github.com/golang/glog v1.1.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.3 // @grafana/backend-platform
github.com/google/btree v1.1.2 // indirect
@ -175,7 +180,7 @@ require (
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.1-0.20191002090509-6af20e3a5340 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-msgpack v0.5.5 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect; @grafana/grafana-as-code
github.com/hashicorp/go-sockaddr v1.0.2 // indirect
github.com/hashicorp/golang-lru v0.6.0 // indirect
github.com/hashicorp/yamux v0.1.1 // indirect
@ -220,7 +225,7 @@ require (
golang.org/x/text v0.11.0 // @grafana/backend-platform
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect; @grafana/backend-platform
google.golang.org/genproto v0.0.0-20230526203410-71b5a4ffd15e // indirect; @grafana/backend-platform
)
require (
@ -239,15 +244,15 @@ require (
github.com/golang-migrate/migrate/v4 v4.7.0 // @grafana/backend-platform
github.com/google/go-github/v45 v45.2.0 // @grafana/grafana-delivery
github.com/grafana/codejen v0.0.3 // @grafana/dataviz-squad
github.com/grafana/dskit v0.0.0-20230620150242-3dc2113b720d // @grafana/backend-platform
github.com/grafana/dskit v0.0.0-20230706162620-5081d8ed53e6 // @grafana/backend-platform
github.com/grafana/phlare/api v0.1.4-0.20230426005640-f90edba05413 // @grafana/observability-traces-and-profiling
github.com/huandu/xstrings v1.3.1 // @grafana/partner-datasources
github.com/jmoiron/sqlx v1.3.5 // @grafana/backend-platform
github.com/matryer/is v1.4.0 // @grafana/grafana-as-code
github.com/urfave/cli v1.22.12 // @grafana/backend-platform
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.40.0 // @grafana/plugins-platform-backend
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.42.0 // @grafana/plugins-platform-backend
go.opentelemetry.io/contrib/propagators/jaeger v1.15.0 // @grafana/backend-platform
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.14.0 // @grafana/backend-platform
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.16.0 // @grafana/backend-platform
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.14.0 // @grafana/backend-platform
gocloud.dev v0.25.0 // @grafana/grafana-app-platform-squad
)
@ -263,7 +268,7 @@ require (
github.com/grafana/dataplane/sdata v0.0.6 // @grafana/observability-metrics
github.com/grafana/go-mssqldb v0.9.1 // @grafana/grafana-bi-squad
github.com/grafana/kindsys v0.0.0-20230508162304-452481b63482 // @grafana/grafana-as-code
github.com/grafana/tempo v1.5.1-0.20230524121406-1dc1bfe7085b
github.com/grafana/tempo v1.5.1-0.20230524121406-1dc1bfe7085b // @grafana/observability-traces-and-profiling
github.com/grafana/thema v0.0.0-20230712153715-375c1b45f3ed // @grafana/grafana-as-code
github.com/ory/fosite v0.44.1-0.20230317114349-45a6785cc54f // @grafana/grafana-authnz-team
github.com/redis/go-redis/v9 v9.0.2 // @grafana/alerting-squad-backend
@ -271,8 +276,17 @@ require (
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // @grafana/grafana-as-code
go.opentelemetry.io/contrib/samplers/jaegerremote v0.9.0 // @grafana/backend-platform
golang.org/x/mod v0.9.0 // @grafana/backend-platform
gopkg.in/square/go-jose.v2 v2.5.2-0.20210529014059-a5c7eec3c614 // @grafana/grafana-authnz-team
k8s.io/utils v0.0.0-20230308161112-d77c459e9343 // @grafana/partner-datasources
gopkg.in/square/go-jose.v2 v2.6.0 // @grafana/grafana-authnz-team
k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 // @grafana/partner-datasources
)
require (
github.com/grafana/grafana-apiserver v0.0.0-20230713001719-88a9ed41992d // @grafana/grafana-app-platform-squad
go.opentelemetry.io/otel v1.16.0 // @grafana/backend-platform
k8s.io/apiserver v0.27.1 // @grafana/grafana-app-platform-squad
k8s.io/apimachinery v0.27.1 // @grafana/grafana-app-platform-squad
k8s.io/client-go v0.27.1 // @grafana/grafana-app-platform-squad
k8s.io/klog/v2 v2.90.1 // @grafana/grafana-app-platform-squad
)
require (
@ -281,11 +295,16 @@ require (
github.com/Azure/azure-pipeline-go v0.2.3 // indirect
github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/NYTimes/gziphandler v1.1.1 // indirect
github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a // indirect
github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 // indirect
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
github.com/armon/go-metrics v0.4.1 // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
github.com/bmatcuk/doublestar v1.1.1 // indirect
github.com/buildkite/yaml v2.1.0+incompatible // indirect
github.com/bwmarrin/snowflake v0.3.0 // indirect
github.com/centrifugal/protocol v0.10.0 // indirect
github.com/cloudflare/circl v1.1.0 // indirect
github.com/cockroachdb/errors v1.9.1 // indirect
github.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f // indirect
@ -296,6 +315,7 @@ require (
github.com/dave/jennifer v1.5.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dgraph-io/ristretto v0.1.0 // indirect
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect
github.com/docker/distribution v2.8.1+incompatible // indirect
github.com/docker/go-connections v0.4.0 // indirect
github.com/drone-runners/drone-runner-docker v1.8.2 // indirect
@ -304,9 +324,14 @@ require (
github.com/drone/runner-go v1.12.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/ecordell/optgen v0.0.6 // indirect
github.com/emicklei/go-restful/v3 v3.10.1 // indirect
github.com/evanphx/json-patch v4.12.0+incompatible // indirect
github.com/felixge/httpsnoop v1.0.3 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/getsentry/sentry-go v0.12.0 // indirect
github.com/go-asn1-ber/asn1-ber v1.5.4 // indirect
github.com/google/cel-go v0.12.6 // indirect
github.com/google/gnostic v0.6.9 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect
@ -318,7 +343,7 @@ require (
github.com/hashicorp/golang-lru/v2 v2.0.2 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/hashicorp/memberlist v0.5.0 // indirect
github.com/inconshreveable/mousetrap v1.0.1 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/invopop/yaml v0.1.0 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/kr/text v0.2.0 // indirect
@ -330,6 +355,8 @@ require (
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mitchellh/reflectwalk 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/onsi/gomega v1.27.6 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.0.3-0.20220512140940-7b36cea86235 // indirect
github.com/ory/go-acc v0.2.6 // indirect
@ -341,15 +368,16 @@ require (
github.com/perimeterx/marshmallow v1.1.4 // indirect
github.com/rivo/uniseg v0.3.4 // indirect
github.com/rogpeppe/go-internal v1.10.0 // indirect
github.com/rueian/rueidis v0.0.100-go1.18 // indirect
github.com/rueian/rueidis v0.0.100 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/segmentio/asm v1.2.0 // indirect
github.com/shopspring/decimal v1.2.0 // indirect
github.com/spf13/afero v1.9.2 // indirect
github.com/spf13/cast v1.5.0 // indirect
github.com/spf13/cobra v1.6.1 // indirect
github.com/spf13/cobra v1.7.0 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/stoewer/go-strcase v1.2.0 // indirect
github.com/subosito/gotenv v1.4.1 // indirect
github.com/unknwon/bra v0.0.0-20200517080246-1e3013ecaff8 // indirect
github.com/unknwon/com v1.0.1 // indirect
@ -357,13 +385,27 @@ require (
github.com/weaveworks/promrus v1.2.0 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
github.com/yuin/gopher-lua v1.1.0 // indirect
go.opentelemetry.io/otel/metric v0.37.0 // indirect
go.etcd.io/etcd/api/v3 v3.5.7 // indirect
go.etcd.io/etcd/client/pkg/v3 v3.5.7 // indirect
go.etcd.io/etcd/client/v3 v3.5.7 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.42.0 // indirect
go.opentelemetry.io/otel/metric v1.16.0 // indirect
go.starlark.net v0.0.0-20221020143700-22309ac47eac // indirect
go.uber.org/multierr v1.10.0 // indirect
go.uber.org/zap v1.24.0 // indirect
golang.org/x/term v0.10.0 // indirect
gopkg.in/fsnotify/fsnotify.v1 v1.4.7 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
k8s.io/klog/v2 v2.90.1 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
k8s.io/api v0.27.1 // indirect
k8s.io/component-base v0.27.1 // indirect
k8s.io/kms v0.27.1 // indirect
k8s.io/kube-aggregator v0.27.1 // indirect
k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // indirect
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.1.2 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
sigs.k8s.io/yaml v1.3.0 // indirect
)
require (
@ -388,6 +430,7 @@ require (
github.com/blugelabs/ice v1.0.0 // indirect
github.com/caio/go-tdigest v3.1.0+incompatible // indirect
github.com/chromedp/cdproto v0.0.0-20220208224320-6efb837e6bc2 // indirect
github.com/coreos/go-semver v0.3.0 // indirect
github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 // indirect
github.com/docker/docker v23.0.4+incompatible // @grafana/grafana-delivery
github.com/elazarl/goproxy v0.0.0-20220115173737-adb46da277ac // indirect
@ -395,10 +438,10 @@ require (
github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 // indirect
github.com/go-git/gcfg v1.5.0 // indirect
github.com/go-git/go-billy/v5 v5.3.1 // indirect
github.com/go-logr/logr v1.2.4 // indirect
github.com/go-logr/logr v1.2.4 // @grafana/grafana-app-platform-squad
github.com/go-logr/stdr v1.2.2 // indirect
github.com/google/go-github v17.0.0+incompatible // @grafana/grafana-app-platform-squad
github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.2 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect
github.com/hmarr/codeowners v1.1.2 // @grafana/grafana-as-code
github.com/imdario/mergo v0.3.13 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
@ -415,8 +458,8 @@ require (
github.com/wk8/go-ordered-map v1.0.0 // @grafana/backend-platform
github.com/xanzy/ssh-agent v0.3.0 // indirect
github.com/xlab/treeprint v1.1.0 // @grafana/observability-traces-and-profiling
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.14.0 // indirect
go.opentelemetry.io/proto/otlp v0.19.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.16.0 // indirect
go.opentelemetry.io/proto/otlp v0.20.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
)
@ -442,6 +485,8 @@ replace github.com/prometheus/alertmanager => github.com/grafana/prometheus-aler
// grpc v1.46.0 removed "WithBalancerName()" API, still in use by weaveworks/commons.
replace google.golang.org/grpc => google.golang.org/grpc v1.45.0
replace google.golang.org/genproto => google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3
// Use 1.10.6 of pq to avoid a change in 1.10.7 that has certificate validation issues. https://github.com/grafana/grafana/issues/65816
replace github.com/lib/pq => github.com/lib/pq v1.10.6

840
go.sum

File diff suppressed because it is too large Load Diff

View File

@ -115,4 +115,5 @@ export interface FeatureToggles {
transformationsRedesign?: boolean;
mlExpressions?: boolean;
disableTraceQLStreaming?: boolean;
grafanaAPIServer?: boolean;
}

View File

@ -11,8 +11,9 @@ import (
"time"
om "github.com/wk8/go-ordered-map"
otlp "go.opentelemetry.io/collector/model/otlp"
otelpdata "go.opentelemetry.io/collector/model/pdata"
"go.opentelemetry.io/collector/model/otlp"
"go.opentelemetry.io/collector/pdata/pcommon"
"go.opentelemetry.io/collector/pdata/ptrace"
)
// KeyVal is an ordered map of string to interface
@ -142,7 +143,7 @@ func (tc TraceContext) KeyVal() *KeyVal {
// Traces wraps the otel traces model.
type Traces struct {
otelpdata.Traces
ptrace.Traces
}
// UnmarshalJSON unmarshals Traces model.
@ -163,12 +164,12 @@ func (t Traces) MarshalJSON() ([]byte, error) {
}
// SpanSlice unpacks Traces entity into a slice of Spans.
func (t Traces) SpanSlice() []otelpdata.Span {
spans := make([]otelpdata.Span, 0)
func (t Traces) SpanSlice() []ptrace.Span {
spans := make([]ptrace.Span, 0)
rss := t.ResourceSpans()
for i := 0; i < rss.Len(); i++ {
rs := rss.At(i)
ilss := rs.InstrumentationLibrarySpans()
ilss := rs.ScopeSpans()
for j := 0; j < ilss.Len(); j++ {
s := ilss.At(j).Spans()
for si := 0; si < s.Len(); si++ {
@ -180,7 +181,7 @@ func (t Traces) SpanSlice() []otelpdata.Span {
}
// SpanToKeyVal returns KeyVal representation of a Span.
func SpanToKeyVal(s otelpdata.Span) *KeyVal {
func SpanToKeyVal(s ptrace.Span) *KeyVal {
kv := NewKeyVal()
if s.StartTimestamp() > 0 {
KeyValAdd(kv, "timestamp", s.StartTimestamp().AsTime().String())
@ -194,7 +195,7 @@ func SpanToKeyVal(s otelpdata.Span) *KeyVal {
KeyValAdd(kv, "span_kind", s.Kind().String())
KeyValAdd(kv, "name", s.Name())
KeyValAdd(kv, "parent_spanID", s.ParentSpanID().HexString())
s.Attributes().Range(func(k string, v otelpdata.AttributeValue) bool {
s.Attributes().Range(func(k string, v pcommon.Value) bool {
KeyValAdd(kv, "attr_"+k, fmt.Sprintf("%v", v))
return true
})

View File

@ -5,9 +5,18 @@ const (
All string = "all"
// BackgroundServices includes all Grafana services that run in the background
BackgroundServices string = "background-services"
// CertGenerator generates certificates for grafana-apiserver
CertGenerator string = "cert-generator"
// GrafanaAPIServer is the Kubertenes API server for Grafana Resources
GrafanaAPIServer string = "grafana-apiserver"
)
// dependencyMap defines Module Targets => Dependencies
var dependencyMap = map[string][]string{
BackgroundServices: {},
CertGenerator: {},
GrafanaAPIServer: {CertGenerator},
All: {BackgroundServices},
}

View File

@ -3,11 +3,13 @@ package modules
import (
"context"
"errors"
"fmt"
"github.com/grafana/dskit/modules"
"github.com/grafana/dskit/services"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/systemd"
)
@ -35,9 +37,14 @@ type service struct {
moduleManager *modules.Manager
serviceManager *services.Manager
serviceMap map[string]services.Service
features *featuremgmt.FeatureManager
}
func ProvideService(cfg *setting.Cfg) *service {
func ProvideService(
cfg *setting.Cfg,
features *featuremgmt.FeatureManager,
) *service {
logger := log.New("modules")
return &service{
@ -47,6 +54,8 @@ func ProvideService(cfg *setting.Cfg) *service {
moduleManager: modules.NewManager(logger),
serviceMap: map[string]services.Service{},
features: features,
}
}
@ -54,6 +63,11 @@ func ProvideService(cfg *setting.Cfg) *service {
func (m *service) Init(_ context.Context) error {
var err error
if err = m.processFeatureFlags(); err != nil {
return err
}
m.log.Debug("Initializing module manager", "targets", m.targets)
for mod, targets := range dependencyMap {
if err := m.moduleManager.AddDependency(mod, targets...); err != nil {
return err
@ -93,6 +107,7 @@ func (m *service) Run(ctx context.Context) error {
listener := newServiceListener(m.log, m)
m.serviceManager.AddListener(listener)
m.log.Debug("Starting module service manager")
// wait until a service fails or stop signal was received
err := m.serviceManager.StartAsync(ctx)
if err != nil {
@ -149,3 +164,31 @@ func (m *service) RegisterInvisibleModule(name string, initFn func() (services.S
func (m *service) IsModuleEnabled(name string) bool {
return stringsContain(m.targets, name)
}
// processFeatureFlags adds or removes targets based on feature flags.
func (m *service) processFeatureFlags() error {
// add GrafanaAPIServer to targets if feature is enabled
if m.features.IsEnabled(featuremgmt.FlagGrafanaAPIServer) {
m.targets = append(m.targets, GrafanaAPIServer)
}
if !m.features.IsEnabled(featuremgmt.FlagGrafanaAPIServer) {
// error if GrafanaAPIServer is in targets
for _, t := range m.targets {
if t == GrafanaAPIServer {
return fmt.Errorf("feature flag %s is disabled, but target %s is still enabled", featuremgmt.FlagGrafanaAPIServer, GrafanaAPIServer)
}
}
// error if GrafanaAPIServer is a dependency of a target
for parent, targets := range dependencyMap {
for _, t := range targets {
if t == GrafanaAPIServer && m.IsModuleEnabled(parent) {
return fmt.Errorf("feature flag %s is disabled, but target %s is enabled with dependency on %s", featuremgmt.FlagGrafanaAPIServer, parent, GrafanaAPIServer)
}
}
}
}
return nil
}

View File

@ -3,9 +3,11 @@ package registry
import (
"github.com/grafana/dskit/services"
"github.com/grafana/grafana-apiserver/pkg/certgenerator"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/modules"
"github.com/grafana/grafana/pkg/server/backgroundsvcs"
grafanaapiserver "github.com/grafana/grafana/pkg/services/grafana-apiserver"
)
type Registry interface{}
@ -17,12 +19,16 @@ type registry struct {
func ProvideRegistry(
moduleManager modules.Manager,
apiServer grafanaapiserver.Service,
backgroundServiceRunner *backgroundsvcs.BackgroundServiceRunner,
certGenerator certgenerator.ServiceInterface,
) *registry {
return newRegistry(
log.New("modules.registry"),
moduleManager,
apiServer,
backgroundServiceRunner,
certGenerator,
)
}
@ -41,7 +47,6 @@ func newRegistry(logger log.Logger, moduleManager modules.Manager, svcs ...servi
})
}
// Register module targets
logger.Debug("Registering module", "name", modules.All)
r.moduleManager.RegisterModule(modules.All, nil)

View File

@ -9,31 +9,8 @@ import (
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/modules"
"github.com/grafana/grafana/pkg/server/backgroundsvcs"
)
func TestProvideRegistry(t *testing.T) {
var registeredInvisibleModules []string
var registeredModules []string
moduleManager := &modules.MockModuleManager{
RegisterModuleFunc: func(name string, initFn func() (services.Service, error)) {
registeredModules = append(registeredModules, name)
},
RegisterInvisibleModuleFunc: func(name string, initFn func() (services.Service, error)) {
registeredInvisibleModules = append(registeredInvisibleModules, name)
},
}
svcRegistry := backgroundsvcs.NewBackgroundServiceRegistry()
svcRunner := backgroundsvcs.ProvideBackgroundServiceRunner(svcRegistry)
r := ProvideRegistry(moduleManager, svcRunner)
require.NotNil(t, r)
require.Equal(t, []string{modules.BackgroundServices}, registeredInvisibleModules)
require.Equal(t, []string{modules.All}, registeredModules)
}
func TestNewRegistry(t *testing.T) {
var registeredInvisibleModules []string
var registeredModules []string

View File

@ -44,6 +44,7 @@ import (
"github.com/grafana/grafana/pkg/services/apikey/apikeyimpl"
"github.com/grafana/grafana/pkg/services/auth/jwt"
"github.com/grafana/grafana/pkg/services/authn/authnimpl"
"github.com/grafana/grafana/pkg/services/certgenerator"
"github.com/grafana/grafana/pkg/services/cleanup"
"github.com/grafana/grafana/pkg/services/contexthandler"
"github.com/grafana/grafana/pkg/services/contexthandler/authproxy"
@ -64,6 +65,7 @@ import (
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/folder"
"github.com/grafana/grafana/pkg/services/folder/folderimpl"
"github.com/grafana/grafana/pkg/services/grafana-apiserver"
"github.com/grafana/grafana/pkg/services/grpcserver"
grpccontext "github.com/grafana/grafana/pkg/services/grpcserver/context"
"github.com/grafana/grafana/pkg/services/grpcserver/interceptors"
@ -204,6 +206,7 @@ var wireBasicSet = wire.NewSet(
wire.Bind(new(httpclient.Provider), new(*sdkhttpclient.Provider)),
serverlock.ProvideService,
annotationsimpl.ProvideCleanupService,
certgenerator.WireSet,
wire.Bind(new(annotations.Cleaner), new(*annotationsimpl.CleanupServiceImpl)),
cleanup.ProvideService,
shorturlimpl.ProvideService,
@ -355,6 +358,7 @@ var wireBasicSet = wire.NewSet(
wire.Bind(new(tag.Service), new(*tagimpl.Service)),
authnimpl.ProvideService,
supportbundlesimpl.ProvideService,
grafanaapiserver.WireSet,
oasimpl.ProvideService,
wire.Bind(new(oauthserver.OAuth2Server), new(*oasimpl.OAuth2ServiceImpl)),
loggermw.Provide,

View File

@ -0,0 +1,14 @@
package certgenerator
import (
"path/filepath"
"github.com/grafana/grafana/pkg/modules"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana-apiserver/pkg/certgenerator"
)
func ProvideService(cfg *setting.Cfg) (*certgenerator.Service, error) {
return certgenerator.CreateService(modules.CertGenerator, filepath.Join(cfg.DataPath, "k8s"))
}

View File

@ -0,0 +1,11 @@
package certgenerator
import (
"github.com/google/wire"
"github.com/grafana/grafana-apiserver/pkg/certgenerator"
)
var WireSet = wire.NewSet(
ProvideService,
wire.Bind(new(certgenerator.ServiceInterface), new(*certgenerator.Service)),
)

View File

@ -115,3 +115,9 @@ func (mw mockWriter) Before(web.BeforeFunc) {}
func (mw mockWriter) Push(target string, opts *http.PushOptions) error {
return nil
}
func (mw mockWriter) CloseNotify() <-chan bool {
return make(<-chan bool)
}
func (mw mockWriter) Unwrap() http.ResponseWriter {
return mw
}

View File

@ -661,5 +661,12 @@ var (
FrontendOnly: true,
Owner: grafanaObservabilityTracesAndProfilingSquad,
},
{
Name: "grafanaAPIServer",
Description: "Enable Kubernetes API Server for Grafana resources",
Stage: FeatureStageExperimental,
FrontendOnly: false,
Owner: grafanaAppPlatformSquad,
},
}
)

View File

@ -96,3 +96,4 @@ awsDatasourcesTempCredentials,experimental,@grafana/aws-datasources,false,false,
transformationsRedesign,experimental,@grafana/observability-metrics,false,false,false,true
mlExpressions,experimental,@grafana/alerting-squad,false,false,false,false
disableTraceQLStreaming,experimental,@grafana/observability-traces-and-profiling,false,false,false,true
grafanaAPIServer,experimental,@grafana/grafana-app-platform-squad,false,false,false,false

1 Name Stage Owner requiresDevMode RequiresLicense RequiresRestart FrontendOnly
96 transformationsRedesign experimental @grafana/observability-metrics false false false true
97 mlExpressions experimental @grafana/alerting-squad false false false false
98 disableTraceQLStreaming experimental @grafana/observability-traces-and-profiling false false false true
99 grafanaAPIServer experimental @grafana/grafana-app-platform-squad false false false false

View File

@ -394,4 +394,8 @@ const (
// FlagDisableTraceQLStreaming
// Disables the option to stream the response of TraceQL queries of the Tempo data source
FlagDisableTraceQLStreaming = "disableTraceQLStreaming"
// FlagGrafanaAPIServer
// Enable Kubernetes API Server for Grafana resources
FlagGrafanaAPIServer = "grafanaAPIServer"
)

View File

@ -0,0 +1,50 @@
package grafanaapiserver
import (
"cuelang.org/go/pkg/strings"
"github.com/go-logr/logr"
"github.com/grafana/grafana/pkg/infra/log"
)
var _ logr.LogSink = (*logAdapter)(nil)
type logAdapter struct {
log log.Logger
}
func newLogAdapter() *logAdapter {
return &logAdapter{log: log.New("k8s.apiserver")}
}
func (l *logAdapter) WithName(name string) logr.LogSink {
l.log = l.log.New("name", name)
return l
}
func (l *logAdapter) WithValues(keysAndValues ...interface{}) logr.LogSink {
l.log = l.log.New(keysAndValues...)
return l
}
func (l *logAdapter) Init(_ logr.RuntimeInfo) {
// TODO: shrug emoji
}
func (l *logAdapter) Enabled(level int) bool {
return level <= 5
}
func (l *logAdapter) Info(level int, msg string, keysAndValues ...interface{}) {
msg = strings.TrimSpace(msg)
if level < 1 {
l.log.Info(msg, keysAndValues...)
return
}
l.log.Debug(msg, keysAndValues...)
}
func (l *logAdapter) Error(err error, msg string, keysAndValues ...interface{}) {
msg = strings.TrimSpace(msg)
l.log.Error(msg, keysAndValues...)
}

View File

@ -0,0 +1,248 @@
package grafanaapiserver
import (
"context"
"crypto/x509"
"net"
"os"
"path"
"strconv"
"cuelang.org/go/pkg/strings"
"github.com/go-logr/logr"
"github.com/grafana/dskit/services"
grafanaapiserveroptions "github.com/grafana/grafana-apiserver/pkg/cmd/server/options"
"k8s.io/apiserver/pkg/authentication/authenticator"
"k8s.io/apiserver/pkg/authentication/request/headerrequest"
"k8s.io/apiserver/pkg/authentication/user"
"k8s.io/apiserver/pkg/endpoints/responsewriter"
genericapiserver "k8s.io/apiserver/pkg/server"
"k8s.io/apiserver/pkg/server/options"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
"k8s.io/klog/v2"
"github.com/grafana/grafana-apiserver/pkg/certgenerator"
"github.com/grafana/grafana/pkg/api/routing"
"github.com/grafana/grafana/pkg/infra/appcontext"
"github.com/grafana/grafana/pkg/middleware"
"github.com/grafana/grafana/pkg/modules"
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/web"
)
const (
DefaultAPIServerHost = "https://" + certgenerator.DefaultAPIServerIp + ":6443"
)
var (
_ Service = (*service)(nil)
_ RestConfigProvider = (*service)(nil)
)
type Service interface {
services.NamedService
}
type RestConfigProvider interface {
GetRestConfig() *rest.Config
}
type service struct {
*services.BasicService
restConfig *rest.Config
rr routing.RouteRegister
handler web.Handler
dataPath string
stopCh chan struct{}
stoppedCh chan error
}
func ProvideService(cfg *setting.Cfg, rr routing.RouteRegister) (*service, error) {
s := &service{
rr: rr,
dataPath: path.Join(cfg.DataPath, "k8s"),
stopCh: make(chan struct{}),
}
s.BasicService = services.NewBasicService(s.start, s.running, nil).WithName(modules.GrafanaAPIServer)
s.rr.Group("/k8s", func(k8sRoute routing.RouteRegister) {
handler := func(c *contextmodel.ReqContext) {
if s.handler == nil {
c.Resp.WriteHeader(404)
_, _ = c.Resp.Write([]byte("Not found"))
return
}
if handle, ok := s.handler.(func(c *contextmodel.ReqContext)); ok {
handle(c)
return
}
}
k8sRoute.Any("/", middleware.ReqSignedIn, handler)
k8sRoute.Any("/*", middleware.ReqSignedIn, handler)
})
return s, nil
}
func (s *service) GetRestConfig() *rest.Config {
return s.restConfig
}
func (s *service) start(ctx context.Context) error {
logger := logr.New(newLogAdapter())
logger.V(9)
klog.SetLoggerWithOptions(logger, klog.ContextualLogger(true))
o := grafanaapiserveroptions.NewGrafanaAPIServerOptions(os.Stdout, os.Stderr)
o.RecommendedOptions.SecureServing.BindPort = 6443
o.RecommendedOptions.Authentication.RemoteKubeConfigFileOptional = true
o.RecommendedOptions.Authorization.RemoteKubeConfigFileOptional = true
o.RecommendedOptions.Authorization.AlwaysAllowPaths = []string{"*"}
o.RecommendedOptions.Authorization.AlwaysAllowGroups = []string{user.SystemPrivilegedGroup, "grafana"}
o.RecommendedOptions.Etcd = nil
// TODO: setting CoreAPI to nil currently segfaults in grafana-apiserver
o.RecommendedOptions.CoreAPI = nil
// Get the util to get the paths to pre-generated certs
certUtil := certgenerator.CertUtil{
K8sDataPath: s.dataPath,
}
o.RecommendedOptions.SecureServing.BindAddress = net.ParseIP(certgenerator.DefaultAPIServerIp)
o.RecommendedOptions.SecureServing.ServerCert.CertKey = options.CertKey{
CertFile: certUtil.APIServerCertFile(),
KeyFile: certUtil.APIServerKeyFile(),
}
if err := o.Complete(); err != nil {
return err
}
if err := o.Validate(); err != nil {
return err
}
serverConfig, err := o.Config()
if err != nil {
return err
}
rootCert, err := certUtil.GetK8sCACert()
if err != nil {
return err
}
authenticator, err := newAuthenticator(rootCert)
if err != nil {
return err
}
serverConfig.GenericConfig.Authentication.Authenticator = authenticator
server, err := serverConfig.Complete().New(genericapiserver.NewEmptyDelegate())
if err != nil {
return err
}
s.restConfig = server.GenericAPIServer.LoopbackClientConfig
err = s.writeKubeConfiguration(s.restConfig)
if err != nil {
return err
}
prepared := server.GenericAPIServer.PrepareRun()
s.handler = func(c *contextmodel.ReqContext) {
req := c.Req
req.URL.Path = strings.TrimPrefix(req.URL.Path, "/k8s")
if req.URL.Path == "" {
req.URL.Path = "/"
}
ctx := req.Context()
signedInUser := appcontext.MustUser(ctx)
req.Header.Set("X-Remote-User", strconv.FormatInt(signedInUser.UserID, 10))
req.Header.Set("X-Remote-Group", "grafana")
req.Header.Set("X-Remote-Extra-token-name", signedInUser.Name)
req.Header.Set("X-Remote-Extra-org-role", string(signedInUser.OrgRole))
req.Header.Set("X-Remote-Extra-org-id", strconv.FormatInt(signedInUser.OrgID, 10))
req.Header.Set("X-Remote-Extra-user-id", strconv.FormatInt(signedInUser.UserID, 10))
resp := responsewriter.WrapForHTTP1Or2(c.Resp)
prepared.GenericAPIServer.Handler.ServeHTTP(resp, req)
}
go func() {
s.stoppedCh <- prepared.Run(s.stopCh)
}()
return nil
}
func (s *service) running(ctx context.Context) error {
select {
case err := <-s.stoppedCh:
if err != nil {
return err
}
case <-ctx.Done():
close(s.stopCh)
}
return nil
}
func (s *service) writeKubeConfiguration(restConfig *rest.Config) error {
clusters := make(map[string]*clientcmdapi.Cluster)
clusters["default-cluster"] = &clientcmdapi.Cluster{
Server: restConfig.Host,
InsecureSkipTLSVerify: true,
}
contexts := make(map[string]*clientcmdapi.Context)
contexts["default-context"] = &clientcmdapi.Context{
Cluster: "default-cluster",
Namespace: "default",
AuthInfo: "default",
}
authinfos := make(map[string]*clientcmdapi.AuthInfo)
authinfos["default"] = &clientcmdapi.AuthInfo{
Token: restConfig.BearerToken,
}
clientConfig := clientcmdapi.Config{
Kind: "Config",
APIVersion: "v1",
Clusters: clusters,
Contexts: contexts,
CurrentContext: "default-context",
AuthInfos: authinfos,
}
return clientcmd.WriteToFile(clientConfig, path.Join(s.dataPath, "grafana.kubeconfig"))
}
func newAuthenticator(cert *x509.Certificate) (authenticator.Request, error) {
reqHeaderOptions := options.RequestHeaderAuthenticationOptions{
UsernameHeaders: []string{"X-Remote-User"},
GroupHeaders: []string{"X-Remote-Group"},
ExtraHeaderPrefixes: []string{"X-Remote-Extra-"},
}
requestHeaderAuthenticator, err := headerrequest.New(
reqHeaderOptions.UsernameHeaders,
reqHeaderOptions.GroupHeaders,
reqHeaderOptions.ExtraHeaderPrefixes,
)
if err != nil {
return nil, err
}
return requestHeaderAuthenticator, nil
}

View File

@ -0,0 +1,11 @@
package grafanaapiserver
import (
"github.com/google/wire"
)
var WireSet = wire.NewSet(
ProvideService,
wire.Bind(new(Service), new(*service)),
wire.Bind(new(RestConfigProvider), new(*service)),
)

View File

@ -15,7 +15,7 @@
package tempo
import (
"go.opentelemetry.io/collector/model/pdata"
"go.opentelemetry.io/collector/pdata/ptrace"
)
// Some of the keys used to represent OTLP constructs as tags or annotations in other formats.
@ -55,9 +55,9 @@ const (
// StatusCodeFromHTTP takes an HTTP status code and return the appropriate OpenTelemetry status code
// See: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/http.md#status
func StatusCodeFromHTTP(httpStatusCode int) pdata.StatusCode {
func StatusCodeFromHTTP(httpStatusCode int) ptrace.StatusCode {
if httpStatusCode >= 100 && httpStatusCode < 399 {
return pdata.StatusCodeUnset
return ptrace.StatusCodeUnset
}
return pdata.StatusCodeError
return ptrace.StatusCodeError
}

View File

@ -7,7 +7,10 @@ import (
"github.com/grafana/grafana-plugin-sdk-go/data"
"go.opentelemetry.io/collector/model/pdata"
semconv "go.opentelemetry.io/collector/model/semconv/v1.8.0"
"go.opentelemetry.io/collector/pdata/pcommon"
"go.opentelemetry.io/collector/pdata/ptrace"
"go.opentelemetry.io/otel/attribute"
semconv "go.opentelemetry.io/otel/semconv/v1.15.0"
)
type KeyValue struct {
@ -27,7 +30,7 @@ type TraceReference struct {
Tags []*KeyValue `json:"tags"`
}
func TraceToFrame(td pdata.Traces) (*data.Frame, error) {
func TraceToFrame(td ptrace.Traces) (*data.Frame, error) {
// In open telemetry format the spans are grouped first by resource/service they originated in and inside that
// resource they are grouped by the instrumentation library which created them.
@ -80,9 +83,9 @@ func TraceToFrame(td pdata.Traces) (*data.Frame, error) {
}
// resourceSpansToRows processes all the spans for a particular resource/service
func resourceSpansToRows(rs pdata.ResourceSpans) ([][]interface{}, error) {
func resourceSpansToRows(rs ptrace.ResourceSpans) ([][]interface{}, error) {
resource := rs.Resource()
ilss := rs.InstrumentationLibrarySpans()
ilss := rs.ScopeSpans()
if resource.Attributes().Len() == 0 || ilss.Len() == 0 {
return [][]interface{}{}, nil
@ -100,7 +103,7 @@ func resourceSpansToRows(rs pdata.ResourceSpans) ([][]interface{}, error) {
for j := 0; j < spans.Len(); j++ {
span := spans.At(j)
row, err := spanToSpanRow(span, ils.InstrumentationLibrary(), resource)
row, err := spanToSpanRow(span, ils.Scope(), resource)
if err != nil {
return nil, err
}
@ -113,7 +116,7 @@ func resourceSpansToRows(rs pdata.ResourceSpans) ([][]interface{}, error) {
return rows, nil
}
func spanToSpanRow(span pdata.Span, libraryTags pdata.InstrumentationLibrary, resource pdata.Resource) ([]interface{}, error) {
func spanToSpanRow(span ptrace.Span, libraryTags pcommon.InstrumentationScope, resource pcommon.Resource) ([]interface{}, error) {
// If the id representation changed from hexstring to something else we need to change the transformBase64IDToHexString in the frontend code
traceID := span.TraceID().HexString()
traceID = strings.TrimPrefix(traceID, strings.Repeat("0", 16))
@ -175,7 +178,7 @@ func spanToSpanRow(span pdata.Span, libraryTags pdata.InstrumentationLibrary, re
}, nil
}
func resourceToProcess(resource pdata.Resource) (string, []*KeyValue) {
func resourceToProcess(resource pcommon.Resource) (string, []*KeyValue) {
attrs := resource.Attributes()
serviceName := ResourceNoServiceName
if attrs.Len() == 0 {
@ -183,8 +186,8 @@ func resourceToProcess(resource pdata.Resource) (string, []*KeyValue) {
}
tags := make([]*KeyValue, 0, attrs.Len()-1)
attrs.Range(func(key string, attr pdata.AttributeValue) bool {
if key == semconv.AttributeServiceName {
attrs.Range(func(key string, attr pcommon.Value) bool {
if attribute.Key(key) == semconv.ServiceNameKey {
serviceName = attr.StringVal()
}
tags = append(tags, &KeyValue{Key: key, Value: getAttributeVal(attr)})
@ -194,44 +197,44 @@ func resourceToProcess(resource pdata.Resource) (string, []*KeyValue) {
return serviceName, tags
}
func getAttributeVal(attr pdata.AttributeValue) interface{} {
func getAttributeVal(attr pcommon.Value) interface{} {
switch attr.Type() {
case pdata.AttributeValueTypeString:
case pcommon.ValueTypeString:
return attr.StringVal()
case pdata.AttributeValueTypeInt:
case pcommon.ValueTypeInt:
return attr.IntVal()
case pdata.AttributeValueTypeBool:
case pcommon.ValueTypeBool:
return attr.BoolVal()
case pdata.AttributeValueTypeDouble:
case pcommon.ValueTypeDouble:
return attr.DoubleVal()
case pdata.AttributeValueTypeMap, pdata.AttributeValueTypeArray:
case pcommon.ValueTypeMap, pcommon.ValueTypeSlice:
return attr.AsString()
default:
return nil
}
}
func getSpanTags(span pdata.Span) []*KeyValue {
func getSpanTags(span ptrace.Span) []*KeyValue {
var tags []*KeyValue
span.Attributes().Range(func(key string, attr pdata.AttributeValue) bool {
span.Attributes().Range(func(key string, attr pcommon.Value) bool {
tags = append(tags, &KeyValue{Key: key, Value: getAttributeVal(attr)})
return true
})
return tags
}
func getSpanKind(spanKind pdata.SpanKind) string {
func getSpanKind(spanKind ptrace.SpanKind) string {
var tagStr string
switch spanKind {
case pdata.SpanKindClient:
case ptrace.SpanKindClient:
tagStr = string(OpenTracingSpanKindClient)
case pdata.SpanKindServer:
case ptrace.SpanKindServer:
tagStr = string(OpenTracingSpanKindServer)
case pdata.SpanKindProducer:
case ptrace.SpanKindProducer:
tagStr = string(OpenTracingSpanKindProducer)
case pdata.SpanKindConsumer:
case ptrace.SpanKindConsumer:
tagStr = string(OpenTracingSpanKindConsumer)
case pdata.SpanKindInternal:
case ptrace.SpanKindInternal:
tagStr = string(OpenTracingSpanKindInternal)
default:
return ""
@ -240,14 +243,11 @@ func getSpanKind(spanKind pdata.SpanKind) string {
return tagStr
}
func getTraceState(traceState pdata.TraceState) string {
if traceState != pdata.TraceStateEmpty {
return string(traceState)
}
return ""
func getTraceState(traceState ptrace.TraceState) string {
return string(traceState)
}
func spanEventsToLogs(events pdata.SpanEventSlice) []*TraceLog {
func spanEventsToLogs(events ptrace.SpanEventSlice) []*TraceLog {
if events.Len() == 0 {
return nil
}
@ -259,10 +259,10 @@ func spanEventsToLogs(events pdata.SpanEventSlice) []*TraceLog {
if event.Name() != "" {
fields = append(fields, &KeyValue{
Key: TagMessage,
Value: event.Name(),
Value: attribute.StringValue(event.Name()),
})
}
event.Attributes().Range(func(key string, attr pdata.AttributeValue) bool {
event.Attributes().Range(func(key string, attr pcommon.Value) bool {
fields = append(fields, &KeyValue{Key: key, Value: getAttributeVal(attr)})
return true
})
@ -290,7 +290,7 @@ func spanLinksToReferences(links pdata.SpanLinkSlice) []*TraceReference {
spanId := link.SpanID().HexString()
tags := make([]*KeyValue, 0, link.Attributes().Len())
link.Attributes().Range(func(key string, attr pdata.AttributeValue) bool {
link.Attributes().Range(func(key string, attr pcommon.Value) bool {
tags = append(tags, &KeyValue{Key: key, Value: getAttributeVal(attr)})
return true
})

View File

@ -5,15 +5,17 @@ import (
"os"
"testing"
"go.opentelemetry.io/collector/pdata/pcommon"
"go.opentelemetry.io/collector/pdata/ptrace"
"github.com/grafana/grafana-plugin-sdk-go/data"
"github.com/stretchr/testify/require"
otlp "go.opentelemetry.io/collector/model/otlp"
"go.opentelemetry.io/collector/model/pdata"
"go.opentelemetry.io/collector/model/otlp"
)
func TestTraceToFrame(t *testing.T) {
t.Run("should transform tempo protobuf response into dataframe", func(t *testing.T) {
// For what ever reason you cannot easily create pdata.Traces for the TraceToFrame from something more readable
// For what ever reason you cannot easily create ptrace.Traces for the TraceToFrame from something more readable
// like json. You could tediously create the structures manually using all the setters for everything or use
// https://github.com/grafana/tempo/tree/master/pkg/tempopb to create the protobuf structs from something like
// json. At the moment just saving some real tempo proto response into file and loading was the easiest and
@ -60,14 +62,14 @@ func TestTraceToFrame(t *testing.T) {
require.NoError(t, err)
var index int
otTrace.ResourceSpans().RemoveIf(func(rsp pdata.ResourceSpans) bool {
rsp.InstrumentationLibrarySpans().RemoveIf(func(sp pdata.InstrumentationLibrarySpans) bool {
sp.Spans().RemoveIf(func(span pdata.Span) bool {
otTrace.ResourceSpans().RemoveIf(func(rsp ptrace.ResourceSpans) bool {
rsp.ScopeSpans().RemoveIf(func(sp ptrace.ScopeSpans) bool {
sp.Spans().RemoveIf(func(span ptrace.Span) bool {
if index == 0 {
span.SetTraceID(pdata.NewTraceID([16]byte{0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7}))
span.SetTraceID(pcommon.NewTraceID([16]byte{0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7}))
}
if index == 1 {
span.SetTraceID(pdata.NewTraceID([16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7}))
span.SetTraceID(pcommon.NewTraceID([16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7}))
}
index++
return false

View File

@ -19,11 +19,15 @@ import (
"errors"
"net"
"net/http"
"k8s.io/apiserver/pkg/endpoints/responsewriter"
)
var (
_ http.ResponseWriter = &responseWriter{}
_ http.Hijacker = &responseWriter{}
_ http.ResponseWriter = &responseWriter{}
_ http.Hijacker = &responseWriter{}
_ responsewriter.CloseNotifierFlusher = &responseWriter{}
_ responsewriter.UserProvidedDecorator = &responseWriter{}
)
// ResponseWriter is a wrapper around http.ResponseWriter that provides extra information about
@ -41,6 +45,10 @@ type ResponseWriter interface {
// Before allows for a function to be called before the ResponseWriter has been written to. This is
// useful for setting headers or any other operations that must happen before a response has been written.
Before(BeforeFunc)
// Needed to support https://pkg.go.dev/k8s.io/apiserver@v0.27.2/pkg/endpoints/responsewriter#WrapForHTTP1Or2
http.CloseNotifier
Unwrap() http.ResponseWriter
}
// BeforeFunc is a function that is called before the ResponseWriter has been written to.
@ -139,3 +147,16 @@ func (rw *responseWriter) Flush() {
flusher.Flush()
}
}
func (rw *responseWriter) Unwrap() http.ResponseWriter {
return rw.ResponseWriter
}
func (rw *responseWriter) CloseNotify() <-chan bool {
notifier, ok := rw.ResponseWriter.(http.CloseNotifier)
if ok {
return notifier.CloseNotify()
}
// TODO: this is probably not the right thing to do here
return make(<-chan bool)
}