K8s: Implement playlist api with k8s client (#77405)

This commit is contained in:
Ryan McKinley
2023-10-31 10:26:39 -07:00
committed by GitHub
parent 254648b96b
commit dd773e74f1
15 changed files with 294 additions and 53 deletions
+7
View File
@@ -850,6 +850,13 @@ var (
Stage: FeatureStageExperimental,
Owner: grafanaAppPlatformSquad,
},
{
Name: "kubernetesPlaylistsAPI",
Description: "Route /api/playlist API to k8s handlers",
Stage: FeatureStageExperimental,
Owner: grafanaAppPlatformSquad,
RequiresRestart: true, // changes the API routing
},
{
Name: "cloudWatchBatchQueries",
Description: "Runs CloudWatch metrics queries as separate batches",
+1
View File
@@ -120,6 +120,7 @@ enableNativeHTTPHistogram,experimental,@grafana/hosted-grafana-team,false,false,
formatString,experimental,@grafana/grafana-bi-squad,false,false,false,true
transformationsVariableSupport,experimental,@grafana/grafana-bi-squad,false,false,false,true
kubernetesPlaylists,experimental,@grafana/grafana-app-platform-squad,false,false,false,true
kubernetesPlaylistsAPI,experimental,@grafana/grafana-app-platform-squad,false,false,true,false
cloudWatchBatchQueries,preview,@grafana/aws-datasources,false,false,false,false
navAdminSubsections,experimental,@grafana/grafana-frontend-platform,false,false,false,false
recoveryThreshold,experimental,@grafana/alerting-squad,false,false,true,false
1 Name Stage Owner requiresDevMode RequiresLicense RequiresRestart FrontendOnly
120 formatString experimental @grafana/grafana-bi-squad false false false true
121 transformationsVariableSupport experimental @grafana/grafana-bi-squad false false false true
122 kubernetesPlaylists experimental @grafana/grafana-app-platform-squad false false false true
123 kubernetesPlaylistsAPI experimental @grafana/grafana-app-platform-squad false false true false
124 cloudWatchBatchQueries preview @grafana/aws-datasources false false false false
125 navAdminSubsections experimental @grafana/grafana-frontend-platform false false false false
126 recoveryThreshold experimental @grafana/alerting-squad false false true false
+4
View File
@@ -491,6 +491,10 @@ const (
// Use the kubernetes API in the frontend for playlists
FlagKubernetesPlaylists = "kubernetesPlaylists"
// FlagKubernetesPlaylistsAPI
// Route /api/playlist API to k8s handlers
FlagKubernetesPlaylistsAPI = "kubernetesPlaylistsAPI"
// FlagCloudWatchBatchQueries
// Runs CloudWatch metrics queries as separate batches
FlagCloudWatchBatchQueries = "cloudWatchBatchQueries"
+49 -26
View File
@@ -4,6 +4,7 @@ import (
"context"
"fmt"
"net/http"
"net/http/httptest"
"path"
"strconv"
@@ -35,7 +36,6 @@ import (
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
filestorage "github.com/grafana/grafana/pkg/services/grafana-apiserver/storage/file"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/web"
)
type StorageType string
@@ -86,6 +86,13 @@ type RestConfigProvider interface {
GetRestConfig() *clientrest.Config
}
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
}
type service struct {
*services.BasicService
@@ -96,7 +103,7 @@ type service struct {
stoppedCh chan error
rr routing.RouteRegister
handler web.Handler
handler http.Handler
builders []APIGroupBuilder
tracing *tracing.TracingService
@@ -133,10 +140,24 @@ func ProvideService(
return
}
if handle, ok := s.handler.(func(c *contextmodel.ReqContext)); ok {
handle(c)
return
req := c.Req
if req.URL.Path == "" {
req.URL.Path = "/"
}
//TODO: add support for the existing MetricsEndpointBasicAuth config option
if req.URL.Path == "/apiserver-metrics" {
req.URL.Path = "/metrics"
}
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")
resp := responsewriter.WrapForHTTP1Or2(c.Resp)
s.handler.ServeHTTP(resp, req)
}
k8sRoute.Any("/", middleware.ReqSignedIn, handler)
k8sRoute.Any("/*", middleware.ReqSignedIn, handler)
@@ -301,27 +322,8 @@ func (s *service) start(ctx context.Context) error {
}
}
// TODO: this is a hack. see note in ProvideService
s.handler = func(c *contextmodel.ReqContext) {
req := c.Req
if req.URL.Path == "" {
req.URL.Path = "/"
}
//TODO: add support for the existing MetricsEndpointBasicAuth config option
if req.URL.Path == "/apiserver-metrics" {
req.URL.Path = "/metrics"
}
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")
resp := responsewriter.WrapForHTTP1Or2(c.Resp)
server.Handler.ServeHTTP(resp, req)
}
// Used by the proxy wrapper registered in ProvideService
s.handler = server.Handler
// skip starting the server in prod mode
if !s.config.devMode {
@@ -335,6 +337,19 @@ func (s *service) start(ctx context.Context) error {
return nil
}
func (s *service) GetDirectRestConfig(c *contextmodel.ReqContext) *clientrest.Config {
return &clientrest.Config{
Transport: &roundTripperFunc{
fn: func(req *http.Request) (*http.Response, error) {
ctx := appcontext.WithUser(req.Context(), c.SignedInUser)
w := httptest.NewRecorder()
s.handler.ServeHTTP(w, req.WithContext(ctx))
return w.Result(), nil
},
},
}
}
func (s *service) running(ctx context.Context) error {
// skip waiting for the server in prod mode
if !s.config.devMode {
@@ -383,3 +398,11 @@ func (s *service) ensureKubeConfig() error {
return clientcmd.WriteToFile(clientConfig, path.Join(s.config.dataPath, "grafana.kubeconfig"))
}
type roundTripperFunc struct {
fn func(req *http.Request) (*http.Response, error)
}
func (f *roundTripperFunc) RoundTrip(req *http.Request) (*http.Response, error) {
return f.fn(req)
}
@@ -11,5 +11,6 @@ var WireSet = wire.NewSet(
wire.Bind(new(RestConfigProvider), new(*service)),
wire.Bind(new(Service), new(*service)),
wire.Bind(new(APIRegistrar), new(*service)),
wire.Bind(new(DirectRestConfigProvider), new(*service)),
authorizer.WireSet,
)
+3
View File
@@ -47,6 +47,9 @@ type PlaylistDTO struct {
// Returned for k8s
OrgID int64 `json:"-"`
// Returned for k8s and added as an annotation
Id int64 `json:"-"`
}
type PlaylistItemDTO struct {
@@ -68,6 +68,7 @@ func (s *Service) Get(ctx context.Context, q *playlist.GetPlaylistByUidQuery) (*
}
}
return &playlist.PlaylistDTO{
Id: v.Id,
Uid: v.UID,
Name: v.Name,
Interval: v.Interval,