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 }