userlog: reworked consumer and http disable/enable ability

This commit is contained in:
Alex Ababii committed 2026-09-07 16:55:48 +02:00
1 parent a9cede5438
commit 9c628b697c
12 files changed
+192 -166

No files matched your search

+2 -2
View File
@@ -102,10 +102,10 @@ func MissingURLSigningSecret(service string) error {
service, defaults.BaseConfigPath())
}
func AllComponentsDisabledError(service string) error {
func AllApiHandlersDisabledError(service string) error {
return fmt.Errorf("All request handlers and event consumers are disabled for %s; at least one component must be enabled."+
"Make sure your %s config contains the proper values "+
"(e.g. by using 'opencloud init --diff' and applying the patch or setting a value manually in "+
"the config/corresponding environment variable).",
"the config/corresponding environment variable).",
service, defaults.BaseConfigPath())
}
+43 -35
View File
@@ -19,11 +19,11 @@ import (
"github.com/opencloud-eu/opencloud/services/userlog/pkg/config"
"github.com/opencloud-eu/opencloud/services/userlog/pkg/config/parser"
"github.com/opencloud-eu/opencloud/services/userlog/pkg/metrics"
"github.com/opencloud-eu/opencloud/services/userlog/pkg/server/consumer"
"github.com/opencloud-eu/opencloud/services/userlog/pkg/server/debug"
"github.com/opencloud-eu/opencloud/services/userlog/pkg/server/http"
"github.com/opencloud-eu/opencloud/services/userlog/pkg/service"
"github.com/go-chi/chi/v5"
"github.com/opencloud-eu/reva/v2/pkg/events"
"github.com/opencloud-eu/reva/v2/pkg/events/stream"
"github.com/opencloud-eu/reva/v2/pkg/rgrpc/todo/pool"
@@ -87,8 +87,16 @@ func Server(cfg *config.Config) *cobra.Command {
connName := generators.GenerateConnectionName(cfg.Service.Name, generators.NTypeBus)
var evStream events.Stream
if !cfg.EventsDisabled {
evStream, err = stream.NatsFromConfig(connName, false, stream.NatsConfig(cfg.Events))
if !cfg.Events.Disabled {
evStream, err = stream.NatsFromConfig(connName, false, stream.NatsConfig{
Endpoint: cfg.Events.Endpoint,
Cluster: cfg.Events.Cluster,
TLSInsecure: cfg.Events.TLSInsecure,
TLSRootCACertificate: cfg.Events.TLSRootCACertificate,
EnableTLS: cfg.Events.EnableTLS,
AuthUsername: cfg.Events.AuthUsername,
AuthPassword: cfg.Events.AuthPassword,
})
if err != nil {
return err
}
@@ -125,21 +133,31 @@ func Server(cfg *config.Config) *cobra.Command {
vClient := settingssvc.NewValueService("eu.opencloud.api.settings", grpcClient)
rClient := settingssvc.NewRoleService("eu.opencloud.api.settings", grpcClient)
handle, err := service.NewUserlogService(
service.Logger(logger),
service.Stream(evStream),
service.Store(st),
service.Config(cfg),
service.HistoryClient(hClient),
service.GatewaySelector(gatewaySelector),
service.ValueClient(vClient),
service.RegisteredEvents(_registeredEvents),
service.TraceProvider(tracerProvider),
)
if err != nil {
return err
}
gr := runner.NewGroup()
if !cfg.HTTPDisabled {
if !cfg.HTTP.Disabled {
server, err := http.Server(
http.Logger(logger),
http.Context(ctx),
http.Config(cfg),
http.Metrics(mtrcs),
http.Store(st),
http.Stream(evStream),
http.GatewaySelector(gatewaySelector),
http.History(hClient),
http.Value(vClient),
http.Role(rClient),
http.RegisteredEvents(_registeredEvents),
http.UserlogService(handle),
http.TracerProvider(tracerProvider),
)
@@ -151,33 +169,23 @@ func Server(cfg *config.Config) *cobra.Command {
gr.Add(runner.NewGoMicroHttpServerRunner(cfg.Service.Name+".http", server))
} else {
logger.Info().Msg("HTTP server disabled, not starting HTTP service")
if !cfg.EventsDisabled {
_, err := service.NewUserlogService(
service.Logger(logger),
service.Stream(evStream),
service.Mux(chi.NewMux()),
service.Store(st),
service.Config(cfg),
service.HistoryClient(hClient),
service.GatewaySelector(gatewaySelector),
service.ValueClient(vClient),
service.RoleClient(rClient),
service.RegisteredEvents(_registeredEvents),
service.TraceProvider(tracerProvider),
)
if err != nil {
return err
}
gr.Add(runner.New(cfg.Service.Name+".consumer", func() error {
<-ctx.Done()
return nil
}, func() {}))
}
}
if cfg.EventsDisabled {
if !cfg.Events.Disabled {
server, err := consumer.Server(
consumer.Logger(logger),
consumer.Context(ctx),
consumer.Config(cfg),
consumer.Stream(evStream),
consumer.UserlogService(handle),
consumer.RegisteredEvents(_registeredEvents),
)
if err != nil {
return err
}
gr.Add(server)
} else {
logger.Info().Msg("event listening disabled, not starting event consumer")
}
+2 -3
View File
@@ -30,9 +30,6 @@ type Config struct {
DisableSSE bool `yaml:"disable_sse" env:"OC_DISABLE_SSE,USERLOG_DISABLE_SSE" desc:"Disables server-sent events (sse). When disabled, clients will no longer receive sse notifications." introductionVersion:"1.0.0"`
EventsDisabled bool `yaml:"events_disabled" env:"USERLOG_EVENTS_DISABLED" desc:"Disables listening for events. Set this to true if the service should only handle HTTP requests." introductionVersion:"%NEXT%"`
HTTPDisabled bool `yaml:"http_disabled" env:"USERLOG_HTTP_DISABLED" desc:"Disables the HTTP service. Set this to true if the service should only handle events." introductionVersion:"%NEXT%"`
GlobalNotificationsSecret string `yaml:"global_notifications_secret" env:"USERLOG_GLOBAL_NOTIFICATIONS_SECRET" desc:"The secret to secure the global notifications endpoint. Only system admins and users knowing that secret can call the global notifications POST/DELETE endpoints." introductionVersion:"1.0.0"`
ServiceAccount ServiceAccount `yaml:"service_account"`
@@ -56,6 +53,7 @@ type Persistence struct {
// Events combines the configuration options for the event bus.
type Events struct {
Disabled bool `yaml:"disabled" env:"USERLOG_EVENTS_DISABLED" desc:"Disables listening for events. Set this to true if the service should only handle HTTP requests." introductionVersion:"%NEXT%"`
Endpoint string `yaml:"endpoint" env:"OC_EVENTS_ENDPOINT;USERLOG_EVENTS_ENDPOINT" desc:"The address of the event system. The event system is the message queuing service. It is used as message broker for the microservice architecture." introductionVersion:"1.0.0"`
Cluster string `yaml:"cluster" env:"OC_EVENTS_CLUSTER;USERLOG_EVENTS_CLUSTER" desc:"The clusterID of the event system. The event system is the message queuing service. It is used as message broker for the microservice architecture. Mandatory when using NATS as event system." introductionVersion:"1.0.0"`
TLSInsecure bool `yaml:"tls_insecure" env:"OC_INSECURE;OC_EVENTS_TLS_INSECURE;USERLOG_EVENTS_TLS_INSECURE" desc:"Whether to verify the server TLS certificates." introductionVersion:"1.0.0"`
@@ -75,6 +73,7 @@ type CORS struct {
// HTTP defines the available http configuration.
type HTTP struct {
Disabled bool `yaml:"disabled" env:"USERLOG_HTTP_DISABLED" desc:"Disables the HTTP service. Set this to true if the service should only handle events." introductionVersion:"%NEXT%"`
Addr string `yaml:"addr" env:"USERLOG_HTTP_ADDR" desc:"The bind address of the HTTP service." introductionVersion:"1.0.0"`
Namespace string `yaml:"-"`
Root string `yaml:"root" env:"USERLOG_HTTP_ROOT" desc:"Subdirectory that serves as the root for this HTTP service." introductionVersion:"1.0.0"`
+2 -2
View File
@@ -46,8 +46,8 @@ func Validate(cfg *config.Config) error {
return shared.MissingServiceAccountSecret(cfg.Service.Name)
}
if cfg.EventsDisabled && cfg.HTTPDisabled {
return shared.AllComponentsDisabledError(cfg.Service.Name)
if cfg.Events.Disabled && cfg.HTTP.Disabled {
return shared.AllApiHandlersDisabledError(cfg.Service.Name)
}
return nil
@@ -0,0 +1,76 @@
package consumer
import (
"context"
"github.com/opencloud-eu/opencloud/pkg/log"
"github.com/opencloud-eu/opencloud/services/userlog/pkg/config"
svc "github.com/opencloud-eu/opencloud/services/userlog/pkg/service"
"github.com/opencloud-eu/reva/v2/pkg/events"
)
// Option defines a single option function.
type Option func(o *Options)
// Options defines the available options for this package.
type Options struct {
Logger log.Logger
Context context.Context
Config *config.Config
Stream events.Stream
UserlogService *svc.UserlogService
RegisteredEvents []events.Unmarshaller
}
// newOptions initializes the available default options.
func newOptions(opts ...Option) Options {
opt := Options{}
for _, o := range opts {
o(&opt)
}
return opt
}
// Logger provides a function to set the logger option.
func Logger(val log.Logger) Option {
return func(o *Options) {
o.Logger = val
}
}
// Context provides a function to set the context option.
func Context(val context.Context) Option {
return func(o *Options) {
o.Context = val
}
}
// Config provides a function to set the config option.
func Config(val *config.Config) Option {
return func(o *Options) {
o.Config = val
}
}
// Stream provides a function to configure the stream
func Stream(stream events.Stream) Option {
return func(o *Options) {
o.Stream = stream
}
}
// UserlogService provides a function to set the userlog service
func UserlogService(s *svc.UserlogService) Option {
return func(o *Options) {
o.UserlogService = s
}
}
// RegisteredEvents provides a function to register events
func RegisteredEvents(evs []events.Unmarshaller) Option {
return func(o *Options) {
o.RegisteredEvents = evs
}
}
@@ -0,0 +1,29 @@
package consumer
import (
"errors"
"github.com/opencloud-eu/opencloud/pkg/runner"
"github.com/opencloud-eu/reva/v2/pkg/events"
)
// Server starts the event consumer for the userlog service
func Server(opts ...Option) (*runner.Runner, error) {
options := newOptions(opts...)
if options.UserlogService == nil {
return nil, errors.New("need non nil userlog service to consume events")
}
ch, err := events.Consume(options.Stream, "userlog", options.RegisteredEvents...)
if err != nil {
return nil, err
}
options.UserlogService.MemorizeEvents(ch)
return runner.New(options.Config.Service.Name+".consumer", func() error {
<-options.Context.Done()
return nil
}, func() {}), nil
}
+13 -57
View File
@@ -3,17 +3,13 @@ package http
import (
"context"
gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
"github.com/opencloud-eu/opencloud/pkg/log"
ehsvc "github.com/opencloud-eu/opencloud/protogen/gen/opencloud/services/eventhistory/v0"
settingssvc "github.com/opencloud-eu/opencloud/protogen/gen/opencloud/services/settings/v0"
"github.com/opencloud-eu/opencloud/services/userlog/pkg/config"
"github.com/opencloud-eu/opencloud/services/userlog/pkg/metrics"
"github.com/opencloud-eu/reva/v2/pkg/events"
"github.com/opencloud-eu/reva/v2/pkg/rgrpc/todo/pool"
svc "github.com/opencloud-eu/opencloud/services/userlog/pkg/service"
"github.com/spf13/pflag"
"go-micro.dev/v4/store"
"go.opentelemetry.io/otel/trace"
)
@@ -22,20 +18,15 @@ type Option func(o *Options)
// Options defines the available options for this package.
type Options struct {
Logger log.Logger
Context context.Context
Config *config.Config
Metrics *metrics.Metrics
Flags []pflag.Flag
Namespace string
Store store.Store
Stream events.Stream
GatewaySelector pool.Selectable[gateway.GatewayAPIClient]
HistoryClient ehsvc.EventHistoryService
ValueClient settingssvc.ValueService
RoleClient settingssvc.RoleService
RegisteredEvents []events.Unmarshaller
TracerProvider trace.TracerProvider
Logger log.Logger
Context context.Context
Config *config.Config
Metrics *metrics.Metrics
Flags []pflag.Flag
Namespace string
RoleClient settingssvc.RoleService
UserlogService *svc.UserlogService
TracerProvider trace.TracerProvider
}
// newOptions initializes the available default options.
@@ -91,45 +82,10 @@ func Namespace(val string) Option {
}
}
// Store provides a function to configure the store
func Store(store store.Store) Option {
// UserlogService provides a function to set the userlog service
func UserlogService(s *svc.UserlogService) Option {
return func(o *Options) {
o.Store = store
}
}
// Stream provides a function to configure the stream
func Stream(stream events.Stream) Option {
return func(o *Options) {
o.Stream = stream
}
}
// GatewaySelector provides a function to configure the gateway client selector
func GatewaySelector(gatewaySelector pool.Selectable[gateway.GatewayAPIClient]) Option {
return func(o *Options) {
o.GatewaySelector = gatewaySelector
}
}
// History provides a function to configure the event history client
func History(h ehsvc.EventHistoryService) Option {
return func(o *Options) {
o.HistoryClient = h
}
}
// RegisteredEvents provides a function to register events
func RegisteredEvents(evs []events.Unmarshaller) Option {
return func(o *Options) {
o.RegisteredEvents = evs
}
}
// Value provides a function to configure the value service client
func Value(vs settingssvc.ValueService) Option {
return func(o *Options) {
o.ValueClient = vs
o.UserlogService = s
}
}
+18 -16
View File
@@ -1,6 +1,7 @@
package http
import (
"errors"
"fmt"
stdhttp "net/http"
@@ -10,6 +11,7 @@ import (
"github.com/opencloud-eu/opencloud/pkg/account"
"github.com/opencloud-eu/opencloud/pkg/cors"
"github.com/opencloud-eu/opencloud/pkg/middleware"
"github.com/opencloud-eu/opencloud/pkg/roles"
"github.com/opencloud-eu/opencloud/pkg/service/http"
"github.com/opencloud-eu/opencloud/pkg/tracing"
"github.com/opencloud-eu/opencloud/pkg/version"
@@ -77,24 +79,24 @@ func Server(opts ...Option) (http.Service, error) {
),
)
handle, err := svc.NewUserlogService(
svc.Logger(options.Logger),
svc.Stream(options.Stream),
svc.Mux(mux),
svc.Store(options.Store),
svc.Config(options.Config),
svc.HistoryClient(options.HistoryClient),
svc.GatewaySelector(options.GatewaySelector),
svc.ValueClient(options.ValueClient),
svc.RoleClient(options.RoleClient),
svc.RegisteredEvents(options.RegisteredEvents),
svc.TraceProvider(options.TracerProvider),
)
if err != nil {
return http.Service{}, err
if options.UserlogService == nil {
return http.Service{}, errors.New("need non nil userlog service to serve http requests")
}
if err := micro.RegisterHandler(service.Server(), handle); err != nil {
m := roles.NewManager(
// TODO: caching?
roles.Logger(options.Logger),
roles.RoleService(options.RoleClient),
)
mux.Route("/ocs/v2.php/apps/notifications/api/v1/notifications", func(r chi.Router) {
r.Get("/", options.UserlogService.HandleGetEvents)
r.Delete("/", options.UserlogService.HandleDeleteEvents)
r.Post("/global", svc.RequireAdminOrSecret(&m, options.Config.GlobalNotificationsSecret)(options.UserlogService.HandlePostGlobalEvent))
r.Delete("/global", svc.RequireAdminOrSecret(&m, options.Config.GlobalNotificationsSecret)(options.UserlogService.HandleDeleteGlobalEvent))
})
if err := micro.RegisterHandler(service.Server(), mux); err != nil {
return http.Service{}, err
}
-5
View File
@@ -18,11 +18,6 @@ import (
// HeaderAcceptLanguage is the header where the client can set the locale
var HeaderAcceptLanguage = "Accept-Language"
// ServeHTTP fulfills Handler interface
func (ul *UserlogService) ServeHTTP(w http.ResponseWriter, r *http.Request) {
ul.m.ServeHTTP(w, r)
}
// HandleGetEvents is the GET handler for events
func (ul *UserlogService) HandleGetEvents(w http.ResponseWriter, r *http.Request) {
ctx, span := ul.tracer.Start(r.Context(), "HandleGetEvents")
-17
View File
@@ -2,7 +2,6 @@ package service
import (
gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
"github.com/go-chi/chi/v5"
"github.com/opencloud-eu/opencloud/pkg/log"
ehsvc "github.com/opencloud-eu/opencloud/protogen/gen/opencloud/services/eventhistory/v0"
settingssvc "github.com/opencloud-eu/opencloud/protogen/gen/opencloud/services/settings/v0"
@@ -20,13 +19,11 @@ type Option func(*Options)
type Options struct {
Logger log.Logger
Stream events.Stream
Mux *chi.Mux
Store store.Store
Config *config.Config
HistoryClient ehsvc.EventHistoryService
GatewaySelector pool.Selectable[gateway.GatewayAPIClient]
ValueClient settingssvc.ValueService
RoleClient settingssvc.RoleService
RegisteredEvents []events.Unmarshaller
TraceProvider trace.TracerProvider
}
@@ -45,13 +42,6 @@ func Stream(s events.Stream) Option {
}
}
// Mux defines the muxer for the userlog service
func Mux(m *chi.Mux) Option {
return func(o *Options) {
o.Mux = m
}
}
// Store defines the store for the userlog service
func Store(s store.Store) Option {
return func(o *Options) {
@@ -94,13 +84,6 @@ func ValueClient(vs settingssvc.ValueService) Option {
}
}
// RoleClient adds a grpc client for the role service
func RoleClient(rs settingssvc.RoleService) Option {
return func(o *Options) {
o.RoleClient = rs
}
}
// TraceProvider adds a tracer provider for the userlog service
func TraceProvider(tp trace.TracerProvider) Option {
return func(o *Options) {
-25
View File
@@ -10,7 +10,6 @@ import (
gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
"github.com/go-chi/chi/v5"
"github.com/opencloud-eu/reva/v2/pkg/events"
"github.com/opencloud-eu/reva/v2/pkg/rgrpc/todo/pool"
"github.com/opencloud-eu/reva/v2/pkg/utils"
@@ -20,7 +19,6 @@ import (
ocEvents "github.com/opencloud-eu/opencloud/pkg/events"
"github.com/opencloud-eu/opencloud/pkg/l10n"
"github.com/opencloud-eu/opencloud/pkg/log"
"github.com/opencloud-eu/opencloud/pkg/roles"
ehmsg "github.com/opencloud-eu/opencloud/protogen/gen/opencloud/messages/eventhistory/v0"
ehsvc "github.com/opencloud-eu/opencloud/protogen/gen/opencloud/services/eventhistory/v0"
settingssvc "github.com/opencloud-eu/opencloud/protogen/gen/opencloud/services/settings/v0"
@@ -30,7 +28,6 @@ import (
// UserlogService is the service responsible for user activities
type UserlogService struct {
log log.Logger
m *chi.Mux
store store.Store
cfg *config.Config
historyClient ehsvc.EventHistoryService
@@ -56,7 +53,6 @@ func NewUserlogService(opts ...Option) (*UserlogService, error) {
ul := &UserlogService{
log: o.Logger,
m: o.Mux,
store: o.Store,
cfg: o.Config,
historyClient: o.HistoryClient,
@@ -74,27 +70,6 @@ func NewUserlogService(opts ...Option) (*UserlogService, error) {
ul.registeredEvents[typ.String()] = e
}
m := roles.NewManager(
// TODO: caching?
roles.Logger(o.Logger),
roles.RoleService(o.RoleClient),
)
ul.m.Route("/ocs/v2.php/apps/notifications/api/v1/notifications", func(r chi.Router) {
r.Get("/", ul.HandleGetEvents)
r.Delete("/", ul.HandleDeleteEvents)
r.Post("/global", RequireAdminOrSecret(&m, o.Config.GlobalNotificationsSecret)(ul.HandlePostGlobalEvent))
r.Delete("/global", RequireAdminOrSecret(&m, o.Config.GlobalNotificationsSecret)(ul.HandleDeleteGlobalEvent))
})
if !o.Config.EventsDisabled {
ch, err := events.Consume(o.Stream, "userlog", o.RegisteredEvents...)
if err != nil {
return nil, err
}
go ul.MemorizeEvents(ch)
}
return ul, nil
}
+7 -4
View File
@@ -12,7 +12,6 @@ import (
user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
"github.com/go-chi/chi/v5"
"github.com/google/uuid"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
@@ -100,7 +99,6 @@ var _ = Describe("UserlogService", func() {
service.Stream(bus),
service.Store(sto),
service.Logger(log.NewLogger()),
service.Mux(chi.NewMux()),
service.GatewaySelector(gatewaySelector),
service.HistoryClient(&ehc),
service.ValueClient(&vc),
@@ -110,6 +108,10 @@ var _ = Describe("UserlogService", func() {
service.TraceProvider(trace.NewNoopTracerProvider()),
)
Expect(err).ToNot(HaveOccurred())
ch, err := events.Consume(bus, "userlog", events.SpaceDisabled{})
Expect(err).ToNot(HaveOccurred())
go ul.MemorizeEvents(ch)
})
It("it stores, returns and deletes a couple of events", func() {
@@ -172,14 +174,15 @@ var _ = Describe("UserlogService", func() {
localEhc := mocks.EventHistoryService{}
cfgNoEvents := &config.Config{
MaxConcurrency: 5,
EventsDisabled: true,
Events: config.Events{
Disabled: true,
},
}
ulNoConsumer, err := service.NewUserlogService(
service.Config(cfgNoEvents),
service.Stream(bus),
service.Store(sto),
service.Logger(log.NewLogger()),
service.Mux(chi.NewMux()),
service.GatewaySelector(gatewaySelector),
service.HistoryClient(&localEhc),
service.ValueClient(&vc),