2024-02-01 17:27:30 -05:00
|
|
|
package apiserver
|
2023-07-14 15:22:10 -04:00
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
2023-10-06 11:55:22 -07:00
|
|
|
"fmt"
|
|
|
|
|
"net/http"
|
2023-07-14 15:22:10 -04:00
|
|
|
"path"
|
|
|
|
|
|
|
|
|
|
"github.com/grafana/dskit/services"
|
2024-08-13 15:28:17 -04:00
|
|
|
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
2024-06-14 10:01:49 +01:00
|
|
|
"github.com/prometheus/client_golang/prometheus"
|
2024-08-06 09:52:11 -06:00
|
|
|
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
|
Storage: Unified Storage based on Entity API (#71977)
* first round of entityapi updates
- quote column names and clean up insert/update queries
- replace grn with guid
- streamline table structure
fixes
streamline entity history
move EntitySummary into proto
remove EntitySummary
add guid to json
fix tests
change DB_Uuid to DB_NVarchar
fix folder test
convert interface to any
more cleanup
start entity store under grafana-apiserver dskit target
CRUD working, kind of
rough cut of wiring entity api to kube-apiserver
fake grafana user in context
add key to entity
list working
revert unnecessary changes
move entity storage files to their own package, clean up
use accessor to read/write grafana annotations
implement separate Create and Update functions
* go mod tidy
* switch from Kind to resource
* basic grpc storage server
* basic support for grpc entity store
* don't connect to database unless it's needed, pass user identity over grpc
* support getting user from k8s context, fix some mysql issues
* assign owner to snowflake dependency
* switch from ulid to uuid for guids
* cleanup, rename Search to List
* remove entityListResult
* EntityAPI: remove extra user abstraction (#79033)
* remove extra user abstraction
* add test stub (but
* move grpc context setup into client wrapper, fix lint issue
* remove unused constants
* remove custom json stuff
* basic list filtering, add todo
* change target to storage-server, allow entityStore flag in prod mode
* fix issue with Update
* EntityAPI: make test work, need to resolve expected differences (#79123)
* make test work, need to resolve expected differences
* remove the fields not supported by legacy
* sanitize out the bits legacy does not support
* sanitize out the bits legacy does not support
---------
Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
* update feature toggle generated files
* remove unused http headers
* update feature flag strategy
* devmode
* update readme
* spelling
* readme
---------
Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
2023-12-06 21:21:21 +01:00
|
|
|
"google.golang.org/grpc"
|
|
|
|
|
"google.golang.org/grpc/credentials/insecure"
|
2023-09-22 14:17:53 -04:00
|
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
|
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
|
|
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
|
|
|
|
"k8s.io/apimachinery/pkg/runtime/serializer"
|
2023-09-25 18:31:58 -04:00
|
|
|
"k8s.io/apiserver/pkg/endpoints/responsewriter"
|
2023-07-14 15:22:10 -04:00
|
|
|
genericapiserver "k8s.io/apiserver/pkg/server"
|
2023-09-22 14:17:53 -04:00
|
|
|
clientrest "k8s.io/client-go/rest"
|
2023-07-14 15:22:10 -04:00
|
|
|
"k8s.io/client-go/tools/clientcmd"
|
2024-08-13 15:28:17 -04:00
|
|
|
aggregatorapiserver "k8s.io/kube-aggregator/pkg/apiserver"
|
2023-12-05 14:31:49 -08:00
|
|
|
|
2024-08-13 15:28:17 -04:00
|
|
|
dataplaneaggregator "github.com/grafana/grafana/pkg/aggregator/apiserver"
|
2023-09-25 18:31:58 -04:00
|
|
|
"github.com/grafana/grafana/pkg/api/routing"
|
2024-07-26 16:39:23 +03:00
|
|
|
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
2024-02-23 15:15:43 -05:00
|
|
|
grafanaresponsewriter "github.com/grafana/grafana/pkg/apiserver/endpoints/responsewriter"
|
Storage: Unified Storage based on Entity API (#71977)
* first round of entityapi updates
- quote column names and clean up insert/update queries
- replace grn with guid
- streamline table structure
fixes
streamline entity history
move EntitySummary into proto
remove EntitySummary
add guid to json
fix tests
change DB_Uuid to DB_NVarchar
fix folder test
convert interface to any
more cleanup
start entity store under grafana-apiserver dskit target
CRUD working, kind of
rough cut of wiring entity api to kube-apiserver
fake grafana user in context
add key to entity
list working
revert unnecessary changes
move entity storage files to their own package, clean up
use accessor to read/write grafana annotations
implement separate Create and Update functions
* go mod tidy
* switch from Kind to resource
* basic grpc storage server
* basic support for grpc entity store
* don't connect to database unless it's needed, pass user identity over grpc
* support getting user from k8s context, fix some mysql issues
* assign owner to snowflake dependency
* switch from ulid to uuid for guids
* cleanup, rename Search to List
* remove entityListResult
* EntityAPI: remove extra user abstraction (#79033)
* remove extra user abstraction
* add test stub (but
* move grpc context setup into client wrapper, fix lint issue
* remove unused constants
* remove custom json stuff
* basic list filtering, add todo
* change target to storage-server, allow entityStore flag in prod mode
* fix issue with Update
* EntityAPI: make test work, need to resolve expected differences (#79123)
* make test work, need to resolve expected differences
* remove the fields not supported by legacy
* sanitize out the bits legacy does not support
* sanitize out the bits legacy does not support
---------
Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
* update feature toggle generated files
* remove unused http headers
* update feature flag strategy
* devmode
* update readme
* spelling
* readme
---------
Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
2023-12-06 21:21:21 +01:00
|
|
|
"github.com/grafana/grafana/pkg/infra/db"
|
2024-07-11 13:23:31 -07:00
|
|
|
"github.com/grafana/grafana/pkg/infra/kvstore"
|
2024-06-14 10:01:49 +01:00
|
|
|
"github.com/grafana/grafana/pkg/infra/metrics"
|
2024-07-11 13:23:31 -07:00
|
|
|
"github.com/grafana/grafana/pkg/infra/serverlock"
|
2023-10-25 15:19:44 -04:00
|
|
|
"github.com/grafana/grafana/pkg/infra/tracing"
|
2023-09-25 18:31:58 -04:00
|
|
|
"github.com/grafana/grafana/pkg/middleware"
|
2023-09-08 10:12:12 -04:00
|
|
|
"github.com/grafana/grafana/pkg/modules"
|
2024-08-13 15:28:17 -04:00
|
|
|
"github.com/grafana/grafana/pkg/plugins"
|
2023-09-25 18:31:58 -04:00
|
|
|
"github.com/grafana/grafana/pkg/registry"
|
2024-08-13 15:28:17 -04:00
|
|
|
"github.com/grafana/grafana/pkg/registry/apis/datasource"
|
|
|
|
|
kubeaggregator "github.com/grafana/grafana/pkg/services/apiserver/aggregator"
|
2024-02-15 12:29:36 -05:00
|
|
|
"github.com/grafana/grafana/pkg/services/apiserver/auth/authenticator"
|
2024-02-01 17:27:30 -05:00
|
|
|
"github.com/grafana/grafana/pkg/services/apiserver/auth/authorizer"
|
2024-07-01 11:42:34 -04:00
|
|
|
"github.com/grafana/grafana/pkg/services/apiserver/builder"
|
2024-03-05 10:34:47 -08:00
|
|
|
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
|
2024-02-01 17:27:30 -05:00
|
|
|
grafanaapiserveroptions "github.com/grafana/grafana/pkg/services/apiserver/options"
|
|
|
|
|
"github.com/grafana/grafana/pkg/services/apiserver/utils"
|
2023-09-25 18:31:58 -04:00
|
|
|
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
2023-11-13 11:39:01 -08:00
|
|
|
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
2024-02-01 17:27:30 -05:00
|
|
|
"github.com/grafana/grafana/pkg/services/org"
|
2024-08-13 15:28:17 -04:00
|
|
|
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore"
|
2023-09-25 18:31:58 -04:00
|
|
|
"github.com/grafana/grafana/pkg/setting"
|
2024-07-09 15:08:13 -07:00
|
|
|
"github.com/grafana/grafana/pkg/storage/unified/apistore"
|
|
|
|
|
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
2024-07-18 17:03:18 +02:00
|
|
|
"github.com/grafana/grafana/pkg/storage/unified/sql"
|
2023-07-14 15:22:10 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
var (
|
2023-09-25 18:31:58 -04:00
|
|
|
_ Service = (*service)(nil)
|
|
|
|
|
_ RestConfigProvider = (*service)(nil)
|
|
|
|
|
_ registry.BackgroundService = (*service)(nil)
|
|
|
|
|
_ registry.CanBeDisabled = (*service)(nil)
|
2023-07-14 15:22:10 -04:00
|
|
|
|
2023-09-22 14:17:53 -04:00
|
|
|
Scheme = runtime.NewScheme()
|
|
|
|
|
Codecs = serializer.NewCodecFactory(Scheme)
|
|
|
|
|
|
|
|
|
|
unversionedVersion = schema.GroupVersion{Group: "", Version: "v1"}
|
|
|
|
|
unversionedTypes = []runtime.Object{
|
|
|
|
|
&metav1.Status{},
|
|
|
|
|
&metav1.WatchEvent{},
|
|
|
|
|
&metav1.APIVersions{},
|
|
|
|
|
&metav1.APIGroupList{},
|
|
|
|
|
&metav1.APIGroup{},
|
|
|
|
|
&metav1.APIResourceList{},
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
|
// we need to add the options to empty v1
|
|
|
|
|
metav1.AddToGroupVersion(Scheme, schema.GroupVersion{Group: "", Version: "v1"})
|
|
|
|
|
Scheme.AddUnversionedTypes(unversionedVersion, unversionedTypes...)
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-14 15:22:10 -04:00
|
|
|
type Service interface {
|
|
|
|
|
services.NamedService
|
2023-09-25 18:31:58 -04:00
|
|
|
registry.BackgroundService
|
|
|
|
|
registry.CanBeDisabled
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-14 15:22:10 -04:00
|
|
|
type RestConfigProvider interface {
|
2023-09-22 14:17:53 -04:00
|
|
|
GetRestConfig() *clientrest.Config
|
2023-07-14 15:22:10 -04:00
|
|
|
}
|
|
|
|
|
|
2023-10-31 10:26:39 -07:00
|
|
|
type DirectRestConfigProvider interface {
|
|
|
|
|
// GetDirectRestConfig returns a k8s client configuration that will use the same
|
|
|
|
|
// logged logged in user as the current request context. This is useful when
|
|
|
|
|
// creating clients that map legacy API handlers to k8s backed services
|
|
|
|
|
GetDirectRestConfig(c *contextmodel.ReqContext) *clientrest.Config
|
2024-01-31 10:36:51 -08:00
|
|
|
|
|
|
|
|
// This can be used to rewrite incoming requests to path now supported under /apis
|
|
|
|
|
DirectlyServeHTTP(w http.ResponseWriter, r *http.Request)
|
2023-10-31 10:26:39 -07:00
|
|
|
}
|
|
|
|
|
|
2023-07-14 15:22:10 -04:00
|
|
|
type service struct {
|
|
|
|
|
*services.BasicService
|
|
|
|
|
|
2024-02-01 17:27:30 -05:00
|
|
|
options *grafanaapiserveroptions.Options
|
2023-10-17 11:29:06 -04:00
|
|
|
restConfig *clientrest.Config
|
2023-07-14 15:22:10 -04:00
|
|
|
|
Storage: Unified Storage based on Entity API (#71977)
* first round of entityapi updates
- quote column names and clean up insert/update queries
- replace grn with guid
- streamline table structure
fixes
streamline entity history
move EntitySummary into proto
remove EntitySummary
add guid to json
fix tests
change DB_Uuid to DB_NVarchar
fix folder test
convert interface to any
more cleanup
start entity store under grafana-apiserver dskit target
CRUD working, kind of
rough cut of wiring entity api to kube-apiserver
fake grafana user in context
add key to entity
list working
revert unnecessary changes
move entity storage files to their own package, clean up
use accessor to read/write grafana annotations
implement separate Create and Update functions
* go mod tidy
* switch from Kind to resource
* basic grpc storage server
* basic support for grpc entity store
* don't connect to database unless it's needed, pass user identity over grpc
* support getting user from k8s context, fix some mysql issues
* assign owner to snowflake dependency
* switch from ulid to uuid for guids
* cleanup, rename Search to List
* remove entityListResult
* EntityAPI: remove extra user abstraction (#79033)
* remove extra user abstraction
* add test stub (but
* move grpc context setup into client wrapper, fix lint issue
* remove unused constants
* remove custom json stuff
* basic list filtering, add todo
* change target to storage-server, allow entityStore flag in prod mode
* fix issue with Update
* EntityAPI: make test work, need to resolve expected differences (#79123)
* make test work, need to resolve expected differences
* remove the fields not supported by legacy
* sanitize out the bits legacy does not support
* sanitize out the bits legacy does not support
---------
Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
* update feature toggle generated files
* remove unused http headers
* update feature flag strategy
* devmode
* update readme
* spelling
* readme
---------
Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
2023-12-06 21:21:21 +01:00
|
|
|
cfg *setting.Cfg
|
|
|
|
|
features featuremgmt.FeatureToggles
|
|
|
|
|
|
2024-06-05 14:23:32 -03:00
|
|
|
startedCh chan struct{}
|
2023-07-14 15:22:10 -04:00
|
|
|
stopCh chan struct{}
|
|
|
|
|
stoppedCh chan error
|
2023-09-25 18:31:58 -04:00
|
|
|
|
Storage: Unified Storage based on Entity API (#71977)
* first round of entityapi updates
- quote column names and clean up insert/update queries
- replace grn with guid
- streamline table structure
fixes
streamline entity history
move EntitySummary into proto
remove EntitySummary
add guid to json
fix tests
change DB_Uuid to DB_NVarchar
fix folder test
convert interface to any
more cleanup
start entity store under grafana-apiserver dskit target
CRUD working, kind of
rough cut of wiring entity api to kube-apiserver
fake grafana user in context
add key to entity
list working
revert unnecessary changes
move entity storage files to their own package, clean up
use accessor to read/write grafana annotations
implement separate Create and Update functions
* go mod tidy
* switch from Kind to resource
* basic grpc storage server
* basic support for grpc entity store
* don't connect to database unless it's needed, pass user identity over grpc
* support getting user from k8s context, fix some mysql issues
* assign owner to snowflake dependency
* switch from ulid to uuid for guids
* cleanup, rename Search to List
* remove entityListResult
* EntityAPI: remove extra user abstraction (#79033)
* remove extra user abstraction
* add test stub (but
* move grpc context setup into client wrapper, fix lint issue
* remove unused constants
* remove custom json stuff
* basic list filtering, add todo
* change target to storage-server, allow entityStore flag in prod mode
* fix issue with Update
* EntityAPI: make test work, need to resolve expected differences (#79123)
* make test work, need to resolve expected differences
* remove the fields not supported by legacy
* sanitize out the bits legacy does not support
* sanitize out the bits legacy does not support
---------
Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
* update feature toggle generated files
* remove unused http headers
* update feature flag strategy
* devmode
* update readme
* spelling
* readme
---------
Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
2023-12-06 21:21:21 +01:00
|
|
|
db db.DB
|
2023-09-25 18:31:58 -04:00
|
|
|
rr routing.RouteRegister
|
2023-10-31 10:26:39 -07:00
|
|
|
handler http.Handler
|
2024-02-01 17:27:30 -05:00
|
|
|
builders []builder.APIGroupBuilder
|
2023-09-28 18:28:58 -04:00
|
|
|
|
2023-10-25 15:19:44 -04:00
|
|
|
tracing *tracing.TracingService
|
2024-06-14 10:01:49 +01:00
|
|
|
metrics prometheus.Registerer
|
2023-10-25 15:19:44 -04:00
|
|
|
|
2024-07-11 13:23:31 -07:00
|
|
|
authorizer *authorizer.GrafanaAuthorizer
|
|
|
|
|
serverLockService builder.ServerLockService
|
|
|
|
|
kvStore kvstore.KVStore
|
2024-08-13 15:28:17 -04:00
|
|
|
|
|
|
|
|
pluginClient plugins.Client
|
|
|
|
|
datasources datasource.ScopedPluginDatasourceProvider
|
|
|
|
|
contextProvider datasource.PluginContextWrapper
|
|
|
|
|
pluginStore pluginstore.Store
|
2023-07-14 15:22:10 -04:00
|
|
|
}
|
|
|
|
|
|
2023-09-26 17:15:15 -04:00
|
|
|
func ProvideService(
|
|
|
|
|
cfg *setting.Cfg,
|
2023-11-13 11:39:01 -08:00
|
|
|
features featuremgmt.FeatureToggles,
|
2023-09-25 18:31:58 -04:00
|
|
|
rr routing.RouteRegister,
|
2023-12-19 09:12:35 -08:00
|
|
|
orgService org.Service,
|
2023-10-25 15:19:44 -04:00
|
|
|
tracing *tracing.TracingService,
|
2024-07-11 13:23:31 -07:00
|
|
|
serverLockService *serverlock.ServerLockService,
|
Storage: Unified Storage based on Entity API (#71977)
* first round of entityapi updates
- quote column names and clean up insert/update queries
- replace grn with guid
- streamline table structure
fixes
streamline entity history
move EntitySummary into proto
remove EntitySummary
add guid to json
fix tests
change DB_Uuid to DB_NVarchar
fix folder test
convert interface to any
more cleanup
start entity store under grafana-apiserver dskit target
CRUD working, kind of
rough cut of wiring entity api to kube-apiserver
fake grafana user in context
add key to entity
list working
revert unnecessary changes
move entity storage files to their own package, clean up
use accessor to read/write grafana annotations
implement separate Create and Update functions
* go mod tidy
* switch from Kind to resource
* basic grpc storage server
* basic support for grpc entity store
* don't connect to database unless it's needed, pass user identity over grpc
* support getting user from k8s context, fix some mysql issues
* assign owner to snowflake dependency
* switch from ulid to uuid for guids
* cleanup, rename Search to List
* remove entityListResult
* EntityAPI: remove extra user abstraction (#79033)
* remove extra user abstraction
* add test stub (but
* move grpc context setup into client wrapper, fix lint issue
* remove unused constants
* remove custom json stuff
* basic list filtering, add todo
* change target to storage-server, allow entityStore flag in prod mode
* fix issue with Update
* EntityAPI: make test work, need to resolve expected differences (#79123)
* make test work, need to resolve expected differences
* remove the fields not supported by legacy
* sanitize out the bits legacy does not support
* sanitize out the bits legacy does not support
---------
Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
* update feature toggle generated files
* remove unused http headers
* update feature flag strategy
* devmode
* update readme
* spelling
* readme
---------
Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
2023-12-06 21:21:21 +01:00
|
|
|
db db.DB,
|
2024-07-11 13:23:31 -07:00
|
|
|
kvStore kvstore.KVStore,
|
2024-08-13 15:28:17 -04:00
|
|
|
pluginClient plugins.Client,
|
|
|
|
|
datasources datasource.ScopedPluginDatasourceProvider,
|
|
|
|
|
contextProvider datasource.PluginContextWrapper,
|
|
|
|
|
pluginStore pluginstore.Store,
|
2023-09-25 18:31:58 -04:00
|
|
|
) (*service, error) {
|
2023-07-14 15:22:10 -04:00
|
|
|
s := &service{
|
2024-08-27 13:31:40 -03:00
|
|
|
cfg: cfg,
|
|
|
|
|
features: features,
|
|
|
|
|
rr: rr,
|
|
|
|
|
startedCh: make(chan struct{}),
|
|
|
|
|
stopCh: make(chan struct{}),
|
|
|
|
|
builders: []builder.APIGroupBuilder{},
|
|
|
|
|
authorizer: authorizer.NewGrafanaAuthorizer(cfg, orgService),
|
|
|
|
|
tracing: tracing,
|
|
|
|
|
db: db, // For Unified storage
|
|
|
|
|
metrics: metrics.ProvideRegisterer(),
|
|
|
|
|
kvStore: kvStore,
|
|
|
|
|
pluginClient: pluginClient,
|
|
|
|
|
datasources: datasources,
|
|
|
|
|
contextProvider: contextProvider,
|
|
|
|
|
pluginStore: pluginStore,
|
|
|
|
|
serverLockService: serverLockService,
|
2023-07-14 15:22:10 -04:00
|
|
|
}
|
|
|
|
|
|
2023-09-25 18:31:58 -04:00
|
|
|
// This will be used when running as a dskit service
|
2023-08-31 09:12:01 -04:00
|
|
|
s.BasicService = services.NewBasicService(s.start, s.running, nil).WithName(modules.GrafanaAPIServer)
|
2023-07-14 15:22:10 -04:00
|
|
|
|
2023-09-25 18:31:58 -04:00
|
|
|
// TODO: this is very hacky
|
|
|
|
|
// We need to register the routes in ProvideService to make sure
|
|
|
|
|
// the routes are registered before the Grafana HTTP server starts.
|
2023-10-04 14:05:50 -04:00
|
|
|
proxyHandler := func(k8sRoute routing.RouteRegister) {
|
2023-09-25 18:31:58 -04:00
|
|
|
handler := func(c *contextmodel.ReqContext) {
|
2024-06-05 14:23:32 -03:00
|
|
|
<-s.startedCh
|
2023-09-25 18:31:58 -04:00
|
|
|
if s.handler == nil {
|
|
|
|
|
c.Resp.WriteHeader(404)
|
|
|
|
|
_, _ = c.Resp.Write([]byte("Not found"))
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-31 10:26:39 -07:00
|
|
|
req := c.Req
|
|
|
|
|
if req.URL.Path == "" {
|
|
|
|
|
req.URL.Path = "/"
|
2023-09-25 18:31:58 -04:00
|
|
|
}
|
2023-10-31 10:26:39 -07:00
|
|
|
|
2024-06-20 17:53:07 +03:00
|
|
|
if c.SignedInUser != nil {
|
2024-07-26 16:39:23 +03:00
|
|
|
ctx := identity.WithRequester(req.Context(), c.SignedInUser)
|
2024-06-20 17:53:07 +03:00
|
|
|
req = req.WithContext(ctx)
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-31 10:26:39 -07:00
|
|
|
resp := responsewriter.WrapForHTTP1Or2(c.Resp)
|
|
|
|
|
s.handler.ServeHTTP(resp, req)
|
2023-09-25 18:31:58 -04:00
|
|
|
}
|
|
|
|
|
k8sRoute.Any("/", middleware.ReqSignedIn, handler)
|
|
|
|
|
k8sRoute.Any("/*", middleware.ReqSignedIn, handler)
|
2023-10-04 14:05:50 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
s.rr.Group("/apis", proxyHandler)
|
2024-02-01 17:27:30 -05:00
|
|
|
s.rr.Group("/livez", proxyHandler)
|
|
|
|
|
s.rr.Group("/readyz", proxyHandler)
|
|
|
|
|
s.rr.Group("/healthz", proxyHandler)
|
2023-10-04 14:05:50 -04:00
|
|
|
s.rr.Group("/openapi", proxyHandler)
|
2024-03-05 10:57:32 -05:00
|
|
|
s.rr.Group("/version", proxyHandler)
|
2023-09-25 18:31:58 -04:00
|
|
|
|
2023-07-14 15:22:10 -04:00
|
|
|
return s, nil
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-22 14:17:53 -04:00
|
|
|
func (s *service) GetRestConfig() *clientrest.Config {
|
2023-07-14 15:22:10 -04:00
|
|
|
return s.restConfig
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-25 18:31:58 -04:00
|
|
|
func (s *service) IsDisabled() bool {
|
2024-02-01 17:27:30 -05:00
|
|
|
return false
|
2023-09-25 18:31:58 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Run is an adapter for the BackgroundService interface.
|
|
|
|
|
func (s *service) Run(ctx context.Context) error {
|
|
|
|
|
if err := s.start(ctx); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
return s.running(ctx)
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-01 17:27:30 -05:00
|
|
|
func (s *service) RegisterAPI(b builder.APIGroupBuilder) {
|
|
|
|
|
s.builders = append(s.builders, b)
|
2023-09-25 18:31:58 -04:00
|
|
|
}
|
|
|
|
|
|
2024-07-09 15:08:13 -07:00
|
|
|
// nolint:gocyclo
|
2023-07-14 15:22:10 -04:00
|
|
|
func (s *service) start(ctx context.Context) error {
|
2024-06-05 14:23:32 -03:00
|
|
|
defer close(s.startedCh)
|
|
|
|
|
|
2023-10-24 10:19:17 -04:00
|
|
|
// Get the list of groups the server will support
|
|
|
|
|
builders := s.builders
|
|
|
|
|
|
|
|
|
|
groupVersions := make([]schema.GroupVersion, 0, len(builders))
|
|
|
|
|
// Install schemas
|
2024-08-13 15:28:17 -04:00
|
|
|
initialSize := len(kubeaggregator.APIVersionPriorities)
|
2024-02-12 15:59:35 -05:00
|
|
|
for i, b := range builders {
|
2023-10-24 10:19:17 -04:00
|
|
|
groupVersions = append(groupVersions, b.GetGroupVersion())
|
|
|
|
|
if err := b.InstallSchema(Scheme); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2023-12-19 09:12:35 -08:00
|
|
|
|
2024-02-12 15:59:35 -05:00
|
|
|
if s.features.IsEnabledGlobally(featuremgmt.FlagKubernetesAggregator) {
|
|
|
|
|
// set the priority for the group+version
|
2024-08-13 15:28:17 -04:00
|
|
|
kubeaggregator.APIVersionPriorities[b.GetGroupVersion()] = kubeaggregator.Priority{Group: 15000, Version: int32(i + initialSize)}
|
2024-02-12 15:59:35 -05:00
|
|
|
}
|
|
|
|
|
|
2023-12-19 09:12:35 -08:00
|
|
|
auth := b.GetAuthorizer()
|
|
|
|
|
if auth != nil {
|
|
|
|
|
s.authorizer.Register(b.GetGroupVersion(), auth)
|
|
|
|
|
}
|
2023-10-24 10:19:17 -04:00
|
|
|
}
|
|
|
|
|
|
2024-02-01 17:27:30 -05:00
|
|
|
o := grafanaapiserveroptions.NewOptions(Codecs.LegacyCodec(groupVersions...))
|
2024-05-07 02:17:03 +08:00
|
|
|
err := applyGrafanaConfig(s.cfg, s.features, o)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2023-09-26 17:15:15 -04:00
|
|
|
|
2024-02-01 17:27:30 -05:00
|
|
|
if errs := o.Validate(); len(errs) != 0 {
|
|
|
|
|
// TODO: handle multiple errors
|
|
|
|
|
return errs[0]
|
|
|
|
|
}
|
2023-07-14 15:22:10 -04:00
|
|
|
|
2024-08-30 10:59:42 +01:00
|
|
|
// This will check that required feature toggles are enabled for more advanced storage modes
|
|
|
|
|
// Any required preconditions should be hardcoded here
|
|
|
|
|
if o.StorageOptions != nil {
|
|
|
|
|
if err := o.StorageOptions.EnforceFeatureToggleAfterMode1(s.features); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-22 14:17:53 -04:00
|
|
|
serverConfig := genericapiserver.NewRecommendedConfig(Codecs)
|
2024-02-01 17:27:30 -05:00
|
|
|
if err := o.ApplyTo(serverConfig); err != nil {
|
|
|
|
|
return err
|
2023-07-14 15:22:10 -04:00
|
|
|
}
|
2024-02-01 17:27:30 -05:00
|
|
|
serverConfig.Authorization.Authorizer = s.authorizer
|
2024-02-15 12:29:36 -05:00
|
|
|
serverConfig.Authentication.Authenticator = authenticator.NewAuthenticator(serverConfig.Authentication.Authenticator)
|
2024-02-01 17:27:30 -05:00
|
|
|
serverConfig.TracerProvider = s.tracing.GetTracerProvider()
|
2023-07-14 15:22:10 -04:00
|
|
|
|
2024-02-12 15:59:35 -05:00
|
|
|
// setup loopback transport for the aggregator server
|
2024-02-01 17:27:30 -05:00
|
|
|
transport := &roundTripperFunc{ready: make(chan struct{})}
|
|
|
|
|
serverConfig.LoopbackClientConfig.Transport = transport
|
|
|
|
|
serverConfig.LoopbackClientConfig.TLSClientConfig = clientrest.TLSClientConfig{}
|
|
|
|
|
|
|
|
|
|
switch o.StorageOptions.StorageType {
|
|
|
|
|
case grafanaapiserveroptions.StorageTypeEtcd:
|
|
|
|
|
if err := o.RecommendedOptions.Etcd.Validate(); len(err) > 0 {
|
2023-10-27 16:39:27 -04:00
|
|
|
return err[0]
|
|
|
|
|
}
|
2024-02-01 17:27:30 -05:00
|
|
|
if err := o.RecommendedOptions.Etcd.ApplyTo(&serverConfig.Config); err != nil {
|
2023-10-23 14:42:10 -04:00
|
|
|
return err
|
|
|
|
|
}
|
2023-07-14 15:22:10 -04:00
|
|
|
|
2024-07-25 18:17:39 +02:00
|
|
|
case grafanaapiserveroptions.StorageTypeUnified:
|
2024-07-18 17:03:18 +02:00
|
|
|
server, err := sql.ProvideResourceServer(s.db, s.cfg, s.features, s.tracing)
|
2024-07-09 15:08:13 -07:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2024-07-18 07:47:47 -07:00
|
|
|
client := resource.NewLocalResourceStoreClient(server)
|
|
|
|
|
serverConfig.Config.RESTOptionsGetter = apistore.NewRESTOptionsGetterForClient(client,
|
|
|
|
|
o.RecommendedOptions.Etcd.StorageConfig)
|
Storage: Unified Storage based on Entity API (#71977)
* first round of entityapi updates
- quote column names and clean up insert/update queries
- replace grn with guid
- streamline table structure
fixes
streamline entity history
move EntitySummary into proto
remove EntitySummary
add guid to json
fix tests
change DB_Uuid to DB_NVarchar
fix folder test
convert interface to any
more cleanup
start entity store under grafana-apiserver dskit target
CRUD working, kind of
rough cut of wiring entity api to kube-apiserver
fake grafana user in context
add key to entity
list working
revert unnecessary changes
move entity storage files to their own package, clean up
use accessor to read/write grafana annotations
implement separate Create and Update functions
* go mod tidy
* switch from Kind to resource
* basic grpc storage server
* basic support for grpc entity store
* don't connect to database unless it's needed, pass user identity over grpc
* support getting user from k8s context, fix some mysql issues
* assign owner to snowflake dependency
* switch from ulid to uuid for guids
* cleanup, rename Search to List
* remove entityListResult
* EntityAPI: remove extra user abstraction (#79033)
* remove extra user abstraction
* add test stub (but
* move grpc context setup into client wrapper, fix lint issue
* remove unused constants
* remove custom json stuff
* basic list filtering, add todo
* change target to storage-server, allow entityStore flag in prod mode
* fix issue with Update
* EntityAPI: make test work, need to resolve expected differences (#79123)
* make test work, need to resolve expected differences
* remove the fields not supported by legacy
* sanitize out the bits legacy does not support
* sanitize out the bits legacy does not support
---------
Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
* update feature toggle generated files
* remove unused http headers
* update feature flag strategy
* devmode
* update readme
* spelling
* readme
---------
Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
2023-12-06 21:21:21 +01:00
|
|
|
|
2024-07-25 18:17:39 +02:00
|
|
|
case grafanaapiserveroptions.StorageTypeUnifiedGrpc:
|
2024-08-06 09:52:11 -06:00
|
|
|
opts := []grpc.DialOption{
|
|
|
|
|
grpc.WithStatsHandler(otelgrpc.NewClientHandler()),
|
|
|
|
|
grpc.WithTransportCredentials(insecure.NewCredentials()),
|
|
|
|
|
}
|
Storage: Unified Storage based on Entity API (#71977)
* first round of entityapi updates
- quote column names and clean up insert/update queries
- replace grn with guid
- streamline table structure
fixes
streamline entity history
move EntitySummary into proto
remove EntitySummary
add guid to json
fix tests
change DB_Uuid to DB_NVarchar
fix folder test
convert interface to any
more cleanup
start entity store under grafana-apiserver dskit target
CRUD working, kind of
rough cut of wiring entity api to kube-apiserver
fake grafana user in context
add key to entity
list working
revert unnecessary changes
move entity storage files to their own package, clean up
use accessor to read/write grafana annotations
implement separate Create and Update functions
* go mod tidy
* switch from Kind to resource
* basic grpc storage server
* basic support for grpc entity store
* don't connect to database unless it's needed, pass user identity over grpc
* support getting user from k8s context, fix some mysql issues
* assign owner to snowflake dependency
* switch from ulid to uuid for guids
* cleanup, rename Search to List
* remove entityListResult
* EntityAPI: remove extra user abstraction (#79033)
* remove extra user abstraction
* add test stub (but
* move grpc context setup into client wrapper, fix lint issue
* remove unused constants
* remove custom json stuff
* basic list filtering, add todo
* change target to storage-server, allow entityStore flag in prod mode
* fix issue with Update
* EntityAPI: make test work, need to resolve expected differences (#79123)
* make test work, need to resolve expected differences
* remove the fields not supported by legacy
* sanitize out the bits legacy does not support
* sanitize out the bits legacy does not support
---------
Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
* update feature toggle generated files
* remove unused http headers
* update feature flag strategy
* devmode
* update readme
* spelling
* readme
---------
Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
2023-12-06 21:21:21 +01:00
|
|
|
// Create a connection to the gRPC server
|
2024-08-06 09:52:11 -06:00
|
|
|
conn, err := grpc.NewClient(o.StorageOptions.Address, opts...)
|
Storage: Unified Storage based on Entity API (#71977)
* first round of entityapi updates
- quote column names and clean up insert/update queries
- replace grn with guid
- streamline table structure
fixes
streamline entity history
move EntitySummary into proto
remove EntitySummary
add guid to json
fix tests
change DB_Uuid to DB_NVarchar
fix folder test
convert interface to any
more cleanup
start entity store under grafana-apiserver dskit target
CRUD working, kind of
rough cut of wiring entity api to kube-apiserver
fake grafana user in context
add key to entity
list working
revert unnecessary changes
move entity storage files to their own package, clean up
use accessor to read/write grafana annotations
implement separate Create and Update functions
* go mod tidy
* switch from Kind to resource
* basic grpc storage server
* basic support for grpc entity store
* don't connect to database unless it's needed, pass user identity over grpc
* support getting user from k8s context, fix some mysql issues
* assign owner to snowflake dependency
* switch from ulid to uuid for guids
* cleanup, rename Search to List
* remove entityListResult
* EntityAPI: remove extra user abstraction (#79033)
* remove extra user abstraction
* add test stub (but
* move grpc context setup into client wrapper, fix lint issue
* remove unused constants
* remove custom json stuff
* basic list filtering, add todo
* change target to storage-server, allow entityStore flag in prod mode
* fix issue with Update
* EntityAPI: make test work, need to resolve expected differences (#79123)
* make test work, need to resolve expected differences
* remove the fields not supported by legacy
* sanitize out the bits legacy does not support
* sanitize out the bits legacy does not support
---------
Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
* update feature toggle generated files
* remove unused http headers
* update feature flag strategy
* devmode
* update readme
* spelling
* readme
---------
Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
2023-12-06 21:21:21 +01:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create a client instance
|
2024-07-09 15:08:13 -07:00
|
|
|
client := resource.NewResourceStoreClientGRPC(conn)
|
2024-07-18 07:47:47 -07:00
|
|
|
serverConfig.Config.RESTOptionsGetter = apistore.NewRESTOptionsGetterForClient(client, o.RecommendedOptions.Etcd.StorageConfig)
|
2024-07-09 15:08:13 -07:00
|
|
|
|
2024-02-01 17:27:30 -05:00
|
|
|
case grafanaapiserveroptions.StorageTypeLegacy:
|
|
|
|
|
fallthrough
|
|
|
|
|
case grafanaapiserveroptions.StorageTypeFile:
|
2024-07-18 07:47:47 -07:00
|
|
|
restOptionsGetter, err := apistore.NewRESTOptionsGetterForFile(o.StorageOptions.DataPath, o.RecommendedOptions.Etcd.StorageConfig)
|
2024-03-05 10:34:47 -08:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
serverConfig.RESTOptionsGetter = restOptionsGetter
|
2023-10-27 16:39:27 -04:00
|
|
|
}
|
|
|
|
|
|
2023-09-22 18:29:43 -07:00
|
|
|
// Add OpenAPI specs for each group+version
|
2024-05-07 02:17:03 +08:00
|
|
|
err = builder.SetupConfig(
|
2024-02-23 15:15:43 -05:00
|
|
|
Scheme,
|
|
|
|
|
serverConfig,
|
|
|
|
|
builders,
|
|
|
|
|
s.cfg.BuildStamp,
|
|
|
|
|
s.cfg.BuildVersion,
|
|
|
|
|
s.cfg.BuildCommit,
|
|
|
|
|
s.cfg.BuildBranch,
|
|
|
|
|
)
|
2023-11-14 12:29:15 -08:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2023-10-25 15:19:44 -04:00
|
|
|
|
2023-09-22 18:29:43 -07:00
|
|
|
// Create the server
|
2023-09-22 14:17:53 -04:00
|
|
|
server, err := serverConfig.Complete().New("grafana-apiserver", genericapiserver.NewEmptyDelegate())
|
2023-07-14 15:22:10 -04:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-12 15:59:35 -05:00
|
|
|
// Install the API group+version
|
2024-07-11 13:23:31 -07:00
|
|
|
err = builder.InstallAPIs(Scheme, Codecs, server, serverConfig.RESTOptionsGetter, builders, o.StorageOptions,
|
|
|
|
|
// Required for the dual writer initialization
|
2024-08-27 13:31:40 -03:00
|
|
|
s.metrics, request.GetNamespaceMapper(s.cfg), kvstore.WithNamespace(s.kvStore, 0, "storage.dualwriting"), s.serverLockService,
|
2024-07-11 13:23:31 -07:00
|
|
|
)
|
2024-01-10 07:30:16 -08:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
2023-07-14 15:22:10 -04:00
|
|
|
}
|
|
|
|
|
|
2024-02-12 15:59:35 -05:00
|
|
|
// stash the options for later use
|
|
|
|
|
s.options = o
|
|
|
|
|
|
2024-08-13 15:28:17 -04:00
|
|
|
delegate := server
|
|
|
|
|
var aggregatorServer *aggregatorapiserver.APIAggregator
|
2024-02-12 15:59:35 -05:00
|
|
|
if s.features.IsEnabledGlobally(featuremgmt.FlagKubernetesAggregator) {
|
2024-08-13 15:28:17 -04:00
|
|
|
aggregatorServer, err = s.createKubeAggregator(serverConfig, server, s.metrics)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
delegate = aggregatorServer.GenericAPIServer
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var runningServer *genericapiserver.GenericAPIServer
|
|
|
|
|
if s.features.IsEnabledGlobally(featuremgmt.FlagDataplaneAggregator) {
|
|
|
|
|
runningServer, err = s.startDataplaneAggregator(ctx, transport, serverConfig, delegate)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
} else if s.features.IsEnabledGlobally(featuremgmt.FlagKubernetesAggregator) {
|
|
|
|
|
runningServer, err = s.startKubeAggregator(ctx, transport, aggregatorServer)
|
2024-02-12 15:59:35 -05:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2024-08-08 11:39:44 -04:00
|
|
|
runningServer, err = s.startCoreServer(ctx, transport, server)
|
2024-02-12 15:59:35 -05:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2024-02-01 17:27:30 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// only write kubeconfig in dev mode
|
|
|
|
|
if o.ExtraOptions.DevMode {
|
2024-02-12 15:59:35 -05:00
|
|
|
if err := ensureKubeConfig(runningServer.LoopbackClientConfig, o.StorageOptions.DataPath); err != nil {
|
2024-02-01 17:27:30 -05:00
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-12 15:59:35 -05:00
|
|
|
// used by the proxy wrapper registered in ProvideService
|
|
|
|
|
s.handler = runningServer.Handler
|
|
|
|
|
// used by local clients to make requests to the server
|
|
|
|
|
s.restConfig = runningServer.LoopbackClientConfig
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *service) startCoreServer(
|
2024-08-08 11:39:44 -04:00
|
|
|
ctx context.Context,
|
2024-02-12 15:59:35 -05:00
|
|
|
transport *roundTripperFunc,
|
|
|
|
|
server *genericapiserver.GenericAPIServer,
|
|
|
|
|
) (*genericapiserver.GenericAPIServer, error) {
|
|
|
|
|
// setup the loopback transport and signal that it's ready.
|
|
|
|
|
// ignore the lint error because the response is passed directly to the client,
|
|
|
|
|
// so the client will be responsible for closing the response body.
|
|
|
|
|
// nolint:bodyclose
|
|
|
|
|
transport.fn = grafanaresponsewriter.WrapHandler(server.Handler)
|
|
|
|
|
close(transport.ready)
|
2023-10-23 14:42:10 -04:00
|
|
|
|
2023-12-06 17:06:18 -05:00
|
|
|
prepared := server.PrepareRun()
|
2024-02-12 15:59:35 -05:00
|
|
|
go func() {
|
2024-08-08 11:39:44 -04:00
|
|
|
s.stoppedCh <- prepared.RunWithContext(ctx)
|
2024-02-12 15:59:35 -05:00
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
return server, nil
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-13 15:28:17 -04:00
|
|
|
func (s *service) startDataplaneAggregator(
|
2024-08-08 11:39:44 -04:00
|
|
|
ctx context.Context,
|
2024-02-12 15:59:35 -05:00
|
|
|
transport *roundTripperFunc,
|
|
|
|
|
serverConfig *genericapiserver.RecommendedConfig,
|
2024-08-13 15:28:17 -04:00
|
|
|
delegate *genericapiserver.GenericAPIServer,
|
2024-02-12 15:59:35 -05:00
|
|
|
) (*genericapiserver.GenericAPIServer, error) {
|
2024-08-13 15:28:17 -04:00
|
|
|
config := &dataplaneaggregator.Config{
|
|
|
|
|
GenericConfig: serverConfig,
|
|
|
|
|
ExtraConfig: dataplaneaggregator.ExtraConfig{
|
|
|
|
|
PluginClient: s.pluginClient,
|
|
|
|
|
PluginContextProvider: &pluginContextProvider{
|
|
|
|
|
pluginStore: s.pluginStore,
|
|
|
|
|
datasources: s.datasources,
|
|
|
|
|
contextProvider: s.contextProvider,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err := s.options.GrafanaAggregatorOptions.ApplyTo(config, s.options.RecommendedOptions.Etcd); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
completedConfig := config.Complete()
|
|
|
|
|
|
|
|
|
|
aggregatorServer, err := completedConfig.NewWithDelegate(delegate)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// setup the loopback transport for the aggregator server and signal that it's ready
|
|
|
|
|
// ignore the lint error because the response is passed directly to the client,
|
|
|
|
|
// so the client will be responsible for closing the response body.
|
|
|
|
|
// nolint:bodyclose
|
|
|
|
|
transport.fn = grafanaresponsewriter.WrapHandler(aggregatorServer.GenericAPIServer.Handler)
|
|
|
|
|
close(transport.ready)
|
2024-03-05 10:34:47 -08:00
|
|
|
|
2024-08-13 15:28:17 -04:00
|
|
|
prepared, err := aggregatorServer.PrepareRun()
|
2024-02-12 15:59:35 -05:00
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-13 15:28:17 -04:00
|
|
|
go func() {
|
|
|
|
|
s.stoppedCh <- prepared.RunWithContext(ctx)
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
return aggregatorServer.GenericAPIServer, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *service) createKubeAggregator(
|
|
|
|
|
serverConfig *genericapiserver.RecommendedConfig,
|
|
|
|
|
server *genericapiserver.GenericAPIServer,
|
|
|
|
|
reg prometheus.Registerer,
|
|
|
|
|
) (*aggregatorapiserver.APIAggregator, error) {
|
|
|
|
|
namespaceMapper := request.GetNamespaceMapper(s.cfg)
|
|
|
|
|
|
|
|
|
|
aggregatorConfig, err := kubeaggregator.CreateAggregatorConfig(s.options, *serverConfig, namespaceMapper(1))
|
2024-02-12 15:59:35 -05:00
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-13 15:28:17 -04:00
|
|
|
return kubeaggregator.CreateAggregatorServer(aggregatorConfig, server, reg)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *service) startKubeAggregator(
|
|
|
|
|
ctx context.Context,
|
|
|
|
|
transport *roundTripperFunc,
|
|
|
|
|
aggregatorServer *aggregatorapiserver.APIAggregator,
|
|
|
|
|
) (*genericapiserver.GenericAPIServer, error) {
|
2024-02-12 15:59:35 -05:00
|
|
|
// setup the loopback transport for the aggregator server and signal that it's ready
|
|
|
|
|
// ignore the lint error because the response is passed directly to the client,
|
|
|
|
|
// so the client will be responsible for closing the response body.
|
|
|
|
|
// nolint:bodyclose
|
|
|
|
|
transport.fn = grafanaresponsewriter.WrapHandler(aggregatorServer.GenericAPIServer.Handler)
|
|
|
|
|
close(transport.ready)
|
|
|
|
|
|
|
|
|
|
prepared, err := aggregatorServer.PrepareRun()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2023-12-06 17:06:18 -05:00
|
|
|
|
2023-07-14 15:22:10 -04:00
|
|
|
go func() {
|
2024-08-08 11:39:44 -04:00
|
|
|
s.stoppedCh <- prepared.Run(ctx)
|
2023-07-14 15:22:10 -04:00
|
|
|
}()
|
2024-02-12 15:59:35 -05:00
|
|
|
|
|
|
|
|
return aggregatorServer.GenericAPIServer, nil
|
2023-07-14 15:22:10 -04:00
|
|
|
}
|
|
|
|
|
|
2023-10-31 10:26:39 -07:00
|
|
|
func (s *service) GetDirectRestConfig(c *contextmodel.ReqContext) *clientrest.Config {
|
|
|
|
|
return &clientrest.Config{
|
|
|
|
|
Transport: &roundTripperFunc{
|
|
|
|
|
fn: func(req *http.Request) (*http.Response, error) {
|
2024-06-05 14:23:32 -03:00
|
|
|
<-s.startedCh
|
2024-07-26 16:39:23 +03:00
|
|
|
ctx := identity.WithRequester(req.Context(), c.SignedInUser)
|
2024-02-12 15:59:35 -05:00
|
|
|
wrapped := grafanaresponsewriter.WrapHandler(s.handler)
|
|
|
|
|
return wrapped(req.WithContext(ctx))
|
2023-10-31 10:26:39 -07:00
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-31 10:36:51 -08:00
|
|
|
func (s *service) DirectlyServeHTTP(w http.ResponseWriter, r *http.Request) {
|
2024-06-05 14:23:32 -03:00
|
|
|
<-s.startedCh
|
2024-01-31 10:36:51 -08:00
|
|
|
s.handler.ServeHTTP(w, r)
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-14 15:22:10 -04:00
|
|
|
func (s *service) running(ctx context.Context) error {
|
|
|
|
|
select {
|
|
|
|
|
case err := <-s.stoppedCh:
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
case <-ctx.Done():
|
2024-08-08 11:39:44 -04:00
|
|
|
return ctx.Err()
|
2023-07-14 15:22:10 -04:00
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-01 17:27:30 -05:00
|
|
|
func ensureKubeConfig(restConfig *clientrest.Config, dir string) error {
|
2023-12-05 14:31:49 -08:00
|
|
|
return clientcmd.WriteToFile(
|
2024-02-01 17:27:30 -05:00
|
|
|
utils.FormatKubeConfig(restConfig),
|
|
|
|
|
path.Join(dir, "grafana.kubeconfig"),
|
2023-12-05 14:31:49 -08:00
|
|
|
)
|
2023-07-14 15:22:10 -04:00
|
|
|
}
|
2023-10-31 10:26:39 -07:00
|
|
|
|
|
|
|
|
type roundTripperFunc struct {
|
2024-02-01 17:27:30 -05:00
|
|
|
ready chan struct{}
|
|
|
|
|
fn func(req *http.Request) (*http.Response, error)
|
2023-10-31 10:26:39 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (f *roundTripperFunc) RoundTrip(req *http.Request) (*http.Response, error) {
|
2024-02-01 17:27:30 -05:00
|
|
|
if f.fn == nil {
|
|
|
|
|
<-f.ready
|
|
|
|
|
}
|
2023-10-31 10:26:39 -07:00
|
|
|
return f.fn(req)
|
|
|
|
|
}
|
2024-08-13 15:28:17 -04:00
|
|
|
|
|
|
|
|
type pluginContextProvider struct {
|
|
|
|
|
pluginStore pluginstore.Store
|
|
|
|
|
datasources datasource.ScopedPluginDatasourceProvider
|
|
|
|
|
contextProvider datasource.PluginContextWrapper
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (p *pluginContextProvider) GetPluginContext(ctx context.Context, pluginID string, uid string) (backend.PluginContext, error) {
|
|
|
|
|
all := p.pluginStore.Plugins(ctx)
|
|
|
|
|
|
|
|
|
|
var datasourceProvider datasource.PluginDatasourceProvider
|
|
|
|
|
for _, plugin := range all {
|
|
|
|
|
if plugin.ID == pluginID {
|
|
|
|
|
datasourceProvider = p.datasources.GetDatasourceProvider(plugin.JSONData)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if datasourceProvider == nil {
|
|
|
|
|
return backend.PluginContext{}, fmt.Errorf("plugin not found")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
s, err := datasourceProvider.GetInstanceSettings(ctx, uid)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return backend.PluginContext{}, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return p.contextProvider.PluginContextForDataSource(ctx, s)
|
|
|
|
|
}
|