github.com/lastbackend/toolkit@v0.0.0-20241020043710-cafa37b95aad/pkg/runtime/controller/controller.go (about)

     1  package controller
     2  
     3  import (
     4  	"context"
     5  	"os"
     6  	"os/signal"
     7  	"syscall"
     8  
     9  	"github.com/common-nighthawk/go-figure"
    10  	"github.com/lastbackend/toolkit"
    11  	"github.com/lastbackend/toolkit/pkg/runtime"
    12  	"github.com/lastbackend/toolkit/pkg/runtime/logger"
    13  	zp "github.com/lastbackend/toolkit/pkg/runtime/logger/zap"
    14  	"github.com/lastbackend/toolkit/pkg/runtime/meta"
    15  	"go.uber.org/fx"
    16  )
    17  
    18  var shutdownSignals = []os.Signal{
    19  	syscall.SIGTERM,
    20  	syscall.SIGINT,
    21  	syscall.SIGQUIT,
    22  	syscall.SIGKILL,
    23  }
    24  
    25  type controller struct {
    26  	fx.Out
    27  	runtime.Runtime
    28  
    29  	app  *fx.App
    30  	meta *meta.Meta
    31  
    32  	service toolkit.Service
    33  
    34  	logger logger.Logger
    35  
    36  	client runtime.Client
    37  	config runtime.Config
    38  	server runtime.Server
    39  	plugin runtime.Plugin
    40  	pkg    runtime.Package
    41  
    42  	providers []interface{}
    43  	invokes   []interface{}
    44  
    45  	onStartHook []func(ctx context.Context) error
    46  	onStopHook  []func(ctx context.Context) error
    47  
    48  	onStartSyncHook []func(ctx context.Context) error
    49  	onStopSyncHook  []func(ctx context.Context) error
    50  
    51  	tools runtime.Tools
    52  	done  chan error
    53  }
    54  
    55  func (c *controller) Service() toolkit.Service {
    56  	return c.service
    57  }
    58  
    59  func (c *controller) Meta() *meta.Meta {
    60  	return c.meta
    61  }
    62  
    63  func (c *controller) Log() logger.Logger {
    64  	return c.logger
    65  }
    66  
    67  func (c *controller) Start(ctx context.Context) error {
    68  	if c.help() {
    69  		return nil
    70  	}
    71  
    72  	return c.start(ctx)
    73  }
    74  
    75  func (c *controller) start(ctx context.Context) error {
    76  
    77  	banner := figure.NewFigure(c.meta.GetName(), "", true)
    78  	banner.Print()
    79  
    80  	c.Log().V(5).Info("runtime.controller.start")
    81  
    82  	opts := make([]fx.Option, 0)
    83  	opts = append(opts, fx.Provide(
    84  		fx.Annotate(
    85  			func() runtime.Runtime {
    86  				return c
    87  			},
    88  		)),
    89  		fx.Provide(
    90  			fx.Annotate(
    91  				func() toolkit.Service {
    92  					return c.Service()
    93  				},
    94  			)),
    95  		fx.Provide(
    96  			fx.Annotate(
    97  				func() logger.Logger {
    98  					return c.logger
    99  				},
   100  			)),
   101  		fx.Provide(
   102  			fx.Annotate(
   103  				func() toolkit.Client {
   104  					return c.service.Client()
   105  				},
   106  			)),
   107  		fx.Provide(
   108  			fx.Annotate(
   109  				func() toolkit.Server {
   110  					return c.service.Server()
   111  				},
   112  			)),
   113  		fx.Provide(func() context.Context { return ctx }))
   114  
   115  	cfgs := c.Config().Configs()
   116  
   117  	c.Log().V(5).Info("runtime.controller: configs supply")
   118  	for _, c := range cfgs {
   119  		opts = append(opts, fx.Supply(c))
   120  	}
   121  
   122  	c.Log().V(5).Info("runtime.controller: user custom provide")
   123  	for _, p := range c.providers {
   124  		opts = append(opts, fx.Provide(p))
   125  	}
   126  
   127  	// Provide plugins
   128  	c.Log().V(5).Info("runtime.controller: plugins provide start")
   129  
   130  	plugins := c.Plugin().Constructors()
   131  	for _, p := range plugins {
   132  		opts = append(opts, fx.Provide(
   133  			fx.Annotate(
   134  				build(p, new(toolkit.Plugin)),
   135  				fx.ResultTags(``, `group:"plugins"`))))
   136  	}
   137  
   138  	c.Log().V(5).Info("runtime.controller: plugins provide end")
   139  
   140  	// Provide packages
   141  	c.Log().V(5).Info("runtime.controller: packages provide start")
   142  	packages := c.Package().Constructors()
   143  	for index, p := range packages {
   144  		opts = append(opts, fx.Provide(
   145  			fx.Annotate(buildPackage(p, &toolkit.PackageItem{Index: index}),
   146  				fx.ResultTags(``, `group:"packages"`))))
   147  	}
   148  	c.Log().V(5).Info("runtime.controller: packages provide end")
   149  
   150  	// Provide servers
   151  	c.Log().V(5).Info("runtime.controller: servers provide start")
   152  	servers := c.Server().Provides()
   153  	for _, s := range servers {
   154  		opts = append(opts, fx.Provide(s))
   155  	}
   156  	c.Log().V(5).Info("runtime.controller: servers provide end")
   157  
   158  	// Start invocations
   159  	// Invoke plugin PreStart
   160  	c.Log().V(5).Info("runtime.controller: plugins invoke registration")
   161  	opts = append(opts, fx.Invoke(fx.Annotate(
   162  		c.Plugin().Register,
   163  		fx.ParamTags(`group:"plugins"`))))
   164  
   165  	c.Log().V(5).Info("runtime.controller: plugins invoke PreStart")
   166  	opts = append(opts, fx.Invoke(c.Plugin().PreStart))
   167  
   168  	// Invoke packages PreStart
   169  	c.Log().V(5).Info("runtime.controller: package invoke registration")
   170  	opts = append(opts, fx.Invoke(fx.Annotate(
   171  		c.Package().Register,
   172  		fx.ParamTags(`group:"packages"`))))
   173  
   174  	c.Log().V(5).Info("runtime.controller: package invoke PreStart")
   175  	opts = append(opts, fx.Invoke(c.Package().PreStart))
   176  
   177  	c.Log().V(5).Info("runtime.controller: user custom invoke")
   178  	for _, p := range c.invokes {
   179  		opts = append(opts, fx.Invoke(p))
   180  	}
   181  
   182  	// get constructors from servers
   183  	c.Log().V(5).Info("runtime.controller: servers constructors invoke")
   184  	constructors := c.Server().Constructors()
   185  	for _, c := range constructors {
   186  		opts = append(opts, fx.Invoke(c))
   187  	}
   188  
   189  	opts = append(opts, fx.Invoke(func(lc fx.Lifecycle) error {
   190  
   191  		lc.Append(fx.Hook{
   192  			OnStart: func(ctx context.Context) error {
   193  				if err := c.onStart(ctx); err != nil {
   194  					return err
   195  				}
   196  				return nil
   197  			},
   198  			OnStop: func(ctx context.Context) error {
   199  				if err := c.onStop(ctx); err != nil {
   200  					return err
   201  				}
   202  				return nil
   203  			},
   204  		})
   205  		return nil
   206  	}))
   207  
   208  	c.app = fx.New(
   209  		fx.Options(opts...),
   210  		fx.WithLogger(c.logger.Fx),
   211  	)
   212  
   213  	defer func(app *fx.App, ctx context.Context) {
   214  		err := app.Stop(ctx)
   215  		if err != nil {
   216  			c.Log().V(5).Errorf("stop runtime.controller failed: %v", err)
   217  		}
   218  	}(c.app, context.Background())
   219  
   220  	if err := c.app.Start(ctx); err != nil {
   221  		c.Log().V(5).Errorf("start runtime.controller failed:%v", err)
   222  		return err
   223  	}
   224  
   225  	c.Log().V(5).Info("runtime.controller.started")
   226  
   227  	sign := make(chan os.Signal)
   228  	signal.Notify(sign, shutdownSignals...)
   229  	select {
   230  	case <-sign:
   231  	case err := <-c.done:
   232  		if err != nil {
   233  			c.Log().Errorf("runtime.controller: stop with err: %v", err)
   234  			return err
   235  		}
   236  	}
   237  
   238  	c.Log().V(5).Info("runtime.controller.stopped")
   239  	return nil
   240  }
   241  
   242  func (c *controller) onStart(ctx context.Context) error {
   243  
   244  	c.Log().V(5).Info("runtime.controller.onStart: start")
   245  
   246  	c.Log().V(5).Info("runtime.controller.onStart: tools OnStart")
   247  	if err := c.Tools().OnStart(ctx); err != nil {
   248  		return err
   249  	}
   250  
   251  	c.Log().V(5).Info("runtime.controller.onStart: server start")
   252  	if err := c.Server().Start(ctx); err != nil {
   253  		return err
   254  	}
   255  
   256  	c.Log().V(5).Info("runtime.controller.onStart: resolver OnStart call")
   257  	if err := c.client.GRPC().GetResolver().OnStart(ctx); err != nil {
   258  		return err
   259  	}
   260  
   261  	c.Log().V(5).Info("runtime.controller.onStart: plugin OnStart call")
   262  	if err := c.Plugin().OnStart(ctx); err != nil {
   263  		return err
   264  	}
   265  
   266  	c.Log().V(5).Info("runtime.controller.onStart: package OnStart call")
   267  	if err := c.Package().OnStart(ctx); err != nil {
   268  		return err
   269  	}
   270  
   271  	for _, fn := range c.onStartHook {
   272  		fn := fn
   273  		go func() {
   274  			if err := fn(ctx); err != nil {
   275  				c.Log().Error(err)
   276  			}
   277  		}()
   278  	}
   279  
   280  	for _, fn := range c.onStartSyncHook {
   281  		if err := fn(ctx); err != nil {
   282  			return err
   283  		}
   284  	}
   285  
   286  	c.Log().V(5).Info("runtime.controller: started")
   287  	return nil
   288  }
   289  
   290  func (c *controller) onStop(ctx context.Context) error {
   291  
   292  	if err := c.Plugin().OnStop(ctx); err != nil {
   293  		return err
   294  	}
   295  	if err := c.Package().OnStop(ctx); err != nil {
   296  		return err
   297  	}
   298  	if err := c.Server().Stop(ctx); err != nil {
   299  		return err
   300  	}
   301  
   302  	for _, fn := range c.onStopHook {
   303  		fn := fn
   304  		go func() {
   305  			if err := fn(ctx); err != nil {
   306  				c.Log().Error(err)
   307  			}
   308  		}()
   309  	}
   310  
   311  	for _, fn := range c.onStopSyncHook {
   312  		if err := fn(ctx); err != nil {
   313  			return err
   314  		}
   315  	}
   316  
   317  	c.Log().V(0).Info("runtime.controller: stopped")
   318  	return nil
   319  }
   320  
   321  func (c *controller) Config() runtime.Config {
   322  	return c.config
   323  }
   324  
   325  func (c *controller) Plugin() runtime.Plugin {
   326  	return c.plugin
   327  }
   328  
   329  func (c *controller) Package() runtime.Package {
   330  	return c.pkg
   331  }
   332  
   333  func (c *controller) Client() runtime.Client {
   334  	return c.client
   335  }
   336  
   337  func (c *controller) Server() runtime.Server {
   338  	return c.server
   339  }
   340  
   341  func (c *controller) Provide(constructor interface{}) {
   342  	c.providers = append(c.providers, constructor)
   343  }
   344  
   345  func (c *controller) Invoke(constructor interface{}) {
   346  	c.invokes = append(c.invokes, constructor)
   347  }
   348  
   349  func (c *controller) Tools() runtime.Tools {
   350  	return c.tools
   351  }
   352  
   353  func (c *controller) Stop(_ context.Context, err error) {
   354  	c.done <- err
   355  	return
   356  }
   357  
   358  func (c *controller) RegisterOnStartHook(fn ...func(ctx context.Context) error) {
   359  	c.onStartHook = append(c.onStartHook, fn...)
   360  }
   361  
   362  func (c *controller) RegisterOnStopHook(fn ...func(ctx context.Context) error) {
   363  	c.onStopHook = append(c.onStopHook, fn...)
   364  }
   365  
   366  func (c *controller) RegisterOnStartSyncHook(fn ...func(ctx context.Context) error) {
   367  	c.onStartSyncHook = append(c.onStartSyncHook, fn...)
   368  }
   369  
   370  func (c *controller) RegisterOnStopSyncHook(fn ...func(ctx context.Context) error) {
   371  	c.onStopSyncHook = append(c.onStopSyncHook, fn...)
   372  }
   373  
   374  func (c *controller) fillMeta(opts ...runtime.Option) {
   375  	for _, opt := range opts {
   376  		switch opt.Name() {
   377  		case runtime.MetaOptionEnvPrefix:
   378  			c.meta.SetEnvPrefix(opt.Value())
   379  		case runtime.MetaOptionVersion:
   380  			c.meta.SetVersion(opt.Value())
   381  		case runtime.MetaOptionDescription:
   382  			c.meta.SetDescription(opt.Value())
   383  		}
   384  	}
   385  }
   386  
   387  func NewRuntime(ctx context.Context, name string, opts ...runtime.Option) (runtime.Runtime, error) {
   388  
   389  	var (
   390  		rt  = new(controller)
   391  		err error
   392  	)
   393  
   394  	rt.done = make(chan error)
   395  
   396  	rt.providers = make([]interface{}, 0)
   397  	rt.invokes = make([]interface{}, 0)
   398  
   399  	rt.onStartHook = make([]func(context.Context) error, 0)
   400  	rt.onStopHook = make([]func(context.Context) error, 0)
   401  
   402  	rt.onStartSyncHook = make([]func(context.Context) error, 0)
   403  	rt.onStopSyncHook = make([]func(context.Context) error, 0)
   404  
   405  	rt.meta = new(meta.Meta)
   406  	rt.meta.SetName(name)
   407  	rt.fillMeta(opts...)
   408  
   409  	rt.config = newConfigController(ctx, rt)
   410  	rt.config.SetMeta(rt.meta)
   411  
   412  	rt.logger = zp.NewLogger(rt, logger.Fields{
   413  		"microservice": name,
   414  	})
   415  
   416  	rt.client = newClientController(ctx, rt)
   417  	rt.plugin = newPluginController(ctx, rt)
   418  	rt.pkg = newPackageController(ctx, rt)
   419  	rt.server = newServerController(ctx, rt)
   420  
   421  	svc := new(service)
   422  	svc.runtime = rt
   423  
   424  	rt.service = svc
   425  
   426  	if rt.tools, err = newToolsRegistration(rt); err != nil {
   427  		return nil, err
   428  	}
   429  
   430  	return rt, nil
   431  }