mirror of https://github.com/grafana/grafana.git
202 lines
6.2 KiB
Go
202 lines
6.2 KiB
Go
package apiserver
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"net"
|
|
"path"
|
|
|
|
"github.com/grafana/pyroscope-go/godeltaprof/http/pprof"
|
|
"github.com/spf13/pflag"
|
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
|
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
|
genericapiserver "k8s.io/apiserver/pkg/server"
|
|
"k8s.io/apiserver/pkg/server/mux"
|
|
"k8s.io/client-go/tools/clientcmd"
|
|
netutils "k8s.io/utils/net"
|
|
|
|
"github.com/grafana/grafana/pkg/infra/log"
|
|
"github.com/grafana/grafana/pkg/infra/tracing"
|
|
grafanaAPIServer "github.com/grafana/grafana/pkg/services/apiserver"
|
|
"github.com/grafana/grafana/pkg/services/apiserver/builder"
|
|
"github.com/grafana/grafana/pkg/services/apiserver/standalone"
|
|
standaloneoptions "github.com/grafana/grafana/pkg/services/apiserver/standalone/options"
|
|
"github.com/grafana/grafana/pkg/services/apiserver/utils"
|
|
"github.com/grafana/grafana/pkg/setting"
|
|
)
|
|
|
|
const (
|
|
dataPath = "data/grafana-apiserver" // same as grafana core
|
|
)
|
|
|
|
// APIServerOptions contains the state for the apiserver
|
|
type APIServerOptions struct {
|
|
factory standalone.APIServerFactory
|
|
builders []builder.APIGroupBuilder
|
|
Options *standaloneoptions.Options
|
|
AlternateDNS []string
|
|
logger log.Logger
|
|
|
|
StdOut io.Writer
|
|
StdErr io.Writer
|
|
}
|
|
|
|
func newAPIServerOptions(out, errOut io.Writer) *APIServerOptions {
|
|
logger := log.New("grafana-apiserver")
|
|
|
|
return &APIServerOptions{
|
|
logger: logger,
|
|
StdOut: out,
|
|
StdErr: errOut,
|
|
Options: standaloneoptions.New(logger, grafanaAPIServer.Codecs.LegacyCodec()),
|
|
}
|
|
}
|
|
|
|
func (o *APIServerOptions) loadAPIGroupBuilders(ctx context.Context, tracer tracing.Tracer, apis []schema.GroupVersion) error {
|
|
o.builders = []builder.APIGroupBuilder{}
|
|
for _, gv := range apis {
|
|
api, err := o.factory.MakeAPIServer(ctx, tracer, gv)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
o.builders = append(o.builders, api)
|
|
}
|
|
|
|
if len(o.builders) < 1 {
|
|
return fmt.Errorf("no apis matched ")
|
|
}
|
|
|
|
// Install schemas
|
|
for _, b := range o.builders {
|
|
if err := b.InstallSchema(grafanaAPIServer.Scheme); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (o *APIServerOptions) Config(tracer tracing.Tracer) (*genericapiserver.RecommendedConfig, error) {
|
|
if err := o.Options.RecommendedOptions.SecureServing.MaybeDefaultWithSelfSignedCerts(
|
|
"localhost", o.AlternateDNS, []net.IP{netutils.ParseIPSloppy("127.0.0.1")},
|
|
); err != nil {
|
|
return nil, fmt.Errorf("error creating self-signed certificates: %v", err)
|
|
}
|
|
|
|
o.Options.RecommendedOptions.Authentication.RemoteKubeConfigFileOptional = true
|
|
|
|
// TODO: determine authorization, currently insecure because Authorization provided by recommended options doesn't work
|
|
// reason: an aggregated server won't be able to post subjectaccessreviews (Grafana doesn't have this kind)
|
|
// exact error: the server could not find the requested resource (post subjectaccessreviews.authorization.k8s.io)
|
|
o.Options.RecommendedOptions.Authorization = nil
|
|
|
|
o.Options.RecommendedOptions.Admission = nil
|
|
o.Options.RecommendedOptions.Etcd = nil
|
|
|
|
if o.Options.RecommendedOptions.CoreAPI.CoreAPIKubeconfigPath == "" {
|
|
o.Options.RecommendedOptions.CoreAPI = nil
|
|
}
|
|
|
|
serverConfig := genericapiserver.NewRecommendedConfig(grafanaAPIServer.Codecs)
|
|
|
|
if err := o.Options.ApplyTo(serverConfig); err != nil {
|
|
return nil, fmt.Errorf("failed to apply options to server config: %w", err)
|
|
}
|
|
|
|
if factoryOptions := o.factory.GetOptions(); factoryOptions != nil {
|
|
err := factoryOptions.ApplyTo(serverConfig)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("factory's applyTo func failed: %s", err.Error())
|
|
}
|
|
}
|
|
|
|
serverConfig.DisabledPostStartHooks = serverConfig.DisabledPostStartHooks.Insert("generic-apiserver-start-informers")
|
|
serverConfig.DisabledPostStartHooks = serverConfig.DisabledPostStartHooks.Insert("priority-and-fairness-config-consumer")
|
|
|
|
// Add OpenAPI specs for each group+version
|
|
err := builder.SetupConfig(
|
|
grafanaAPIServer.Scheme,
|
|
serverConfig,
|
|
o.builders,
|
|
setting.BuildStamp,
|
|
setting.BuildVersion,
|
|
setting.BuildCommit,
|
|
setting.BuildBranch,
|
|
o.factory.GetOptionalMiddlewares(tracer)...,
|
|
)
|
|
return serverConfig, err
|
|
}
|
|
|
|
func (o *APIServerOptions) AddFlags(fs *pflag.FlagSet) {
|
|
o.Options.AddFlags(fs)
|
|
|
|
if factoryOptions := o.factory.GetOptions(); factoryOptions != nil {
|
|
factoryOptions.AddFlags(fs)
|
|
}
|
|
}
|
|
|
|
// Validate validates APIServerOptions
|
|
func (o *APIServerOptions) Validate() error {
|
|
errors := make([]error, 0)
|
|
|
|
if factoryOptions := o.factory.GetOptions(); factoryOptions != nil {
|
|
errors = append(errors, factoryOptions.ValidateOptions()...)
|
|
}
|
|
|
|
if errs := o.Options.Validate(); len(errs) > 0 {
|
|
errors = append(errors, errors...)
|
|
}
|
|
|
|
return utilerrors.NewAggregate(errors)
|
|
}
|
|
|
|
// Complete fills in fields required to have valid data
|
|
func (o *APIServerOptions) Complete() error {
|
|
return nil
|
|
}
|
|
|
|
func (o *APIServerOptions) RunAPIServer(ctx context.Context, config *genericapiserver.RecommendedConfig) error {
|
|
delegationTarget := genericapiserver.NewEmptyDelegate()
|
|
completedConfig := config.Complete()
|
|
|
|
server, err := completedConfig.New("standalone-apiserver", delegationTarget)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Install the API Group+version
|
|
// #TODO figure out how to configure storage type in o.Options.StorageOptions
|
|
err = builder.InstallAPIs(grafanaAPIServer.Scheme, grafanaAPIServer.Codecs, server, config.RESTOptionsGetter, o.builders, o.Options.StorageOptions,
|
|
o.Options.MetricsOptions.MetricsRegisterer, nil, nil, // no need for server lock in standalone
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// write the local config to disk
|
|
if o.Options.ExtraOptions.DevMode {
|
|
if err = clientcmd.WriteToFile(
|
|
utils.FormatKubeConfig(server.LoopbackClientConfig),
|
|
path.Join(dataPath, "apiserver.kubeconfig"),
|
|
); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if config.EnableProfiling {
|
|
deltaProfiling{}.Install(server.Handler.NonGoRestfulMux)
|
|
}
|
|
|
|
return server.PrepareRun().RunWithContext(ctx)
|
|
}
|
|
|
|
// deltaProfiling adds godeltapprof handlers for pprof under /debug/pprof.
|
|
type deltaProfiling struct{}
|
|
|
|
// Install register godeltapprof handlers to the given mux.
|
|
func (d deltaProfiling) Install(c *mux.PathRecorderMux) {
|
|
c.UnlistedHandleFunc("/debug/pprof/delta_heap", pprof.Heap)
|
|
c.UnlistedHandleFunc("/debug/pprof/delta_block", pprof.Block)
|
|
c.UnlistedHandleFunc("/debug/pprof/delta_mutex", pprof.Mutex)
|
|
}
|