From 238d2dfdbc19fa4eaeef47c910bebcd17f31fc82 Mon Sep 17 00:00:00 2001 From: David Christofas Date: Fri, 18 Feb 2022 15:44:53 +0100 Subject: [PATCH] add new notifications service --- notifications/cmd/notifications/main.go | 14 +++++ notifications/pkg/command/health.go | 18 +++++++ notifications/pkg/command/root.go | 64 +++++++++++++++++++++++ notifications/pkg/command/server.go | 47 +++++++++++++++++ notifications/pkg/command/version.go | 19 +++++++ notifications/pkg/config/config.go | 19 +++++++ notifications/pkg/config/debug.go | 9 ++++ notifications/pkg/config/defaultconfig.go | 12 +++++ notifications/pkg/config/log.go | 9 ++++ notifications/pkg/config/parser/parse.go | 40 ++++++++++++++ notifications/pkg/config/service.go | 6 +++ notifications/pkg/logging/logging.go | 17 ++++++ notifications/pkg/service/service.go | 46 ++++++++++++++++ ocis-pkg/config/config.go | 2 + ocis-pkg/config/defaultconfig.go | 2 + ocis/pkg/command/notifications.go | 26 +++++++++ ocis/pkg/runtime/service/service.go | 2 + 17 files changed, 352 insertions(+) create mode 100644 notifications/cmd/notifications/main.go create mode 100644 notifications/pkg/command/health.go create mode 100644 notifications/pkg/command/root.go create mode 100644 notifications/pkg/command/server.go create mode 100644 notifications/pkg/command/version.go create mode 100644 notifications/pkg/config/config.go create mode 100644 notifications/pkg/config/debug.go create mode 100644 notifications/pkg/config/defaultconfig.go create mode 100644 notifications/pkg/config/log.go create mode 100644 notifications/pkg/config/parser/parse.go create mode 100644 notifications/pkg/config/service.go create mode 100644 notifications/pkg/logging/logging.go create mode 100644 notifications/pkg/service/service.go create mode 100644 ocis/pkg/command/notifications.go diff --git a/notifications/cmd/notifications/main.go b/notifications/cmd/notifications/main.go new file mode 100644 index 000000000..6b4323433 --- /dev/null +++ b/notifications/cmd/notifications/main.go @@ -0,0 +1,14 @@ +package main + +import ( + "os" + + "github.com/owncloud/ocis/notifications/pkg/command" + "github.com/owncloud/ocis/notifications/pkg/config" +) + +func main() { + if err := command.Execute(config.DefaultConfig()); err != nil { + os.Exit(1) + } +} diff --git a/notifications/pkg/command/health.go b/notifications/pkg/command/health.go new file mode 100644 index 000000000..7f2adb85b --- /dev/null +++ b/notifications/pkg/command/health.go @@ -0,0 +1,18 @@ +package command + +import ( + "github.com/owncloud/ocis/notifications/pkg/config" + "github.com/urfave/cli/v2" +) + +// Health is the entrypoint for the health command. +func Health(cfg *config.Config) *cli.Command { + return &cli.Command{ + Name: "health", + Usage: "Check health status", + Action: func(c *cli.Context) error { + // Not implemented + return nil + }, + } +} diff --git a/notifications/pkg/command/root.go b/notifications/pkg/command/root.go new file mode 100644 index 000000000..805d8cec3 --- /dev/null +++ b/notifications/pkg/command/root.go @@ -0,0 +1,64 @@ +package command + +import ( + "context" + "os" + + "github.com/owncloud/ocis/notifications/pkg/config" + "github.com/owncloud/ocis/ocis-pkg/clihelper" + ociscfg "github.com/owncloud/ocis/ocis-pkg/config" + "github.com/thejerf/suture/v4" + "github.com/urfave/cli/v2" +) + +// GetCommands provides all commands for this service +func GetCommands(cfg *config.Config) cli.Commands { + return []*cli.Command{ + // start this service + Server(cfg), + + // interaction with this service + + // infos about this service + Health(cfg), + Version(cfg), + } +} + +// Execute is the entry point for the notifications command. +func Execute(cfg *config.Config) error { + app := clihelper.DefaultApp(&cli.App{ + Name: "notifications", + Usage: "starts notifications service", + Commands: GetCommands(cfg), + }) + + cli.HelpFlag = &cli.BoolFlag{ + Name: "help,h", + Usage: "Show the help", + } + + return app.Run(os.Args) +} + +// SutureService allows for the notifications command to be embedded and supervised by a suture supervisor tree. +type SutureService struct { + cfg *config.Config +} + +// NewSutureService creates a new notifications.SutureService +func NewSutureService(cfg *ociscfg.Config) suture.Service { + cfg.Settings.Commons = cfg.Commons + return SutureService{ + cfg: cfg.Notifications, + } +} + +func (s SutureService) Serve(ctx context.Context) error { + s.cfg.Context = ctx + if err := Execute(s.cfg); err != nil { + return err + } + + return nil +} diff --git a/notifications/pkg/command/server.go b/notifications/pkg/command/server.go new file mode 100644 index 000000000..48ba224aa --- /dev/null +++ b/notifications/pkg/command/server.go @@ -0,0 +1,47 @@ +package command + +import ( + "fmt" + + "github.com/asim/go-micro/plugins/events/nats/v4" + "github.com/cs3org/reva/pkg/events" + "github.com/cs3org/reva/pkg/events/server" + "github.com/owncloud/ocis/notifications/pkg/config" + "github.com/owncloud/ocis/notifications/pkg/config/parser" + "github.com/owncloud/ocis/notifications/pkg/logging" + "github.com/owncloud/ocis/notifications/pkg/service" + "github.com/urfave/cli/v2" +) + +// Server is the entrypoint for the server command. +func Server(cfg *config.Config) *cli.Command { + return &cli.Command{ + Name: "server", + Usage: fmt.Sprintf("start %s extension without runtime (unsupervised mode)", cfg.Service.Name), + Category: "server", + Before: func(c *cli.Context) error { + return parser.ParseConfig(cfg) + }, + Action: func(c *cli.Context) error { + logger := logging.Configure(cfg.Service.Name, cfg.Log) + + group := "notifications" + + evs := []events.Unmarshaller{ + events.ShareCreated{}, + } + + client, err := server.NewNatsStream(nats.Address("127.0.0.1:4222"), nats.ClusterID("test-cluster")) + if err != nil { + return err + } + evts, err := events.Consume(client, group, evs...) + if err != nil { + return err + } + + svc := service.NewEventsNotifier(evts, logger) + return svc.Run() + }, + } +} diff --git a/notifications/pkg/command/version.go b/notifications/pkg/command/version.go new file mode 100644 index 000000000..f2d47a569 --- /dev/null +++ b/notifications/pkg/command/version.go @@ -0,0 +1,19 @@ +package command + +import ( + "github.com/owncloud/ocis/notifications/pkg/config" + "github.com/urfave/cli/v2" +) + +// Version prints the service versions of all running instances. +func Version(cfg *config.Config) *cli.Command { + return &cli.Command{ + Name: "version", + Usage: "print the version of this binary and the running extension instances", + Category: "info", + Action: func(c *cli.Context) error { + // not implemented + return nil + }, + } +} diff --git a/notifications/pkg/config/config.go b/notifications/pkg/config/config.go new file mode 100644 index 000000000..254832cd1 --- /dev/null +++ b/notifications/pkg/config/config.go @@ -0,0 +1,19 @@ +package config + +import ( + "context" + + "github.com/owncloud/ocis/ocis-pkg/shared" +) + +// Config combines all available configuration parts. +type Config struct { + *shared.Commons + + Service Service + + Log *Log `ocisConfig:"log"` + Debug Debug `ocisConfig:"debug"` + + Context context.Context +} diff --git a/notifications/pkg/config/debug.go b/notifications/pkg/config/debug.go new file mode 100644 index 000000000..da6d2d590 --- /dev/null +++ b/notifications/pkg/config/debug.go @@ -0,0 +1,9 @@ +package config + +// Debug defines the available debug configuration. +type Debug struct { + Addr string `ocisConfig:"addr" env:"NOTIFICATIONS_DEBUG_ADDR"` + Token string `ocisConfig:"token" env:"NOTIFICATIONS_DEBUG_TOKEN"` + Pprof bool `ocisConfig:"pprof" env:"NOTIFICATIONS_DEBUG_PPROF"` + Zpages bool `ocisConfig:"zpages" env:"NOTIFICATIONS_DEBUG_ZPAGES"` +} diff --git a/notifications/pkg/config/defaultconfig.go b/notifications/pkg/config/defaultconfig.go new file mode 100644 index 000000000..e98e72ff6 --- /dev/null +++ b/notifications/pkg/config/defaultconfig.go @@ -0,0 +1,12 @@ +package config + +// NOTE: Most of this configuration is not needed to keep it as simple as possible +// TODO: Clean up unneeded configuration + +func DefaultConfig() *Config { + return &Config{ + Service: Service{ + Name: "notifications", + }, + } +} diff --git a/notifications/pkg/config/log.go b/notifications/pkg/config/log.go new file mode 100644 index 000000000..ddb4d391f --- /dev/null +++ b/notifications/pkg/config/log.go @@ -0,0 +1,9 @@ +package config + +// Log defines the available log configuration. +type Log struct { + Level string `mapstructure:"level" env:"OCIS_LOG_LEVEL;NOTIFICATIONS_LOG_LEVEL"` + Pretty bool `mapstructure:"pretty" env:"OCIS_LOG_PRETTY;NOTIFICATIONS_LOG_PRETTY"` + Color bool `mapstructure:"color" env:"OCIS_LOG_COLOR;NOTIFICATIONS_LOG_COLOR"` + File string `mapstructure:"file" env:"OCIS_LOG_FILE;NOTIFICATIONS_LOG_FILE"` +} diff --git a/notifications/pkg/config/parser/parse.go b/notifications/pkg/config/parser/parse.go new file mode 100644 index 000000000..5bc4e6e57 --- /dev/null +++ b/notifications/pkg/config/parser/parse.go @@ -0,0 +1,40 @@ +package parser + +import ( + "errors" + + "github.com/owncloud/ocis/notifications/pkg/config" + ociscfg "github.com/owncloud/ocis/ocis-pkg/config" + + "github.com/owncloud/ocis/ocis-pkg/config/envdecode" +) + +// ParseConfig loads accounts configuration from known paths. +func ParseConfig(cfg *config.Config) error { + _, err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg) + if err != nil { + return err + } + + // provide with defaults for shared logging, since we need a valid destination address for BindEnv. + if cfg.Log == nil && cfg.Commons != nil && cfg.Commons.Log != nil { + cfg.Log = &config.Log{ + Level: cfg.Commons.Log.Level, + Pretty: cfg.Commons.Log.Pretty, + Color: cfg.Commons.Log.Color, + File: cfg.Commons.Log.File, + } + } else if cfg.Log == nil { + cfg.Log = &config.Log{} + } + + // load all env variables relevant to the config in the current context. + if err := envdecode.Decode(cfg); err != nil { + // no environment variable set for this config is an expected "error" + if !errors.Is(err, envdecode.ErrNoTargetFieldsAreSet) { + return err + } + } + + return nil +} diff --git a/notifications/pkg/config/service.go b/notifications/pkg/config/service.go new file mode 100644 index 000000000..f98aa3d27 --- /dev/null +++ b/notifications/pkg/config/service.go @@ -0,0 +1,6 @@ +package config + +// Service defines the available service configuration. +type Service struct { + Name string +} diff --git a/notifications/pkg/logging/logging.go b/notifications/pkg/logging/logging.go new file mode 100644 index 000000000..039b0451c --- /dev/null +++ b/notifications/pkg/logging/logging.go @@ -0,0 +1,17 @@ +package logging + +import ( + "github.com/owncloud/ocis/notifications/pkg/config" + "github.com/owncloud/ocis/ocis-pkg/log" +) + +// LoggerFromConfig initializes a service-specific logger instance. +func Configure(name string, cfg *config.Log) log.Logger { + return log.NewLogger( + log.Name(name), + log.Level(cfg.Level), + log.Pretty(cfg.Pretty), + log.Color(cfg.Color), + log.File(cfg.File), + ) +} diff --git a/notifications/pkg/service/service.go b/notifications/pkg/service/service.go new file mode 100644 index 000000000..07a46f083 --- /dev/null +++ b/notifications/pkg/service/service.go @@ -0,0 +1,46 @@ +package service + +import ( + "fmt" + "os" + "os/signal" + "syscall" + + "github.com/owncloud/ocis/ocis-pkg/log" +) + +type Service interface { + Run() error +} + +func NewEventsNotifier(events <-chan interface{}, logger log.Logger) Service { + return eventsNotifier{ + logger: logger, + events: events, + signals: make(chan os.Signal, 1), + } +} + +type eventsNotifier struct { + logger log.Logger + events <-chan interface{} + signals chan os.Signal +} + +func (s eventsNotifier) Run() error { + signal.Notify(s.signals, syscall.SIGINT, syscall.SIGTERM) + s.logger.Debug(). + Msg("eventsNotifier started") + for { + select { + case evt := <-s.events: + go func() { + fmt.Println(evt) + }() + case <-s.signals: + s.logger.Debug(). + Msg("eventsNotifier stopped") + return nil + } + } +} diff --git a/ocis-pkg/config/config.go b/ocis-pkg/config/config.go index 76aaeb35f..66707055a 100644 --- a/ocis-pkg/config/config.go +++ b/ocis-pkg/config/config.go @@ -9,6 +9,7 @@ import ( graph "github.com/owncloud/ocis/graph/pkg/config" idp "github.com/owncloud/ocis/idp/pkg/config" nats "github.com/owncloud/ocis/nats/pkg/config" + notifications "github.com/owncloud/ocis/notifications/pkg/config" ocs "github.com/owncloud/ocis/ocs/pkg/config" proxy "github.com/owncloud/ocis/proxy/pkg/config" settings "github.com/owncloud/ocis/settings/pkg/config" @@ -62,6 +63,7 @@ type Config struct { GraphExplorer *graphExplorer.Config `ocisConfig:"graph_explorer"` IDP *idp.Config `ocisConfig:"idp"` Nats *nats.Config `ocisConfig:"nats"` + Notifications *notifications.Config `ocisConfig:"notifications"` OCS *ocs.Config `ocisConfig:"ocs"` Web *web.Config `ocisConfig:"web"` Proxy *proxy.Config `ocisConfig:"proxy"` diff --git a/ocis-pkg/config/defaultconfig.go b/ocis-pkg/config/defaultconfig.go index 53853cbfa..60eea86a6 100644 --- a/ocis-pkg/config/defaultconfig.go +++ b/ocis-pkg/config/defaultconfig.go @@ -7,6 +7,7 @@ import ( graph "github.com/owncloud/ocis/graph/pkg/config" idp "github.com/owncloud/ocis/idp/pkg/config" nats "github.com/owncloud/ocis/nats/pkg/config" + notifications "github.com/owncloud/ocis/notifications/pkg/config" ocs "github.com/owncloud/ocis/ocs/pkg/config" proxy "github.com/owncloud/ocis/proxy/pkg/config" settings "github.com/owncloud/ocis/settings/pkg/config" @@ -31,6 +32,7 @@ func DefaultConfig() *Config { Graph: graph.DefaultConfig(), IDP: idp.DefaultConfig(), Nats: nats.DefaultConfig(), + Notifications: notifications.DefaultConfig(), Proxy: proxy.DefaultConfig(), GraphExplorer: graphExplorer.DefaultConfig(), OCS: ocs.DefaultConfig(), diff --git a/ocis/pkg/command/notifications.go b/ocis/pkg/command/notifications.go new file mode 100644 index 000000000..ec2eb3570 --- /dev/null +++ b/ocis/pkg/command/notifications.go @@ -0,0 +1,26 @@ +package command + +import ( + "github.com/owncloud/ocis/notifications/pkg/command" + "github.com/owncloud/ocis/ocis-pkg/config" + "github.com/owncloud/ocis/ocis-pkg/config/parser" + "github.com/owncloud/ocis/ocis/pkg/register" + "github.com/urfave/cli/v2" +) + +// NatsServerCommand is the entrypoint for the nats server command. +func NotificationsCommand(cfg *config.Config) *cli.Command { + return &cli.Command{ + Name: "notifications", + Usage: "start notifications service", + Category: "extensions", + Before: func(ctx *cli.Context) error { + return parser.ParseConfig(cfg) + }, + Subcommands: command.GetCommands(cfg.Notifications), + } +} + +func init() { + register.AddCommand(NotificationsCommand) +} diff --git a/ocis/pkg/runtime/service/service.go b/ocis/pkg/runtime/service/service.go index d160305ee..fa1a12908 100644 --- a/ocis/pkg/runtime/service/service.go +++ b/ocis/pkg/runtime/service/service.go @@ -24,6 +24,7 @@ import ( graph "github.com/owncloud/ocis/graph/pkg/command" idp "github.com/owncloud/ocis/idp/pkg/command" nats "github.com/owncloud/ocis/nats/pkg/command" + notifications "github.com/owncloud/ocis/notifications/pkg/command" "github.com/owncloud/ocis/ocis-pkg/config" ociscfg "github.com/owncloud/ocis/ocis-pkg/config" "github.com/owncloud/ocis/ocis-pkg/log" @@ -114,6 +115,7 @@ func NewService(options ...Option) (*Service, error) { s.ServicesRegistry["storage-shares"] = storage.NewStorageShares s.ServicesRegistry["storage-public-link"] = storage.NewStoragePublicLink s.ServicesRegistry["storage-appprovider"] = storage.NewAppProvider + s.ServicesRegistry["notifications"] = notifications.NewSutureService // populate delayed services s.Delayed["storage-sharing"] = storage.NewSharing