refactor nats

Signed-off-by: Christian Richter <crichter@owncloud.com>
This commit is contained in:
Christian Richter
2022-04-13 17:04:37 +02:00
parent bfc8db848c
commit bff47cc0f1
19 changed files with 17 additions and 17 deletions
+14
View File
@@ -0,0 +1,14 @@
package main
import (
"os"
"github.com/owncloud/ocis/extensions/nats/pkg/command"
"github.com/owncloud/ocis/extensions/nats/pkg/config/defaults"
)
func main() {
if err := command.Execute(defaults.DefaultConfig()); err != nil {
os.Exit(1)
}
}
+18
View File
@@ -0,0 +1,18 @@
package command
import (
"github.com/owncloud/ocis/extensions/nats/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
},
}
}
+64
View File
@@ -0,0 +1,64 @@
package command
import (
"context"
"os"
"github.com/owncloud/ocis/extensions/nats/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 nats command.
func Execute(cfg *config.Config) error {
app := clihelper.DefaultApp(&cli.App{
Name: "nats",
Usage: "starts nats server",
Commands: GetCommands(cfg),
})
cli.HelpFlag = &cli.BoolFlag{
Name: "help,h",
Usage: "Show the help",
}
return app.Run(os.Args)
}
// SutureService allows for the nats command to be embedded and supervised by a suture supervisor tree.
type SutureService struct {
cfg *config.Config
}
// NewSutureService creates a new nats.SutureService
func NewSutureService(cfg *ociscfg.Config) suture.Service {
cfg.Settings.Commons = cfg.Commons
return SutureService{
cfg: cfg.Nats,
}
}
func (s SutureService) Serve(ctx context.Context) error {
s.cfg.Context = ctx
if err := Execute(s.cfg); err != nil {
return err
}
return nil
}
+70
View File
@@ -0,0 +1,70 @@
package command
import (
"context"
"fmt"
"github.com/oklog/run"
"github.com/owncloud/ocis/extensions/nats/pkg/config"
"github.com/owncloud/ocis/extensions/nats/pkg/config/parser"
"github.com/owncloud/ocis/extensions/nats/pkg/logging"
"github.com/owncloud/ocis/extensions/nats/pkg/server/nats"
"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)
gr := run.Group{}
ctx, cancel := func() (context.Context, context.CancelFunc) {
if cfg.Context == nil {
return context.WithCancel(context.Background())
}
return context.WithCancel(cfg.Context)
}()
defer cancel()
natsServer, err := nats.NewNATSServer(
ctx,
logging.NewLogWrapper(logger),
nats.Host(cfg.Nats.Host),
nats.Port(cfg.Nats.Port),
nats.ClusterID(cfg.Nats.ClusterID),
nats.StoreDir(cfg.Nats.StoreDir),
)
if err != nil {
return err
}
gr.Add(func() error {
err := make(chan error)
select {
case <-ctx.Done():
return nil
case err <- natsServer.ListenAndServe():
return <-err
}
}, func(_ error) {
logger.Info().
Msg("Shutting down server")
natsServer.Shutdown()
cancel()
})
return gr.Run()
},
}
}
+19
View File
@@ -0,0 +1,19 @@
package command
import (
"github.com/owncloud/ocis/extensions/nats/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
},
}
}
+29
View File
@@ -0,0 +1,29 @@
package config
import (
"context"
"github.com/owncloud/ocis/ocis-pkg/shared"
)
// Config combines all available configuration parts.
type Config struct {
*shared.Commons `yaml:"-"`
Service Service `yaml:"-"`
Log *Log `yaml:"log"`
Debug Debug `yaml:"debug"`
Nats Nats `ociConfig:"nats"`
Context context.Context `yaml:"-"`
}
// Nats is the nats config
type Nats struct {
Host string `yaml:"host" env:"NATS_NATS_HOST"`
Port int `yaml:"port" env:"NATS_NATS_PORT"`
ClusterID string `yaml:"clusterid" env:"NATS_NATS_CLUSTER_ID"`
StoreDir string `yaml:"store_dir" env:"NATS_NATS_STORE_DIR"`
}
+9
View File
@@ -0,0 +1,9 @@
package config
// Debug defines the available debug configuration.
type Debug struct {
Addr string `yaml:"addr" env:"NATS_DEBUG_ADDR"`
Token string `yaml:"token" env:"NATS_DEBUG_TOKEN"`
Pprof bool `yaml:"pprof" env:"NATS_DEBUG_PPROF"`
Zpages bool `yaml:"zpages" env:"NATS_DEBUG_ZPAGES"`
}
@@ -0,0 +1,52 @@
package defaults
import (
"path"
"github.com/owncloud/ocis/extensions/nats/pkg/config"
"github.com/owncloud/ocis/ocis-pkg/config/defaults"
)
// NOTE: Most of this configuration is not needed to keep it as simple as possible
// TODO: Clean up unneeded configuration
func FullDefaultConfig() *config.Config {
cfg := DefaultConfig()
EnsureDefaults(cfg)
Sanitize(cfg)
return cfg
}
func DefaultConfig() *config.Config {
return &config.Config{
Service: config.Service{
Name: "nats",
},
Nats: config.Nats{
Host: "127.0.0.1",
Port: 9233,
ClusterID: "ocis-cluster",
StoreDir: path.Join(defaults.BaseDataPath(), "nats"),
},
}
}
func EnsureDefaults(cfg *config.Config) {
// 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{}
}
}
func Sanitize(cfg *config.Config) {
// nothing to sanitize here atm
}
+9
View File
@@ -0,0 +1,9 @@
package config
// Log defines the available log configuration.
type Log struct {
Level string `mapstructure:"level" env:"OCIS_LOG_LEVEL;NATS_LOG_LEVEL"`
Pretty bool `mapstructure:"pretty" env:"OCIS_LOG_PRETTY;NATS_LOG_PRETTY"`
Color bool `mapstructure:"color" env:"OCIS_LOG_COLOR;NATS_LOG_COLOR"`
File string `mapstructure:"file" env:"OCIS_LOG_FILE;NATS_LOG_FILE"`
}
@@ -0,0 +1,33 @@
package parser
import (
"errors"
"github.com/owncloud/ocis/extensions/nats/pkg/config"
"github.com/owncloud/ocis/extensions/nats/pkg/config/defaults"
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
}
defaults.EnsureDefaults(cfg)
// 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
}
}
defaults.Sanitize(cfg)
return nil
}
+6
View File
@@ -0,0 +1,6 @@
package config
// Service defines the available service configuration.
type Service struct {
Name string `yaml:"-"`
}
+17
View File
@@ -0,0 +1,17 @@
package logging
import (
"github.com/owncloud/ocis/extensions/nats/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),
)
}
+52
View File
@@ -0,0 +1,52 @@
package logging
import (
"fmt"
"github.com/owncloud/ocis/ocis-pkg/log"
)
func NewLogWrapper(logger log.Logger) *LogWrapper {
return &LogWrapper{logger}
}
// we need to wrap our logger so we can pass it to the nats server
type LogWrapper struct {
logger log.Logger
}
// Noticef logs a notice statement
func (l *LogWrapper) Noticef(format string, v ...interface{}) {
msg := fmt.Sprintf(format, v...)
l.logger.Info().Msg(msg)
}
// Warnf logs a warning statement
func (l *LogWrapper) Warnf(format string, v ...interface{}) {
msg := fmt.Sprintf(format, v...)
l.logger.Warn().Msg(msg)
}
// Fatalf logs a fatal statement
func (l *LogWrapper) Fatalf(format string, v ...interface{}) {
msg := fmt.Sprintf(format, v...)
l.logger.Fatal().Msg(msg)
}
// Errorf logs an error statement
func (l *LogWrapper) Errorf(format string, v ...interface{}) {
msg := fmt.Sprintf(format, v...)
l.logger.Error().Msg(msg)
}
// Debugf logs a debug statement
func (l *LogWrapper) Debugf(format string, v ...interface{}) {
msg := fmt.Sprintf(format, v...)
l.logger.Debug().Msg(msg)
}
// Tracef logs a trace statement
func (l *LogWrapper) Tracef(format string, v ...interface{}) {
msg := fmt.Sprintf(format, v...)
l.logger.Trace().Msg(msg)
}
+49
View File
@@ -0,0 +1,49 @@
package nats
import (
"context"
"time"
nserver "github.com/nats-io/nats-server/v2/server"
)
var NATSListenAndServeLoopTimer = 1 * time.Second
type NATSServer struct {
ctx context.Context
server *nserver.Server
}
func NewNATSServer(ctx context.Context, logger nserver.Logger, opts ...NatsOption) (*NATSServer, error) {
natsOpts := &nserver.Options{}
for _, o := range opts {
o(natsOpts)
}
// enable JetStream
natsOpts.JetStream = true
server, err := nserver.NewServer(natsOpts)
if err != nil {
return nil, err
}
server.SetLoggerV2(logger, true, true, false)
return &NATSServer{
ctx: ctx,
server: server,
}, nil
}
// ListenAndServe runs the NATSServer in a blocking way until the server is shutdown or an error occurs
func (n *NATSServer) ListenAndServe() (err error) {
go n.server.Start()
<-n.ctx.Done()
return nil
}
func (n *NATSServer) Shutdown() {
n.server.Shutdown()
}
@@ -0,0 +1,36 @@
package nats
import (
nserver "github.com/nats-io/nats-server/v2/server"
)
// NatsOption configures the nats server
type NatsOption func(*nserver.Options)
// Host sets the host URL for the nats server
func Host(url string) NatsOption {
return func(o *nserver.Options) {
o.Host = url
}
}
// Port sets the host URL for the nats server
func Port(port int) NatsOption {
return func(o *nserver.Options) {
o.Port = port
}
}
// ClusterID sets the name for the nats cluster
func ClusterID(clusterID string) NatsOption {
return func(o *nserver.Options) {
o.Cluster.Name = clusterID
}
}
// StoreDir sets the folder for persistence
func StoreDir(StoreDir string) NatsOption {
return func(o *nserver.Options) {
o.StoreDir = StoreDir
}
}