make service start

Signed-off-by: jkoberg <jkoberg@owncloud.com>
This commit is contained in:
jkoberg
2022-03-08 14:44:39 +01:00
parent f0671015f8
commit 1609bb3a7c
19 changed files with 126 additions and 387 deletions
-105
View File
@@ -1,105 +0,0 @@
// Package channels provides different communication channels to notify users.
package channels
import (
"context"
"net/smtp"
gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
groups "github.com/cs3org/go-cs3apis/cs3/identity/group/v1beta1"
rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
"github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool"
"github.com/owncloud/ocis/notifications/pkg/config"
"github.com/owncloud/ocis/ocis-pkg/log"
"github.com/pkg/errors"
)
// Channel defines the methods of a communication channel.
type Channel interface {
// SendMessage sends a message to users.
SendMessage(userIDs []string, msg string) error
// SendMessageToGroup sends a message to a group.
SendMessageToGroup(groupdID *groups.GroupId, msg string) error
}
// NewMailChannel instantiates a new mail communication channel.
func NewMailChannel(cfg config.Config, logger log.Logger) (Channel, error) {
gc, err := pool.GetGatewayServiceClient(cfg.Notifications.RevaGateway)
if err != nil {
logger.Error().Err(err).Msg("could not get gateway client")
return nil, err
}
return Mail{
gatewayClient: gc,
conf: cfg,
logger: logger,
}, nil
}
// Mail is the communcation channel for email.
type Mail struct {
gatewayClient gateway.GatewayAPIClient
conf config.Config
logger log.Logger
}
// SendMessage sends a message to all given users.
func (m Mail) SendMessage(userIDs []string, msg string) error {
to, err := m.getReceiverAddresses(userIDs)
if err != nil {
return err
}
body := []byte(msg)
smtpConf := m.conf.Notifications.SMTP
auth := smtp.PlainAuth("", smtpConf.Sender, smtpConf.Password, smtpConf.Host)
if err := smtp.SendMail(smtpConf.Host+":"+smtpConf.Port, auth, smtpConf.Sender, to, body); err != nil {
return errors.Wrap(err, "could not send mail")
}
return nil
}
// SendMessageToGroup sends a message to all members of the given group.
func (m Mail) SendMessageToGroup(groupID *groups.GroupId, msg string) error {
// TODO We need an authenticated context here...
res, err := m.gatewayClient.GetGroup(context.Background(), &groups.GetGroupRequest{GroupId: groupID})
if err != nil {
return err
}
if res.Status.Code != rpc.Code_CODE_OK {
return errors.New("could not get group")
}
members := make([]string, 0, len(res.Group.Members))
for _, id := range res.Group.Members {
members = append(members, id.OpaqueId)
}
return m.SendMessage(members, msg)
}
func (m Mail) getReceiverAddresses(receivers []string) ([]string, error) {
addresses := make([]string, 0, len(receivers))
for _, id := range receivers {
// Authenticate is too costly but at the moment our only option to get the user.
// We don't have an authenticated context so calling `GetUser` doesn't work.
res, err := m.gatewayClient.Authenticate(context.Background(), &gateway.AuthenticateRequest{
Type: "machine",
ClientId: "userid:" + id,
ClientSecret: m.conf.Notifications.MachineAuthSecret,
})
if err != nil {
return nil, err
}
if res.Status.Code != rpc.Code_CODE_OK {
m.logger.Error().
Interface("status", res.Status).
Str("receiver_id", id).
Msg("could not get user")
continue
}
addresses = append(addresses, res.User.Mail)
}
return addresses, nil
}
+1 -1
View File
@@ -1,7 +1,7 @@
package command
import (
"github.com/owncloud/ocis/notifications/pkg/config"
"github.com/owncloud/ocis/audit/pkg/config"
"github.com/urfave/cli/v2"
)
+7 -7
View File
@@ -4,7 +4,7 @@ import (
"context"
"os"
"github.com/owncloud/ocis/notifications/pkg/config"
"github.com/owncloud/ocis/audit/pkg/config"
"github.com/owncloud/ocis/ocis-pkg/clihelper"
ociscfg "github.com/owncloud/ocis/ocis-pkg/config"
"github.com/thejerf/suture/v4"
@@ -25,11 +25,11 @@ func GetCommands(cfg *config.Config) cli.Commands {
}
}
// Execute is the entry point for the notifications command.
// Execute is the entry point for the audit command.
func Execute(cfg *config.Config) error {
app := clihelper.DefaultApp(&cli.App{
Name: "notifications",
Usage: "starts notifications service",
Name: "audit",
Usage: "starts audit service",
Commands: GetCommands(cfg),
})
@@ -41,16 +41,16 @@ func Execute(cfg *config.Config) error {
return app.Run(os.Args)
}
// SutureService allows for the notifications command to be embedded and supervised by a suture supervisor tree.
// SutureService allows for the audit command to be embedded and supervised by a suture supervisor tree.
type SutureService struct {
cfg *config.Config
}
// NewSutureService creates a new notifications.SutureService
// NewSutureService creates a new audit.SutureService
func NewSutureService(cfg *ociscfg.Config) suture.Service {
cfg.Settings.Commons = cfg.Commons
return SutureService{
cfg: cfg.Notifications,
cfg: cfg.Audit,
}
}
+8 -12
View File
@@ -6,11 +6,10 @@ import (
"github.com/asim/go-micro/plugins/events/nats/v4"
"github.com/cs3org/reva/v2/pkg/events"
"github.com/cs3org/reva/v2/pkg/events/server"
"github.com/owncloud/ocis/notifications/pkg/channels"
"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/owncloud/ocis/audit/pkg/config"
"github.com/owncloud/ocis/audit/pkg/config/parser"
"github.com/owncloud/ocis/audit/pkg/logging"
svc "github.com/owncloud/ocis/audit/pkg/service"
"github.com/urfave/cli/v2"
)
@@ -30,7 +29,7 @@ func Server(cfg *config.Config) *cli.Command {
events.ShareCreated{},
}
evtsCfg := cfg.Notifications.Events
evtsCfg := cfg.Events
client, err := server.NewNatsStream(nats.Address(evtsCfg.Endpoint), nats.ClusterID(evtsCfg.Cluster))
if err != nil {
return err
@@ -39,12 +38,9 @@ func Server(cfg *config.Config) *cli.Command {
if err != nil {
return err
}
channel, err := channels.NewMailChannel(*cfg, logger)
if err != nil {
return err
}
svc := service.NewEventsNotifier(evts, channel, logger)
return svc.Run()
svc.StartAuditLogger(cfg.Auditlog, evts, logger)
return nil
},
}
}
+1 -1
View File
@@ -1,7 +1,7 @@
package command
import (
"github.com/owncloud/ocis/notifications/pkg/config"
"github.com/owncloud/ocis/audit/pkg/config"
"github.com/urfave/cli/v2"
)
+13 -20
View File
@@ -15,30 +15,23 @@ type Config struct {
Log *Log `ocisConfig:"log"`
Debug Debug `ocisConfig:"debug"`
Notifications Notifications `ocisConfig:"notifications"`
Events Events `ocisConfig:"events"`
Auditlog Auditlog `ocisConfig:"auditlog"`
Context context.Context
}
// Notifications definces the config options for the notifications service.
type Notifications struct {
SMTP SMTP `ocisConfig:"SMTP"`
Events Events `ocisConfig:"events"`
RevaGateway string `ocisConfig:"reva_gateway" env:"REVA_GATEWAY;NOTIFICATIONS_REVA_GATEWAY"`
MachineAuthSecret string `ocisConfig:"machine_auth_api_key" env:"OCIS_MACHINE_AUTH_API_KEY;NOTIFICATIONS_MACHINE_AUTH_API_KEY"`
}
// SMTP combines the smtp configuration options.
type SMTP struct {
Host string `ocisConfig:"smtp_host" env:"NOTIFICATIONS_SMTP_HOST"`
Port string `ocisConfig:"smtp_port" env:"NOTIFICATIONS_SMTP_PORT"`
Sender string `ocisConfig:"smtp_sender" env:"NOTIFICATIONS_SMTP_SENDER"`
Password string `ocisConfig:"smtp_password" env:"NOTIFICATIONS_SMTP_PASSWORD"`
}
// Events combines the configuration options for the event bus.
type Events struct {
Endpoint string `ocisConfig:"events_endpoint" env:"NOTIFICATIONS_EVENTS_ENDPOINT"`
Cluster string `ocisConfig:"events_cluster" env:"NOTIFICATIONS_EVENTS_CLUSTER"`
ConsumerGroup string `ocisConfig:"events_group" env:"NOTIFICATIONS_EVENTS_GROUP"`
Endpoint string `ocisConfig:"events_endpoint" env:"AUDIT_EVENTS_ENDPOINT"`
Cluster string `ocisConfig:"events_cluster" env:"AUDIT_EVENTS_CLUSTER"`
ConsumerGroup string `ocisConfig:"events_group" env:"AUDIT_EVENTS_GROUP"`
}
// Auditlog holds audit log information
type Auditlog struct {
LogToConsole bool `ocisConfig:"log_to_console" env:"AUDIT_LOG_TO_CONSOLE"`
LogToFile bool `ocisConfig:"log_to_file" env:"AUDIT_LOG_TO_FILE"`
FilePath string `ocisConfig:"filepath" env:"AUDIT_FILEPATH"`
Format string `ocisConfig:"format" env:"AUDIT_FORMAT"`
}
+9 -18
View File
@@ -1,27 +1,18 @@
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",
Name: "audit",
},
Notifications: Notifications{
SMTP: SMTP{
Host: "127.0.0.1",
Port: "1025",
Sender: "god@example.com",
Password: "godisdead",
},
Events: Events{
Endpoint: "127.0.0.1:9233",
Cluster: "test-cluster",
ConsumerGroup: "notifications",
},
RevaGateway: "127.0.0.1:9142",
MachineAuthSecret: "change-me-please",
Events: Events{
Endpoint: "127.0.0.1:9233",
Cluster: "test-cluster",
ConsumerGroup: "audit",
},
Auditlog: Auditlog{
LogToConsole: true,
Format: "json",
},
}
}
+1 -1
View File
@@ -3,7 +3,7 @@ package parser
import (
"errors"
"github.com/owncloud/ocis/notifications/pkg/config"
"github.com/owncloud/ocis/audit/pkg/config"
ociscfg "github.com/owncloud/ocis/ocis-pkg/config"
"github.com/owncloud/ocis/ocis-pkg/config/envdecode"
+1 -1
View File
@@ -1,7 +1,7 @@
package logging
import (
"github.com/owncloud/ocis/notifications/pkg/config"
"github.com/owncloud/ocis/audit/pkg/config"
"github.com/owncloud/ocis/ocis-pkg/log"
)
-79
View File
@@ -1,79 +0,0 @@
package svc
import (
"encoding/json"
"fmt"
"os"
"github.com/asim/go-micro/plugins/events/nats/v4"
"github.com/cs3org/reva/v2/pkg/events"
"github.com/cs3org/reva/v2/pkg/events/server"
"github.com/owncloud/ocis/audit/pkg/config"
"github.com/owncloud/ocis/audit/pkg/types"
"github.com/owncloud/ocis/ocis-pkg/log"
)
func startConsumer(c config.Eventstream, log log.Logger) (<-chan interface{}, error) {
s, err := server.NewNatsStream(nats.Address(c.Address), nats.ClusterID(c.ClusterID))
if err != nil {
return nil, err
}
return events.Consume(s, "audit", events.ShareCreated{})
}
func startAuditLogger(c config.Auditlog, ch <-chan interface{}, log log.Logger) {
for {
i := <-ch
var auditEvent interface{}
switch ev := i.(type) {
case events.ShareCreated:
auditEvent = types.ShareCreated(ev)
default:
log.Error().Interface("event", ev).Msg(fmt.Sprintf("can't handle event of type '%T'", ev))
continue
}
b, err := marshal(auditEvent, c.Format)
if err != nil {
log.Error().Err(err).Msg("error marshaling the event")
continue
}
if c.LogToConsole {
log.Info().Msg(string(b))
}
if c.LogToFile {
err := writeToFile(c.FilePath, b)
if err != nil {
log.Error().Err(err).Msg("error writing audit log file")
}
}
}
}
func writeToFile(path string, ev []byte) error {
file, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
if err != nil {
return err
}
defer file.Close()
if _, err := fmt.Fprintln(file, string(ev)); err != nil {
return err
}
return nil
}
func marshal(ev interface{}, format string) ([]byte, error) {
switch format {
default:
return nil, fmt.Errorf("unsupported format '%s'", format)
case "json":
return json.Marshal(ev)
}
}
-21
View File
@@ -1,21 +0,0 @@
package svc
/*
// NewInstrument returns a service that instruments metrics.
func NewInstrument(next Service, metrics *metrics.Metrics) Service {
return instrument{
next: next,
metrics: metrics,
}
}
type instrument struct {
next Service
metrics *metrics.Metrics
}
// ListenForEvents implements service interface
func (i instrument) ListenForEvents() {
i.next.ListenForEvents()
}
*/
-23
View File
@@ -1,23 +0,0 @@
package svc
import (
"github.com/owncloud/ocis/ocis-pkg/log"
)
// NewLogging returns a service that logs messages.
func NewLogging(next Service, logger log.Logger) Service {
return logging{
next: next,
logger: logger,
}
}
type logging struct {
next Service
logger log.Logger
}
// ListenForEvents implements service interface
func (l logging) ListenForEvents() {
l.next.ListenForEvents()
}
-50
View File
@@ -1,50 +0,0 @@
package svc
import (
"net/http"
"github.com/owncloud/ocis/audit/pkg/config"
"github.com/owncloud/ocis/ocis-pkg/log"
)
// 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
Config *config.Config
Middleware []func(http.Handler) http.Handler
}
// 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
}
}
// Config provides a function to set the config option.
func Config(val *config.Config) Option {
return func(o *Options) {
o.Config = val
}
}
// Middleware provides a function to set the middleware option.
func Middleware(val ...func(http.Handler) http.Handler) Option {
return func(o *Options) {
o.Middleware = val
}
}
+52 -31
View File
@@ -1,48 +1,69 @@
package svc
import (
"github.com/go-chi/chi"
"encoding/json"
"fmt"
"os"
"github.com/cs3org/reva/v2/pkg/events"
"github.com/owncloud/ocis/audit/pkg/config"
"github.com/owncloud/ocis/audit/pkg/types"
"github.com/owncloud/ocis/ocis-pkg/log"
)
// Service defines the extension handlers.
type Service interface {
ListenForEvents()
}
// StartAuditLogger starts the audit logger
func StartAuditLogger(c config.Auditlog, ch <-chan interface{}, log log.Logger) {
for {
i := <-ch
// NewService returns a service implementation for Service.
func NewService(opts ...Option) Service {
options := newOptions(opts...)
var auditEvent interface{}
switch ev := i.(type) {
case events.ShareCreated:
auditEvent = types.ShareCreated(ev)
default:
log.Error().Interface("event", ev).Msg(fmt.Sprintf("can't handle event of type '%T'", ev))
continue
m := chi.NewMux()
m.Use(options.Middleware...)
}
b, err := marshal(auditEvent, c.Format)
if err != nil {
log.Error().Err(err).Msg("error marshaling the event")
continue
}
if c.LogToConsole {
log.Error().Msg(string(b))
}
if c.LogToFile {
err := writeToFile(c.FilePath, b)
if err != nil {
log.Error().Err(err).Msg("error writing audit log file")
}
}
svc := Audit{
logger: options.Logger,
config: options.Config,
mux: m,
}
go svc.ListenForEvents()
return svc
}
// Audit defines implements the business logic for Service.
type Audit struct {
logger log.Logger
config *config.Config
mux *chi.Mux
}
// ListenForEvents hooks into event queue and logs interesting events
func (g Audit) ListenForEvents() {
log := g.logger
ch, err := startConsumer(g.config.Eventstream, log)
func writeToFile(path string, ev []byte) error {
file, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
if err != nil {
log.Fatal().Err(err).Msg("can't listen for events")
return
return err
}
defer file.Close()
if _, err := fmt.Fprintln(file, string(ev)); err != nil {
return err
}
return nil
}
func marshal(ev interface{}, format string) ([]byte, error) {
switch format {
default:
return nil, fmt.Errorf("unsupported format '%s'", format)
case "json":
return json.Marshal(ev)
}
startAuditLogger(g.config.Auditlog, ch, log)
}
-17
View File
@@ -1,17 +0,0 @@
package svc
// NewTracing returns a service that instruments traces.
func NewTracing(next Service) Service {
return tracing{
next: next,
}
}
type tracing struct {
next Service
}
// ListenForEvents implements service interface
func (t tracing) ListenForEvents() {
t.next.ListenForEvents()
}
+2
View File
@@ -4,6 +4,7 @@ import (
"github.com/owncloud/ocis/ocis-pkg/shared"
accounts "github.com/owncloud/ocis/accounts/pkg/config"
audit "github.com/owncloud/ocis/audit/pkg/config"
glauth "github.com/owncloud/ocis/glauth/pkg/config"
graphExplorer "github.com/owncloud/ocis/graph-explorer/pkg/config"
graph "github.com/owncloud/ocis/graph/pkg/config"
@@ -58,6 +59,7 @@ type Config struct {
TokenManager TokenManager `ocisConfig:"token_manager"`
Runtime Runtime `ocisConfig:"runtime"`
Audit *audit.Config `ocsiConfig:"audit"`
Accounts *accounts.Config `ocisConfig:"accounts"`
GLAuth *glauth.Config `ocisConfig:"glauth"`
Graph *graph.Config `ocisConfig:"graph"`
+2
View File
@@ -2,6 +2,7 @@ package config
import (
accounts "github.com/owncloud/ocis/accounts/pkg/config"
audit "github.com/owncloud/ocis/audit/pkg/config"
glauth "github.com/owncloud/ocis/glauth/pkg/config"
graphExplorer "github.com/owncloud/ocis/graph-explorer/pkg/config"
graph "github.com/owncloud/ocis/graph/pkg/config"
@@ -28,6 +29,7 @@ func DefaultConfig() *Config {
Port: "9250",
Host: "localhost",
},
Audit: audit.DefaultConfig(),
Accounts: accounts.DefaultConfig(),
GLAuth: glauth.DefaultConfig(),
Graph: graph.DefaultConfig(),
+26
View File
@@ -0,0 +1,26 @@
package command
import (
"github.com/owncloud/ocis/audit/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"
)
// AuditCommand is the entrypoint for the audit command.
func AuditCommand(cfg *config.Config) *cli.Command {
return &cli.Command{
Name: "audit",
Usage: "start audit service",
Category: "extensions",
Before: func(ctx *cli.Context) error {
return parser.ParseConfig(cfg)
},
Subcommands: command.GetCommands(cfg.Audit),
}
}
func init() {
register.AddCommand(AuditCommand)
}
+3
View File
@@ -18,7 +18,9 @@ import (
mzlog "github.com/asim/go-micro/plugins/logger/zerolog/v4"
"github.com/mohae/deepcopy"
"github.com/olekukonko/tablewriter"
accounts "github.com/owncloud/ocis/accounts/pkg/command"
audit "github.com/owncloud/ocis/audit/pkg/command"
glauth "github.com/owncloud/ocis/glauth/pkg/command"
graphExplorer "github.com/owncloud/ocis/graph-explorer/pkg/command"
graph "github.com/owncloud/ocis/graph/pkg/command"
@@ -93,6 +95,7 @@ func NewService(options ...Option) (*Service, error) {
cfg: opts.Config,
}
s.ServicesRegistry["audit"] = audit.NewSutureService
s.ServicesRegistry["settings"] = settings.NewSutureService
s.ServicesRegistry["nats"] = nats.NewSutureService
s.ServicesRegistry["storage-metadata"] = storage.NewStorageMetadata