github.com/erda-project/erda-infra@v1.0.9/base/servicehub/hub.go (about) 1 // Copyright (c) 2021 Terminus, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package servicehub 16 17 import ( 18 "bytes" 19 "context" 20 "fmt" 21 "io" 22 "os" 23 "reflect" 24 "runtime" 25 "strconv" 26 "strings" 27 "sync" 28 "syscall" 29 "time" 30 31 "github.com/recallsong/go-utils/errorx" 32 "github.com/recallsong/go-utils/os/signalx" 33 "github.com/sirupsen/logrus" 34 "github.com/spf13/pflag" 35 36 "github.com/erda-project/erda-infra/base/logs" 37 "github.com/erda-project/erda-infra/base/logs/logrusx" 38 graph "github.com/erda-project/erda-infra/base/servicehub/dependency-graph" 39 "github.com/erda-project/erda-infra/pkg/config" 40 ) 41 42 // Hub . 43 type Hub struct { 44 logger logs.Logger 45 providersMap map[string][]*providerContext 46 providers []*providerContext 47 servicesMap map[string][]*providerContext 48 servicesTypes map[reflect.Type][]*providerContext 49 lock sync.RWMutex 50 51 started bool 52 ctx context.Context 53 cancel func() 54 wg sync.WaitGroup 55 56 listeners []Listener 57 58 // problematic providers 59 problematicProviderNames []string 60 problemLock sync.Mutex 61 } 62 63 // New . 64 func New(options ...interface{}) *Hub { 65 hub := &Hub{} 66 hub.ctx, hub.cancel = context.WithCancel(context.Background()) 67 for _, opt := range options { 68 processOptions(hub, opt) 69 } 70 if hub.logger == nil { 71 level := os.Getenv("LOG_LEVEL") 72 lvl, err := logrus.ParseLevel(level) 73 if err == nil { 74 hub.logger = logrusx.New(logrusx.WithLevel(lvl)) 75 } else { 76 hub.logger = logrusx.New() 77 } 78 } 79 return hub 80 } 81 82 // Init . 83 func (h *Hub) Init(config map[string]interface{}, flags *pflag.FlagSet, args []string) (err error) { 84 defer func() { 85 // exp := recover() 86 // if exp != nil { 87 // if e, ok := exp.(error); ok { 88 // err = e 89 // } else { 90 // err = fmt.Errorf("%v", exp) 91 // } 92 // } 93 if err != nil { 94 h.logger.Errorf("fail to init service hub: %s", err) 95 } 96 }() 97 for i, l := 0, len(h.listeners); i < l; i++ { 98 err = h.listeners[i].BeforeInitialization(h, config) 99 if err != nil { 100 return err 101 } 102 } 103 err = h.loadProviders(config) 104 if err != nil { 105 return err 106 } 107 108 depGraph, err := h.resolveDependency(h.providersMap) 109 if err != nil { 110 return fmt.Errorf("fail to resolve dependency: %s", err) 111 } 112 113 flags.BoolP("providers", "p", false, "print all providers supported") 114 flags.BoolP("graph", "g", false, "print providers dependency graph") 115 for _, ctx := range h.providers { 116 err = ctx.BindConfig(flags) 117 if err != nil { 118 return fmt.Errorf("fail to bind config for provider %s: %s", ctx.name, err) 119 } 120 } 121 err = flags.Parse(args) 122 if err != nil { 123 return fmt.Errorf("fail to bind flags: %s", err) 124 } 125 if ok, err := flags.GetBool("providers"); err == nil && ok { 126 usage := Usage() 127 fmt.Println(usage) 128 os.Exit(0) 129 } 130 if ok, err := flags.GetBool("graph"); err == nil && ok { 131 depGraph.Display() 132 os.Exit(0) 133 } 134 for _, ctx := range h.providers { 135 h.logger.Infof("provider %s is initializing", ctx.key) 136 err = ctx.Init() 137 if err != nil { 138 return err 139 } 140 dependencies := ctx.dependencies() 141 if len(dependencies) > 0 { 142 h.logger.Infof("provider %s (depends %s) initialized", ctx.key, dependencies) 143 } else { 144 h.logger.Infof("provider %s initialized", ctx.key) 145 } 146 } 147 for i := len(h.listeners) - 1; i >= 0; i-- { 148 err = h.listeners[i].AfterInitialization(h) 149 if err != nil { 150 return err 151 } 152 } 153 return nil 154 } 155 156 func (h *Hub) resolveDependency(providersMap map[string][]*providerContext) (graph.Graph, error) { 157 services := map[string][]*providerContext{} 158 types := map[reflect.Type][]*providerContext{} 159 for _, p := range providersMap { 160 d := p[0].define 161 var list []string 162 if ps, ok := d.(ProviderServices); ok { 163 list = ps.Services() 164 } else if ps, ok := d.(ProviderService); ok { 165 list = ps.Service() 166 } 167 for _, s := range list { 168 if exist, ok := services[s]; ok { 169 return nil, fmt.Errorf("service %s conflict between %s and %s", s, exist[0].name, p[0].name) 170 } 171 services[s] = p 172 } 173 if ts, ok := d.(ServiceTypes); ok { 174 for _, t := range ts.Types() { 175 if exist, ok := types[t]; ok { 176 return nil, fmt.Errorf("service type %s conflict between %s and %s", t, exist[0].name, p[0].name) 177 } 178 types[t] = p 179 } 180 } 181 } 182 h.servicesMap = services 183 h.servicesTypes = types 184 var depGraph graph.Graph 185 for name, p := range providersMap { 186 providers := map[string]*providerContext{} 187 dependsServices, dependsProviders := p[0].Dependencies() 188 loop: 189 for _, service := range dependsServices { 190 name := service 191 var label string 192 idx := strings.Index(service, "@") 193 if idx > 0 { 194 name, label = service[0:idx], service[idx+1:] 195 } 196 if deps, ok := services[name]; ok { 197 if len(label) > 0 { 198 for _, dep := range deps { 199 if dep.label == label { 200 providers[dep.name] = dep 201 continue loop 202 } 203 } 204 } else if len(deps) > 0 { 205 providers[deps[0].name] = deps[0] 206 continue loop 207 } 208 } 209 return nil, fmt.Errorf("provider %s depends on service %s, but it not found", p[0].fullName(), service) 210 } 211 node := graph.NewNode(name) 212 for dep := range providers { 213 node.Deps = append(node.Deps, dep) 214 } 215 for _, dep := range dependsProviders { 216 if _, ok := providers[dep]; !ok { 217 node.Deps = append(node.Deps, dep) 218 } 219 } 220 depGraph = append(depGraph, node) 221 } 222 resolved, err := graph.Resolve(depGraph) 223 if err != nil { 224 depGraph.Display() 225 return depGraph, err 226 } 227 var providers []*providerContext 228 for _, node := range resolved { 229 providers = append(providers, providersMap[node.Name]...) 230 } 231 h.providers = providers 232 return resolved, nil 233 } 234 235 // StartWithSignal . 236 func (h *Hub) StartWithSignal() error { 237 sigs := []os.Signal{syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT} 238 h.logger.Infof("signals to quit: %v", sigs) 239 return h.Start(signalx.Notify(sigs...)) 240 } 241 242 // Start . 243 func (h *Hub) Start(closer ...<-chan os.Signal) (err error) { 244 h.lock.Lock() 245 ctx := h.ctx 246 ch := make(chan error, len(h.providers)) 247 var num int 248 for _, item := range h.providers { 249 key := item.key 250 if key != item.name { 251 key = fmt.Sprintf("%s (%s)", item.key, item.name) 252 } 253 if runner, ok := item.provider.(ProviderRunner); ok { 254 num++ 255 h.wg.Add(1) 256 go func(key string, provider ProviderRunner) { 257 h.logger.Infof("provider %s starting ...", key) 258 err := provider.Start() 259 if err != nil { 260 h.logger.Errorf("failed to start provider %s: %s", key, err) 261 h.addProblematicProvider(key) 262 } else { 263 h.logger.Infof("provider %s closed", key) 264 } 265 h.wg.Done() 266 ch <- err 267 }(key, runner) 268 } 269 if runner, ok := item.provider.(ProviderRunnerWithContext); ok { 270 num++ 271 h.wg.Add(1) 272 go func(key string, provider ProviderRunnerWithContext) { 273 h.logger.Infof("provider %s running ...", key) 274 err := provider.Run(ctx) 275 if err != nil { 276 h.addProblematicProvider(key) 277 h.logger.Errorf("failed to run provider %s: %s", key, err) 278 } else { 279 h.logger.Infof("provider %s Run exit", key) 280 } 281 h.wg.Done() 282 ch <- err 283 }(key, runner) 284 } 285 for i, t := range item.tasks { 286 num++ 287 h.wg.Add(1) 288 go func(key string, i int, t task) { 289 tname := t.name 290 if len(tname) <= 0 { 291 tname = strconv.Itoa(i + 1) 292 } 293 h.logger.Infof("provider %s task(%s) running ...", key, tname) 294 err := t.fn(ctx) 295 if err != nil { 296 h.addProblematicProvider(key) 297 h.logger.Errorf("failed to run provider %s task(%s): %s", key, tname, err) 298 } else { 299 h.logger.Infof("provider %s task(%s) exit", key, tname) 300 } 301 h.wg.Done() 302 ch <- err 303 }(key, i, t) 304 } 305 } 306 h.started = true 307 h.lock.Unlock() 308 runtime.Gosched() 309 310 for i, l := 0, len(h.listeners); i < l; i++ { 311 err = h.listeners[i].AfterStart(h) 312 if err != nil { 313 return err 314 } 315 } 316 317 closeCh, closed := make(chan struct{}), false 318 var elock sync.Mutex 319 for _, ch := range closer { 320 go func(ch <-chan os.Signal) { 321 select { 322 case signal := <-ch: 323 h.logger.Errorf("signal received: %s, hub begin quitting ...\n", signal.String()) 324 case <-closeCh: 325 } 326 elock.Lock() 327 fmt.Println() 328 wait := make(chan error) 329 go func() { 330 wait <- h.Close() 331 }() 332 select { 333 case <-time.After(30 * time.Second): 334 h.logger.Errorf("exit service manager timeout !") 335 h.printProblematicProviders() 336 os.Exit(1) 337 case err := <-wait: 338 h.printProblematicProviders() 339 if err != nil { 340 h.logger.Errorf("fail to exit: %s", err) 341 os.Exit(1) 342 } 343 } 344 }(ch) 345 } 346 // wait to stop 347 errs := errorx.Errors{} 348 for i := 0; i < num; i++ { 349 select { 350 case err := <-ch: 351 if err != nil { 352 errs = append(errs, err) 353 if !closed { 354 close(closeCh) 355 closed = true 356 } 357 } 358 } 359 } 360 err = errs.MaybeUnwrap() 361 for i, l := 0, len(h.listeners); i < l; i++ { 362 err = h.listeners[i].BeforeExit(h, err) 363 } 364 h.printProblematicProviders() 365 return err 366 } 367 368 func (h *Hub) addProblematicProvider(name string) { 369 h.problemLock.Lock() 370 defer h.problemLock.Unlock() 371 h.problematicProviderNames = append(h.problematicProviderNames, name) 372 } 373 func (h *Hub) printProblematicProviders() { 374 if len(h.problematicProviderNames) == 0 { 375 return 376 } 377 h.logger.Errorf("problematic providers: %v", h.problematicProviderNames) 378 } 379 380 // Close . 381 func (h *Hub) Close() error { 382 h.lock.Lock() 383 if !h.started { 384 h.lock.Unlock() 385 return nil 386 } 387 var errs errorx.Errors 388 for i := len(h.providers) - 1; i >= 0; i-- { 389 if runner, ok := h.providers[i].provider.(ProviderRunner); ok { 390 err := runner.Close() 391 if err != nil { 392 errs = append(errs, err) 393 } 394 } 395 } 396 h.cancel() 397 h.wg.Wait() 398 h.started = false 399 h.ctx, h.cancel = context.WithCancel(context.Background()) 400 h.lock.Unlock() 401 return errs.MaybeUnwrap() 402 } 403 404 // ForeachServices . 405 func (h *Hub) ForeachServices(fn func(service string) bool) { 406 for key := range h.servicesMap { 407 if !fn(key) { 408 return 409 } 410 } 411 } 412 413 // IsServiceExist . 414 func (h *Hub) IsServiceExist(service string) bool { 415 return len(h.servicesMap[service]) > 0 416 } 417 418 // Service . 419 func (h *Hub) Service(name string, options ...interface{}) interface{} { 420 return h.getService(newDependencyContext( 421 name, 422 "", 423 nil, 424 reflect.StructTag(""), 425 ), options...) 426 } 427 428 func (h *Hub) getService(dc DependencyContext, options ...interface{}) (instance interface{}) { 429 var pc *providerContext 430 if len(dc.Service()) > 0 { 431 if providers, ok := h.servicesMap[dc.Service()]; ok { 432 if len(providers) > 0 { 433 if len(dc.Label()) > 0 { 434 for _, item := range providers { 435 if item.label == dc.Label() { 436 pc = item 437 break 438 } 439 } 440 } else { 441 for _, item := range providers { 442 if item.key == item.name { 443 pc = item 444 break 445 } 446 } 447 if pc == nil && len(providers) > 0 { 448 pc = providers[0] 449 } 450 } 451 } 452 } 453 } else if dc.Type() != nil { 454 providers := h.servicesTypes[dc.Type()] 455 for _, item := range providers { 456 if item.key == item.name { 457 pc = item 458 break 459 } 460 } 461 if pc == nil && len(providers) > 0 { 462 pc = providers[0] 463 } 464 } 465 if pc != nil { 466 provider := pc.provider 467 if prod, ok := provider.(DependencyProvider); ok { 468 return prod.Provide(dc, options...) 469 } 470 return provider 471 } 472 return nil 473 } 474 475 // Provider . 476 func (h *Hub) Provider(name string) interface{} { 477 var label string 478 idx := strings.Index(name, "@") 479 if idx > 0 { 480 label = name[idx+1:] 481 name = name[0:idx] 482 } 483 ps := h.providersMap[name] 484 if len(label) > 0 { 485 for _, p := range ps { 486 if p.label == label { 487 return p.provider 488 } 489 } 490 } else if len(ps) > 0 { 491 return ps[0].provider 492 } 493 return nil 494 } 495 496 // RunOptions . 497 type RunOptions struct { 498 Name string 499 ConfigFile string 500 Content interface{} 501 Format string 502 Args []string 503 } 504 505 // RunWithOptions . 506 func (h *Hub) RunWithOptions(opts *RunOptions) { 507 name := opts.Name 508 if len(name) <= 0 { 509 name = getAppName(opts.Args...) 510 } 511 config.LoadEnvFile() 512 513 var err error 514 var start bool 515 defer func() { 516 if !start { 517 for i, l := 0, len(h.listeners); i < l; i++ { 518 err = h.listeners[i].BeforeExit(h, err) 519 } 520 } 521 if err != nil { 522 os.Exit(1) 523 } 524 }() 525 526 format := "yaml" 527 if len(opts.Format) > 0 { 528 format = opts.Format 529 } 530 cfgmap := make(map[string]interface{}) 531 if opts.Content != nil { 532 var reader io.Reader 533 switch val := opts.Content.(type) { 534 case map[string]interface{}: 535 cfgmap = val 536 case string: 537 reader = strings.NewReader(val) 538 case []byte: 539 reader = bytes.NewReader(val) 540 default: 541 err = fmt.Errorf("invalid config content type") 542 h.logger.Error(err) 543 return 544 } 545 if reader != nil { 546 err = config.UnmarshalToMap(reader, format, cfgmap) 547 if err != nil { 548 h.logger.Errorf("fail to parse %s config: %s", format, err) 549 return 550 } 551 } 552 } 553 554 cfgfile := opts.ConfigFile 555 if len(cfgmap) <= 0 && len(opts.ConfigFile) <= 0 { 556 cfgfile = name + "." + format 557 } 558 cfgmap, err = h.loadConfigWithArgs(cfgfile, cfgmap, opts.Args...) 559 if err != nil { 560 return 561 } 562 563 flags := pflag.NewFlagSet(name, pflag.ExitOnError) 564 flags.StringP("config", "c", cfgfile, "config file to load providers") 565 err = h.Init(cfgmap, flags, opts.Args) 566 if err != nil { 567 return 568 } 569 defer h.Close() 570 start = true 571 err = h.StartWithSignal() 572 if err != nil { 573 return 574 } 575 } 576 577 // Run . 578 func (h *Hub) Run(name, cfgfile string, args ...string) { 579 h.RunWithOptions(&RunOptions{ 580 Name: name, 581 ConfigFile: cfgfile, 582 Args: args, 583 }) 584 } 585 586 // Run . 587 func Run(opts *RunOptions) *Hub { 588 hub := New() 589 hub.RunWithOptions(opts) 590 return hub 591 } 592 593 func getAppName(args ...string) string { 594 if len(args) <= 0 { 595 return "" 596 } 597 name := args[0] 598 idx := strings.LastIndex(os.Args[0], "/") 599 if idx >= 0 { 600 return name[idx+1:] 601 } 602 return "" 603 }