mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
folder: allow standalone folder apiserver (#99508)
* add standalone folders * add validate * fix auth
This commit is contained in:
parent
a5916116b1
commit
7720f0b64e
@ -53,6 +53,7 @@ type FolderAPIBuilder struct {
|
||||
accessControl accesscontrol.AccessControl
|
||||
searcher resource.ResourceIndexClient
|
||||
cfg *setting.Cfg
|
||||
ignoreLegacy bool // skip legacy storage and only use unified storage
|
||||
}
|
||||
|
||||
func RegisterAPIService(cfg *setting.Cfg,
|
||||
@ -85,6 +86,14 @@ func RegisterAPIService(cfg *setting.Cfg,
|
||||
return builder
|
||||
}
|
||||
|
||||
func NewAPIService() *FolderAPIBuilder {
|
||||
return &FolderAPIBuilder{
|
||||
gv: resourceInfo.GroupVersion(),
|
||||
namespacer: request.GetNamespaceMapper(nil),
|
||||
ignoreLegacy: true,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *FolderAPIBuilder) GetGroupVersion() schema.GroupVersion {
|
||||
return b.gv
|
||||
}
|
||||
@ -122,6 +131,18 @@ func (b *FolderAPIBuilder) UpdateAPIGroupInfo(apiGroupInfo *genericapiserver.API
|
||||
scheme := opts.Scheme
|
||||
optsGetter := opts.OptsGetter
|
||||
dualWriteBuilder := opts.DualWriteBuilder
|
||||
storage := map[string]rest.Storage{}
|
||||
|
||||
if b.ignoreLegacy {
|
||||
store, err := grafanaregistry.NewRegistryStore(opts.Scheme, resourceInfo, opts.OptsGetter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
storage[resourceInfo.StoragePath()] = store
|
||||
apiGroupInfo.VersionedResourcesStorageMap[v0alpha1.VERSION] = storage
|
||||
b.storage = storage[resourceInfo.StoragePath()].(grafanarest.Storage)
|
||||
return nil
|
||||
}
|
||||
|
||||
legacyStore := &legacyStorage{
|
||||
service: b.folderSvc,
|
||||
@ -135,7 +156,6 @@ func (b *FolderAPIBuilder) UpdateAPIGroupInfo(apiGroupInfo *genericapiserver.API
|
||||
opts.StorageOptions(resourceInfo.GroupResource(), apistore.StorageOptions{
|
||||
RequireDeprecatedInternalID: true})
|
||||
|
||||
storage := map[string]rest.Storage{}
|
||||
storage[resourceInfo.StoragePath()] = legacyStore
|
||||
if optsGetter != nil && dualWriteBuilder != nil {
|
||||
store, err := grafanaregistry.NewRegistryStore(scheme, resourceInfo, optsGetter)
|
||||
|
@ -5,11 +5,18 @@ import (
|
||||
"net"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
genericapiserver "k8s.io/apiserver/pkg/server"
|
||||
"k8s.io/apiserver/pkg/server/options"
|
||||
|
||||
"github.com/grafana/authlib/authn"
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/apistore"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
)
|
||||
|
||||
type StorageType string
|
||||
@ -28,8 +35,12 @@ type StorageOptions struct {
|
||||
// The desired storage type
|
||||
StorageType StorageType
|
||||
|
||||
// For unified-grpc, the address is required
|
||||
Address string
|
||||
// For unified-grpc
|
||||
Address string
|
||||
GrpcClientAuthenticationToken string
|
||||
GrpcClientAuthenticationTokenExchangeURL string
|
||||
GrpcClientAuthenticationTokenNamespace string
|
||||
GrpcClientAuthenticationAllowInsecure bool
|
||||
|
||||
// For file storage, this is the requested path
|
||||
DataPath string
|
||||
@ -47,8 +58,10 @@ type StorageOptions struct {
|
||||
|
||||
func NewStorageOptions() *StorageOptions {
|
||||
return &StorageOptions{
|
||||
StorageType: StorageTypeUnified,
|
||||
Address: "localhost:10000",
|
||||
StorageType: StorageTypeUnified,
|
||||
Address: "localhost:10000",
|
||||
GrpcClientAuthenticationTokenNamespace: "*",
|
||||
GrpcClientAuthenticationAllowInsecure: false,
|
||||
}
|
||||
}
|
||||
|
||||
@ -56,6 +69,10 @@ func (o *StorageOptions) AddFlags(fs *pflag.FlagSet) {
|
||||
fs.StringVar((*string)(&o.StorageType), "grafana-apiserver-storage-type", string(o.StorageType), "Storage type")
|
||||
fs.StringVar(&o.DataPath, "grafana-apiserver-storage-path", o.DataPath, "Storage path for file storage")
|
||||
fs.StringVar(&o.Address, "grafana-apiserver-storage-address", o.Address, "Remote grpc address endpoint")
|
||||
fs.StringVar(&o.GrpcClientAuthenticationToken, "grpc-client-authentication-token", o.GrpcClientAuthenticationToken, "Token for grpc client authentication")
|
||||
fs.StringVar(&o.GrpcClientAuthenticationTokenExchangeURL, "grpc-client-authentication-token-exchange-url", o.GrpcClientAuthenticationTokenExchangeURL, "Token exchange url for grpc client authentication")
|
||||
fs.StringVar(&o.GrpcClientAuthenticationTokenNamespace, "grpc-client-authentication-token-namespace", o.GrpcClientAuthenticationTokenNamespace, "Token namespace for grpc client authentication")
|
||||
fs.BoolVar(&o.GrpcClientAuthenticationAllowInsecure, "grpc-client-authentication-allow-insecure", o.GrpcClientAuthenticationAllowInsecure, "Allow insecure grpc client authentication")
|
||||
}
|
||||
|
||||
func (o *StorageOptions) Validate() []error {
|
||||
@ -79,11 +96,49 @@ func (o *StorageOptions) Validate() []error {
|
||||
if o.BlobStoreURL != "" && o.StorageType != StorageTypeUnified {
|
||||
errs = append(errs, fmt.Errorf("blob storage is only valid with unified storage"))
|
||||
}
|
||||
|
||||
// Validate grpc client with auth
|
||||
if o.StorageType == StorageTypeUnifiedGrpc && o.GrpcClientAuthenticationToken != "" {
|
||||
if o.GrpcClientAuthenticationToken == "" {
|
||||
errs = append(errs, fmt.Errorf("grpc client auth token is required for unified-grpc storage"))
|
||||
}
|
||||
if o.GrpcClientAuthenticationTokenExchangeURL == "" {
|
||||
errs = append(errs, fmt.Errorf("grpc client auth token exchange url is required for unified-grpc storage"))
|
||||
}
|
||||
if o.GrpcClientAuthenticationTokenNamespace == "" {
|
||||
errs = append(errs, fmt.Errorf("grpc client auth namespace is required for unified-grpc storage"))
|
||||
}
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
func (o *StorageOptions) ApplyTo(serverConfig *genericapiserver.RecommendedConfig, etcdOptions *options.EtcdOptions) error {
|
||||
// TODO: move storage setup here
|
||||
func (o *StorageOptions) ApplyTo(serverConfig *genericapiserver.RecommendedConfig, etcdOptions *options.EtcdOptions, tracer tracing.Tracer) error {
|
||||
if o.StorageType != StorageTypeUnifiedGrpc {
|
||||
return nil
|
||||
}
|
||||
conn, err := grpc.NewClient(o.Address,
|
||||
grpc.WithStatsHandler(otelgrpc.NewClientHandler()),
|
||||
grpc.WithTransportCredentials(insecure.NewCredentials()),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
authCfg := authn.GrpcClientConfig{
|
||||
TokenClientConfig: &authn.TokenExchangeConfig{
|
||||
Token: o.GrpcClientAuthenticationToken,
|
||||
TokenExchangeURL: o.GrpcClientAuthenticationTokenExchangeURL,
|
||||
},
|
||||
TokenRequest: &authn.TokenExchangeRequest{
|
||||
Audiences: []string{"resourceStore"},
|
||||
Namespace: o.GrpcClientAuthenticationTokenNamespace,
|
||||
},
|
||||
}
|
||||
unified, err := resource.NewCloudResourceClient(tracer, conn, authCfg, o.GrpcClientAuthenticationAllowInsecure)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
getter := apistore.NewRESTOptionsGetterForClient(unified, etcdOptions.StorageConfig)
|
||||
serverConfig.RESTOptionsGetter = getter
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -63,3 +63,40 @@ func TestStorageOptions_CheckFeatureToggle(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestStorageOptions_Validate(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
Opts StorageOptions
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "with unified storage grpc and no auth token",
|
||||
Opts: StorageOptions{
|
||||
StorageType: StorageTypeUnifiedGrpc,
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "with unified storage grpc and auth info",
|
||||
Opts: StorageOptions{
|
||||
StorageType: StorageTypeUnifiedGrpc,
|
||||
Address: "localhost:10000",
|
||||
GrpcClientAuthenticationToken: "1234",
|
||||
GrpcClientAuthenticationTokenExchangeURL: "http://localhost:8080",
|
||||
GrpcClientAuthenticationTokenNamespace: "*",
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
errs := tt.Opts.Validate()
|
||||
if tt.wantErr {
|
||||
assert.NotEmpty(t, errs)
|
||||
return
|
||||
}
|
||||
assert.Empty(t, errs)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user