Zanzana: Split up settings into client and server sections (#99066)
* Split up zanzana settings into client and server sections * Update workspace
This commit is contained in:
@@ -22,8 +22,6 @@ import (
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
authzextv1 "github.com/grafana/grafana/pkg/services/authz/proto/v1"
|
||||
"github.com/grafana/grafana/pkg/services/authz/zanzana"
|
||||
zclient "github.com/grafana/grafana/pkg/services/authz/zanzana/client"
|
||||
zserver "github.com/grafana/grafana/pkg/services/authz/zanzana/server"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/grpcserver"
|
||||
"github.com/grafana/grafana/pkg/services/grpcserver/interceptors"
|
||||
@@ -36,17 +34,17 @@ const zanzanaAudience = "zanzana"
|
||||
// It will also start an embedded ZanzanaSever if mode is set to "embedded".
|
||||
func ProvideZanzana(cfg *setting.Cfg, db db.DB, features featuremgmt.FeatureToggles) (zanzana.Client, error) {
|
||||
if !features.IsEnabledGlobally(featuremgmt.FlagZanzana) {
|
||||
return zclient.NewNoop(), nil
|
||||
return zanzana.NewNoopClient(), nil
|
||||
}
|
||||
|
||||
logger := log.New("zanzana")
|
||||
|
||||
var client zanzana.Client
|
||||
switch cfg.Zanzana.Mode {
|
||||
switch cfg.ZanzanaClient.Mode {
|
||||
case setting.ZanzanaModeClient:
|
||||
tokenClient, err := authnlib.NewTokenExchangeClient(authnlib.TokenExchangeConfig{
|
||||
Token: cfg.Zanzana.Token,
|
||||
TokenExchangeURL: cfg.Zanzana.TokenExchangeURL,
|
||||
Token: cfg.ZanzanaClient.Token,
|
||||
TokenExchangeURL: cfg.ZanzanaClient.TokenExchangeURL,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to initialize token exchange client: %w", err)
|
||||
@@ -69,12 +67,12 @@ func ProvideZanzana(cfg *setting.Cfg, db db.DB, features featuremgmt.FeatureTogg
|
||||
grpc.WithPerRPCCredentials(tokenAuthCred),
|
||||
}
|
||||
|
||||
conn, err := grpc.NewClient(cfg.Zanzana.Addr, dialOptions...)
|
||||
conn, err := grpc.NewClient(cfg.ZanzanaClient.Addr, dialOptions...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create zanzana client to remote server: %w", err)
|
||||
}
|
||||
|
||||
client, err = zclient.NewClient(context.Background(), conn, cfg)
|
||||
client, err = zanzana.NewClient(conn)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to initialize zanzana client: %w", err)
|
||||
}
|
||||
@@ -84,12 +82,12 @@ func ProvideZanzana(cfg *setting.Cfg, db db.DB, features featuremgmt.FeatureTogg
|
||||
return nil, fmt.Errorf("failed to start zanzana: %w", err)
|
||||
}
|
||||
|
||||
openfga, err := zserver.NewOpenFGA(&cfg.Zanzana, store, logger)
|
||||
openfga, err := zanzana.NewOpenFGAServer(cfg.ZanzanaServer, store, logger)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to start zanzana: %w", err)
|
||||
}
|
||||
|
||||
srv, err := zserver.NewAuthzServer(cfg, openfga)
|
||||
srv, err := zanzana.NewServer(cfg.ZanzanaServer, openfga, logger)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to start zanzana: %w", err)
|
||||
}
|
||||
@@ -109,13 +107,13 @@ func ProvideZanzana(cfg *setting.Cfg, db db.DB, features featuremgmt.FeatureTogg
|
||||
authzv1.RegisterAuthzServiceServer(channel, srv)
|
||||
authzextv1.RegisterAuthzExtentionServiceServer(channel, srv)
|
||||
|
||||
client, err = zclient.NewClient(context.Background(), channel, cfg)
|
||||
client, err = zanzana.NewClient(channel)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to initialize zanzana client: %w", err)
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported zanzana mode: %s", cfg.Zanzana.Mode)
|
||||
return nil, fmt.Errorf("unsupported zanzana mode: %s", cfg.ZanzanaClient.Mode)
|
||||
}
|
||||
|
||||
return client, nil
|
||||
@@ -156,12 +154,12 @@ func (z *Zanzana) start(ctx context.Context) error {
|
||||
return fmt.Errorf("failed to initilize zanana store: %w", err)
|
||||
}
|
||||
|
||||
openfga, err := zserver.NewOpenFGA(&z.cfg.Zanzana, store, z.logger)
|
||||
openfga, err := zanzana.NewOpenFGAServer(z.cfg.ZanzanaServer, store, z.logger)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to start zanzana: %w", err)
|
||||
}
|
||||
|
||||
srv, err := zserver.NewAuthzServer(z.cfg, openfga)
|
||||
srv, err := zanzana.NewServer(z.cfg.ZanzanaServer, openfga, z.logger)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to start zanzana: %w", err)
|
||||
}
|
||||
@@ -183,7 +181,7 @@ func (z *Zanzana) start(ctx context.Context) error {
|
||||
AllowedAudiences: []string{zanzanaAudience},
|
||||
},
|
||||
authnlib.NewKeyRetriever(authnlib.KeyRetrieverConfig{
|
||||
SigningKeysURL: z.cfg.Zanzana.SigningKeysURL,
|
||||
SigningKeysURL: z.cfg.ZanzanaServer.SigningKeysURL,
|
||||
}),
|
||||
),
|
||||
)
|
||||
@@ -218,14 +216,18 @@ func (z *Zanzana) start(ctx context.Context) error {
|
||||
}
|
||||
|
||||
func (z *Zanzana) running(ctx context.Context) error {
|
||||
if z.cfg.Env == setting.Dev && z.cfg.Zanzana.ListenHTTP {
|
||||
go func() {
|
||||
z.logger.Info("Starting OpenFGA HTTP server")
|
||||
err := zserver.StartOpenFGAHttpSever(z.cfg, z.handle, z.logger)
|
||||
if err != nil {
|
||||
z.logger.Error("failed to start OpenFGA HTTP server", "error", err)
|
||||
}
|
||||
}()
|
||||
if z.cfg.Env == setting.Dev && z.cfg.ZanzanaServer.OpenFGAHttpAddr != "" {
|
||||
srv, err := zanzana.NewOpenFGAHttpServer(z.cfg.ZanzanaServer, z.handle)
|
||||
if err != nil {
|
||||
z.logger.Error("failed to create OpenFGA HTTP server", "error", err)
|
||||
} else {
|
||||
go func() {
|
||||
z.logger.Info("Starting OpenFGA HTTP server")
|
||||
if err := srv.ListenAndServe(); err != nil {
|
||||
z.logger.Error("failed to start OpenFGA HTTP server", "error", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
// Run is blocking so we can just run it here
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
|
||||
"github.com/grafana/authlib/authz"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
authzextv1 "github.com/grafana/grafana/pkg/services/authz/proto/v1"
|
||||
"github.com/grafana/grafana/pkg/services/authz/zanzana/client"
|
||||
@@ -17,6 +18,10 @@ type Client interface {
|
||||
BatchCheck(ctx context.Context, req *authzextv1.BatchCheckRequest) (*authzextv1.BatchCheckResponse, error)
|
||||
}
|
||||
|
||||
func NewClient(cc grpc.ClientConnInterface) (*client.Client, error) {
|
||||
return client.New(cc)
|
||||
}
|
||||
|
||||
func NewNoopClient() *client.NoopClient {
|
||||
return client.NewNoop()
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/grafana/authlib/authz"
|
||||
authzv1 "github.com/grafana/authlib/authz/proto/v1"
|
||||
@@ -13,66 +12,28 @@ import (
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
authzextv1 "github.com/grafana/grafana/pkg/services/authz/proto/v1"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
var _ authz.AccessClient = (*Client)(nil)
|
||||
|
||||
var tracer = otel.Tracer("github.com/grafana/grafana/pkg/services/authz/zanzana/client")
|
||||
|
||||
type ClientOption func(c *Client)
|
||||
|
||||
func WithTenantID(tenantID string) ClientOption {
|
||||
return func(c *Client) {
|
||||
c.tenantID = tenantID
|
||||
}
|
||||
}
|
||||
|
||||
func WithLogger(logger log.Logger) ClientOption {
|
||||
return func(c *Client) {
|
||||
c.logger = logger
|
||||
}
|
||||
}
|
||||
|
||||
type Client struct {
|
||||
logger log.Logger
|
||||
authz authzv1.AuthzServiceClient
|
||||
authzext authzextv1.AuthzExtentionServiceClient
|
||||
tenantID string
|
||||
}
|
||||
|
||||
func NewClient(ctx context.Context, cc grpc.ClientConnInterface, cfg *setting.Cfg) (*Client, error) {
|
||||
stackID := cfg.StackID
|
||||
if stackID == "" {
|
||||
stackID = "default"
|
||||
}
|
||||
|
||||
return New(
|
||||
ctx,
|
||||
cc,
|
||||
WithTenantID(fmt.Sprintf("stacks-%s", stackID)),
|
||||
WithLogger(log.New("zanzana-client")),
|
||||
)
|
||||
}
|
||||
|
||||
func New(ctx context.Context, cc grpc.ClientConnInterface, opts ...ClientOption) (*Client, error) {
|
||||
func New(cc grpc.ClientConnInterface) (*Client, error) {
|
||||
c := &Client{
|
||||
authz: authzv1.NewAuthzServiceClient(cc),
|
||||
authzext: authzextv1.NewAuthzExtentionServiceClient(cc),
|
||||
}
|
||||
|
||||
for _, o := range opts {
|
||||
o(c)
|
||||
}
|
||||
|
||||
if c.logger == nil {
|
||||
c.logger = log.NewNopLogger()
|
||||
logger: log.New("zanzana-client"),
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// Check implements authz.AccessClient.
|
||||
func (c *Client) Check(ctx context.Context, id claims.AuthInfo, req authz.CheckRequest) (authz.CheckResponse, error) {
|
||||
ctx, span := tracer.Start(ctx, "authz.zanzana.client.Check")
|
||||
defer span.End()
|
||||
|
||||
@@ -1 +1,26 @@
|
||||
package zanzana
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
openfgav1 "github.com/openfga/api/proto/openfga/v1"
|
||||
openfgaserver "github.com/openfga/openfga/pkg/server"
|
||||
openfgastorage "github.com/openfga/openfga/pkg/storage"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/services/authz/zanzana/server"
|
||||
"github.com/grafana/grafana/pkg/services/grpcserver"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
func NewServer(cfg setting.ZanzanaServerSettings, openfga openfgav1.OpenFGAServiceServer, logger log.Logger) (*server.Server, error) {
|
||||
return server.NewServer(cfg, openfga, logger)
|
||||
}
|
||||
|
||||
func NewOpenFGAServer(cfg setting.ZanzanaServerSettings, store openfgastorage.OpenFGADatastore, logger log.Logger) (*openfgaserver.Server, error) {
|
||||
return server.NewOpenFGAServer(cfg, store, logger)
|
||||
}
|
||||
|
||||
func NewOpenFGAHttpServer(cfg setting.ZanzanaServerSettings, srv grpcserver.Provider) (*http.Server, error) {
|
||||
return server.NewOpenFGAHttpServer(cfg, srv)
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ import (
|
||||
|
||||
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
|
||||
"github.com/rs/cors"
|
||||
"go.uber.org/zap/zapcore"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
healthv1pb "google.golang.org/grpc/health/grpc_health_v1"
|
||||
@@ -27,7 +26,7 @@ import (
|
||||
zlogger "github.com/grafana/grafana/pkg/services/authz/zanzana/logger"
|
||||
)
|
||||
|
||||
func NewOpenFGA(cfg *setting.ZanzanaSettings, store storage.OpenFGADatastore, logger log.Logger) (*server.Server, error) {
|
||||
func NewOpenFGAServer(cfg setting.ZanzanaServerSettings, store storage.OpenFGADatastore, logger log.Logger) (*server.Server, error) {
|
||||
opts := []server.OpenFGAServiceV1Option{
|
||||
server.WithDatastore(store),
|
||||
server.WithLogger(zlogger.New(logger)),
|
||||
@@ -48,8 +47,7 @@ func NewOpenFGA(cfg *setting.ZanzanaSettings, store storage.OpenFGADatastore, lo
|
||||
return srv, nil
|
||||
}
|
||||
|
||||
// StartOpenFGAHttpSever starts HTTP server which allows to use fga cli.
|
||||
func StartOpenFGAHttpSever(cfg *setting.Cfg, srv grpcserver.Provider, logger log.Logger) error {
|
||||
func NewOpenFGAHttpServer(cfg setting.ZanzanaServerSettings, srv grpcserver.Provider) (*http.Server, error) {
|
||||
dialOpts := []grpc.DialOption{
|
||||
grpc.WithTransportCredentials(insecure.NewCredentials()),
|
||||
}
|
||||
@@ -66,12 +64,12 @@ func StartOpenFGAHttpSever(cfg *setting.Cfg, srv grpcserver.Provider, logger log
|
||||
retries++
|
||||
}
|
||||
if addr == "" {
|
||||
return fmt.Errorf("failed to start HTTP server: GRPC server unavailable")
|
||||
return nil, fmt.Errorf("failed to create HTTP server: GRPC server unavailable")
|
||||
}
|
||||
|
||||
conn, err := grpc.NewClient(addr, dialOpts...)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to dial GRPC: %w", err)
|
||||
return nil, fmt.Errorf("unable to dial GRPC: %w", err)
|
||||
}
|
||||
|
||||
muxOpts := []runtime.ServeMuxOption{
|
||||
@@ -91,11 +89,11 @@ func StartOpenFGAHttpSever(cfg *setting.Cfg, srv grpcserver.Provider, logger log
|
||||
}
|
||||
mux := runtime.NewServeMux(muxOpts...)
|
||||
if err := openfgav1.RegisterOpenFGAServiceHandler(context.TODO(), mux, conn); err != nil {
|
||||
return fmt.Errorf("failed to register gateway handler: %w", err)
|
||||
return nil, fmt.Errorf("failed to register gateway handler: %w", err)
|
||||
}
|
||||
|
||||
httpServer := &http.Server{
|
||||
Addr: cfg.Zanzana.HttpAddr,
|
||||
return &http.Server{
|
||||
Addr: cfg.OpenFGAHttpAddr,
|
||||
Handler: cors.New(cors.Options{
|
||||
AllowedOrigins: []string{"*"},
|
||||
AllowCredentials: true,
|
||||
@@ -104,13 +102,5 @@ func StartOpenFGAHttpSever(cfg *setting.Cfg, srv grpcserver.Provider, logger log
|
||||
http.MethodHead, http.MethodPatch, http.MethodDelete, http.MethodPut},
|
||||
}).Handler(mux),
|
||||
ReadHeaderTimeout: 30 * time.Second,
|
||||
}
|
||||
go func() {
|
||||
err = httpServer.ListenAndServe()
|
||||
if err != nil {
|
||||
logger.Error("failed to start http server", zapcore.Field{Key: "err", Type: zapcore.ErrorType, Interface: err})
|
||||
}
|
||||
}()
|
||||
logger.Info(fmt.Sprintf("OpenFGA HTTP server listening on '%s'...", httpServer.Addr))
|
||||
return nil
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"github.com/fullstorydev/grpchan/inprocgrpc"
|
||||
authzv1 "github.com/grafana/authlib/authz/proto/v1"
|
||||
openfgav1 "github.com/openfga/api/proto/openfga/v1"
|
||||
"github.com/openfga/language/pkg/go/transformer"
|
||||
"go.opentelemetry.io/otel"
|
||||
|
||||
dashboardalpha1 "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1"
|
||||
@@ -34,9 +33,8 @@ type Server struct {
|
||||
openfga openfgav1.OpenFGAServiceServer
|
||||
openfgaClient openfgav1.OpenFGAServiceClient
|
||||
|
||||
cfg setting.ZanzanaSettings
|
||||
cfg setting.ZanzanaServerSettings
|
||||
logger log.Logger
|
||||
modules []transformer.ModuleFile
|
||||
stores map[string]storeInfo
|
||||
storesMU *sync.Mutex
|
||||
cache *localcache.CacheService
|
||||
@@ -47,25 +45,7 @@ type storeInfo struct {
|
||||
ModelID string
|
||||
}
|
||||
|
||||
type ServerOption func(s *Server)
|
||||
|
||||
func WithLogger(logger log.Logger) ServerOption {
|
||||
return func(s *Server) {
|
||||
s.logger = logger
|
||||
}
|
||||
}
|
||||
|
||||
func WithSchema(modules []transformer.ModuleFile) ServerOption {
|
||||
return func(s *Server) {
|
||||
s.modules = modules
|
||||
}
|
||||
}
|
||||
|
||||
func NewAuthzServer(cfg *setting.Cfg, openfga openfgav1.OpenFGAServiceServer) (*Server, error) {
|
||||
return NewAuthz(cfg, openfga)
|
||||
}
|
||||
|
||||
func NewAuthz(cfg *setting.Cfg, openfga openfgav1.OpenFGAServiceServer, opts ...ServerOption) (*Server, error) {
|
||||
func NewServer(cfg setting.ZanzanaServerSettings, openfga openfgav1.OpenFGAServiceServer, logger log.Logger) (*Server, error) {
|
||||
channel := &inprocgrpc.Channel{}
|
||||
openfgav1.RegisterOpenFGAServiceServer(channel, openfga)
|
||||
openFGAClient := openfgav1.NewOpenFGAServiceClient(channel)
|
||||
@@ -75,16 +55,9 @@ func NewAuthz(cfg *setting.Cfg, openfga openfgav1.OpenFGAServiceServer, opts ...
|
||||
openfgaClient: openFGAClient,
|
||||
storesMU: &sync.Mutex{},
|
||||
stores: make(map[string]storeInfo),
|
||||
cfg: cfg.Zanzana,
|
||||
cache: localcache.New(cfg.Zanzana.CheckQueryCacheTTL, cacheCleanInterval),
|
||||
}
|
||||
|
||||
for _, o := range opts {
|
||||
o(s)
|
||||
}
|
||||
|
||||
if s.logger == nil {
|
||||
s.logger = log.New("authz-server")
|
||||
cfg: cfg,
|
||||
cache: localcache.New(cfg.CheckQueryCacheTTL, cacheCleanInterval),
|
||||
logger: logger,
|
||||
}
|
||||
|
||||
return s, nil
|
||||
|
||||
@@ -71,10 +71,10 @@ func setup(t *testing.T, testDB db.DB, cfg *setting.Cfg) *Server {
|
||||
t.Helper()
|
||||
store, err := store.NewEmbeddedStore(cfg, testDB, log.NewNopLogger())
|
||||
require.NoError(t, err)
|
||||
openfga, err := NewOpenFGA(&cfg.Zanzana, store, log.NewNopLogger())
|
||||
openfga, err := NewOpenFGAServer(cfg.ZanzanaServer, store, log.NewNopLogger())
|
||||
require.NoError(t, err)
|
||||
|
||||
srv, err := NewAuthz(cfg, openfga)
|
||||
srv, err := NewServer(cfg.ZanzanaServer, openfga, log.NewNopLogger())
|
||||
require.NoError(t, err)
|
||||
|
||||
storeInf, err := srv.getStoreInfo(context.Background(), namespace)
|
||||
|
||||
Reference in New Issue
Block a user