folder: allow standalone folder apiserver (#99508)

* add standalone folders

* add validate

* fix auth
This commit is contained in:
Georges Chaudy
2025-01-27 15:50:06 +01:00
committed by GitHub
parent a5916116b1
commit 7720f0b64e
3 changed files with 119 additions and 7 deletions
+61 -6
View File
@@ -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)
})
}
}