github.com/cloudwan/edgelq-sdk@v1.15.4/iam/access/v1alpha2/user/user.pb.watcher.go (about) 1 // Code generated by protoc-gen-goten-access 2 // Resource: User 3 // DO NOT EDIT!!! 4 5 package user_access 6 7 import ( 8 "context" 9 "errors" 10 "fmt" 11 "sync/atomic" 12 13 "google.golang.org/grpc" 14 "google.golang.org/protobuf/proto" 15 16 gotenaccess "github.com/cloudwan/goten-sdk/runtime/access" 17 gotenobservability "github.com/cloudwan/goten-sdk/runtime/observability" 18 gotenresource "github.com/cloudwan/goten-sdk/runtime/resource" 19 "github.com/cloudwan/goten-sdk/types/view" 20 "github.com/cloudwan/goten-sdk/types/watch_type" 21 22 user_client "github.com/cloudwan/edgelq-sdk/iam/client/v1alpha2/user" 23 user "github.com/cloudwan/edgelq-sdk/iam/resources/v1alpha2/user" 24 ) 25 26 // TODO: This watcher is for stateless watch type only de facto as of now. 27 // Ordering and multiple filters do not cooperate at all. 28 // We could: 29 // * Add Cursor as parameter to NewWatcher + page size to WatcherConfig 30 // * Add ResetCursor method to Watcher - only one page available at the time byt still dynamic 31 // * Merge many pages with sorting/pagination within watcher and forward aggregated changes. 32 33 // Watcher is higher level stateful watcher with dynamic + multi filter support. 34 type Watcher struct { 35 client user_client.UserServiceClient 36 config *WatcherConfig 37 outputEvtChan chan WatcherEvent 38 iOutputEvtChan chan gotenaccess.WatcherEvent 39 queryWatcherStates map[int]*queryWatcherState 40 watcherEvtsChan chan *QueryWatcherEvent 41 filters []*WatcherFilterParams 42 filtersResetChan chan struct { 43 f []*WatcherFilterParams 44 v int32 45 } 46 inSyncFlag int32 47 nextIdentifier int 48 watcherCtx context.Context 49 watcherCtxCancel context.CancelFunc 50 useIChan int32 51 nextFiltersVersion int32 52 filtersVersion int32 53 } 54 55 type WatcherConfig struct { 56 *gotenaccess.WatcherConfigBase 57 58 // common params that must be shared across queries 59 WatchType watch_type.WatchType 60 View view.View 61 FieldMask *user.User_FieldMask 62 OrderBy *user.OrderBy 63 ChunkSize int 64 } 65 66 type WatcherFilterParams struct { 67 Filter *user.Filter 68 } 69 70 func (p *WatcherFilterParams) String() string { 71 if p == nil { 72 return "<nil>" 73 } 74 return fmt.Sprintf("%s", p.Filter) 75 } 76 77 func (p *WatcherFilterParams) GetIFilter() gotenresource.Filter { 78 return p.Filter 79 } 80 81 func (p *WatcherFilterParams) GetIParentName() gotenresource.Name { 82 return nil 83 } 84 85 type queryWatcherState struct { 86 cancel context.CancelFunc 87 watcher *QueryWatcher 88 inSync bool 89 cache map[user.Name]*user.User 90 filter *WatcherFilterParams 91 inSnapshot bool 92 pendingSnapshot []*user.UserChange 93 } 94 95 func NewWatcher(client user_client.UserServiceClient, config *WatcherConfig, filters ...*WatcherFilterParams) *Watcher { 96 ctx, cancel := context.WithCancel(context.Background()) 97 watcher := &Watcher{ 98 client: client, 99 config: config, 100 filters: filters, 101 outputEvtChan: make(chan WatcherEvent, config.WatcherEventBufferSize), 102 queryWatcherStates: map[int]*queryWatcherState{}, 103 watcherEvtsChan: make(chan *QueryWatcherEvent, config.WatcherEventBufferSize), 104 filtersResetChan: make(chan struct { 105 f []*WatcherFilterParams 106 v int32 107 }, 5), 108 watcherCtx: ctx, 109 watcherCtxCancel: cancel, 110 } 111 watcher.filtersResetChan <- struct { 112 f []*WatcherFilterParams 113 v int32 114 }{f: filters, v: 0} 115 return watcher 116 } 117 118 func (pw *Watcher) Events() <-chan WatcherEvent { 119 return pw.outputEvtChan 120 } 121 122 func (pw *Watcher) IEvents() <-chan gotenaccess.WatcherEvent { 123 if atomic.CompareAndSwapInt32(&pw.useIChan, 0, 1) { 124 pw.iOutputEvtChan = make(chan gotenaccess.WatcherEvent, pw.config.WatcherEventBufferSize) 125 go func() { 126 for { 127 select { 128 case <-pw.watcherCtx.Done(): 129 return 130 case evt := <-pw.outputEvtChan: 131 select { 132 case <-pw.watcherCtx.Done(): 133 return 134 case pw.iOutputEvtChan <- &evt: 135 } 136 } 137 } 138 }() 139 } 140 return pw.iOutputEvtChan 141 } 142 143 func (pw *Watcher) InSync() bool { 144 return atomic.LoadInt32(&pw.inSyncFlag) > 0 145 } 146 147 func (pw *Watcher) GetFilters() []*WatcherFilterParams { 148 copied := make([]*WatcherFilterParams, 0, len(pw.filters)) 149 for _, query := range pw.filters { 150 cQuery := &WatcherFilterParams{} 151 if query.Filter != nil { 152 strValue, _ := query.Filter.ProtoString() 153 cQuery.Filter = &user.Filter{} 154 _ = cQuery.Filter.ParseProtoString(strValue) 155 } 156 copied = append(copied, cQuery) 157 } 158 return copied 159 } 160 161 func (pw *Watcher) GetIFilters() []gotenaccess.WatcherFilterParams { 162 filters := pw.GetFilters() 163 result := make([]gotenaccess.WatcherFilterParams, 0, len(filters)) 164 for _, filter := range filters { 165 result = append(result, filter) 166 } 167 return result 168 } 169 170 func (pw *Watcher) ResetFilters(ctx context.Context, filters ...*WatcherFilterParams) (int32, error) { 171 pw.nextFiltersVersion++ 172 pw.filters = filters 173 select { 174 case <-ctx.Done(): 175 return -1, ctx.Err() 176 case pw.filtersResetChan <- struct { 177 f []*WatcherFilterParams 178 v int32 179 }{f: filters, v: pw.nextFiltersVersion}: 180 } 181 return pw.nextFiltersVersion, nil 182 } 183 184 func (pw *Watcher) ResetIFilters(ctx context.Context, filters ...gotenaccess.WatcherFilterParams) (int32, error) { 185 typedFilters := make([]*WatcherFilterParams, 0, len(filters)) 186 for _, filter := range filters { 187 typedFilters = append(typedFilters, filter.(*WatcherFilterParams)) 188 } 189 return pw.ResetFilters(ctx, typedFilters...) 190 } 191 192 func (pw *Watcher) Run(ctx context.Context) error { 193 log := gotenobservability.LoggerFromContext(ctx). 194 WithField("watcher", "user-watcher") 195 ctx = gotenobservability.LoggerToContext(ctx, log) 196 197 log.Debugf("running") 198 defer func() { 199 for _, state := range pw.queryWatcherStates { 200 state.cancel() 201 } 202 pw.watcherCtxCancel() 203 }() 204 205 for { 206 select { 207 case newFilters := <-pw.filtersResetChan: 208 pw.filtersVersion = newFilters.v 209 pw.resetFilters(ctx, newFilters.f) 210 case evt := <-pw.watcherEvtsChan: 211 if evt.LostSync { 212 pw.onQueryLostSync(ctx, evt) 213 } else if evt.CheckSize { 214 pw.onQueryVerifySnapshotSize(ctx, evt) 215 } else if evt.Reset { 216 pw.onQueryResetStateIndicator(evt) 217 } else { 218 pw.onQueryChanges(ctx, evt) 219 } 220 case <-ctx.Done(): 221 log.Debugf("context done, reason: %s", ctx.Err()) 222 if errors.Is(ctx.Err(), context.Canceled) { 223 return nil 224 } else { 225 return ctx.Err() 226 } 227 } 228 } 229 } 230 231 func (pw *Watcher) resetFilters(ctx context.Context, filters []*WatcherFilterParams) { 232 filtersMap := map[string]*WatcherFilterParams{} 233 for _, filter := range filters { 234 filtersMap[filter.String()] = filter 235 } 236 237 // Eliminate existing queries from queriesMap, eliminate queries that should be stopped 238 for identifier, state := range pw.queryWatcherStates { 239 key := state.filter.String() 240 if _, exists := filtersMap[key]; !exists { 241 state.cancel() 242 delete(pw.queryWatcherStates, identifier) 243 } else { 244 delete(filtersMap, key) 245 } 246 } 247 248 // start new queries with new filters 249 for _, filter := range filtersMap { 250 wctx, cancel := context.WithCancel(ctx) 251 newWatcher := NewQueryWatcher( 252 pw.nextIdentifier, 253 pw.client, 254 &QueryWatcherParams{ 255 Filter: filter.Filter, 256 View: pw.config.View, 257 FieldMask: pw.config.FieldMask, 258 OrderBy: pw.config.OrderBy, 259 ChunkSize: pw.config.ChunkSize, 260 WatchType: pw.config.WatchType, 261 RecoveryDeadline: pw.config.RecoveryDeadline, 262 RetryTimeout: pw.config.RetryTimeout, 263 }, 264 pw.watcherEvtsChan) 265 pw.queryWatcherStates[pw.nextIdentifier] = &queryWatcherState{ 266 cancel: cancel, 267 watcher: newWatcher, 268 cache: map[user.Name]*user.User{}, 269 filter: filter, 270 inSnapshot: true, 271 } 272 pw.nextIdentifier++ 273 274 go func() { 275 _ = newWatcher.Run(wctx) 276 }() 277 } 278 279 // if all in sync (possible if queries were eliminated with ResetFilters call), send snapshot, otherwise 280 // communicate "lost sync" 281 if pw.allInSync() { 282 pw.processSnapshot(ctx) 283 } else if pw.inSyncFlag > 0 { 284 pw.processSyncLost(ctx) 285 } 286 } 287 288 func (pw *Watcher) onQueryLostSync(ctx context.Context, evt *QueryWatcherEvent) { 289 state := pw.queryWatcherStates[evt.Identifier] 290 if state == nil { 291 return 292 } 293 state.inSync = false 294 state.cache = map[user.Name]*user.User{} 295 state.inSnapshot = true 296 if pw.inSyncFlag > 0 { 297 pw.processSyncLost(ctx) 298 } 299 } 300 301 func (pw *Watcher) onQueryVerifySnapshotSize(ctx context.Context, evt *QueryWatcherEvent) { 302 state := pw.queryWatcherStates[evt.Identifier] 303 if state == nil { 304 return 305 } 306 currentSize := state.computeSize(evt.Changes) 307 if currentSize != evt.SnapshotSize { 308 gotenobservability.LoggerFromContext(ctx). 309 Warnf("Detected mismatch in snapshot size for filter %s: expected %d, has %d", 310 state.filter, evt.SnapshotSize, currentSize) 311 state.cancel() 312 delete(pw.queryWatcherStates, evt.Identifier) 313 314 // refresh this particular watcher 315 wctx, cancel := context.WithCancel(ctx) 316 newWatcher := NewQueryWatcher( 317 pw.nextIdentifier, 318 pw.client, 319 &QueryWatcherParams{ 320 Filter: state.filter.Filter, 321 View: pw.config.View, 322 FieldMask: pw.config.FieldMask, 323 OrderBy: pw.config.OrderBy, 324 ChunkSize: pw.config.ChunkSize, 325 WatchType: pw.config.WatchType, 326 RecoveryDeadline: pw.config.RecoveryDeadline, 327 RetryTimeout: pw.config.RetryTimeout, 328 }, 329 pw.watcherEvtsChan) 330 pw.queryWatcherStates[pw.nextIdentifier] = &queryWatcherState{ 331 cancel: cancel, 332 watcher: newWatcher, 333 cache: state.cache, 334 filter: state.filter, 335 inSnapshot: true, 336 inSync: state.inSync, 337 } 338 pw.nextIdentifier++ 339 340 go func() { 341 _ = newWatcher.Run(wctx) 342 }() 343 } 344 } 345 346 func (pw *Watcher) onQueryResetStateIndicator(evt *QueryWatcherEvent) { 347 state := pw.queryWatcherStates[evt.Identifier] 348 if state == nil { 349 return 350 } 351 state.inSnapshot = true 352 state.pendingSnapshot = nil 353 } 354 355 func (pw *Watcher) onQueryChanges(ctx context.Context, evt *QueryWatcherEvent) { 356 var changes []*WatcherEventChange 357 state := pw.queryWatcherStates[evt.Identifier] 358 if state == nil { 359 return 360 } 361 362 // evt.InSync is set when snapshot is complete or for every incremental update 363 // Since Watcher is stateful, we will collect query snapshot data without processing 364 // till its complete. 365 if state.inSnapshot { 366 state.pendingSnapshot = append(state.pendingSnapshot, evt.Changes...) 367 if evt.InSync { 368 // processes snapshot for this query only 369 // changes however may be only a diff from previous known state 370 changes = state.processSnapshot() 371 } 372 } else { 373 changes = state.transform(evt.Changes) 374 } 375 if evt.InSync { 376 if !state.inSync { 377 state.inSync = true 378 if pw.allInSync() { 379 pw.processSnapshot(ctx) 380 return 381 } 382 } else if pw.inSyncFlag > 0 { 383 evt := &WatcherEvent{WatcherEventBase: gotenaccess.NewWatcherEventBase(false, pw.filtersVersion), Changes: changes} 384 pw.sendEvent(ctx, evt) 385 } 386 } 387 } 388 389 func (pw *Watcher) processSyncLost(ctx context.Context) { 390 gotenobservability.LoggerFromContext(ctx).Infof("Notifiying watcher-wide sync lost") 391 atomic.StoreInt32(&pw.inSyncFlag, 0) 392 evt := &WatcherEvent{WatcherEventBase: gotenaccess.NewWatcherEventBaseLostSync(pw.filtersVersion)} 393 pw.sendEvent(ctx, evt) 394 } 395 396 func (pw *Watcher) processSnapshot(ctx context.Context) { 397 gotenobservability.LoggerFromContext(ctx).Infof("Watcher in sync") 398 atomic.StoreInt32(&pw.inSyncFlag, 1) 399 snapshot := make([]*WatcherEventChange, 0) 400 for _, state := range pw.queryWatcherStates { 401 for _, item := range state.cache { 402 snapshot = append(snapshot, NewAddWatcherEventChange(item)) 403 } 404 } 405 evt := &WatcherEvent{WatcherEventBase: gotenaccess.NewWatcherEventBase(true, pw.filtersVersion), Changes: snapshot} 406 pw.sendEvent(ctx, evt) 407 } 408 409 func (pw *Watcher) allInSync() bool { 410 for _, state := range pw.queryWatcherStates { 411 if !state.inSync { 412 return false 413 } 414 } 415 return true 416 } 417 418 func (pw *Watcher) sendEvent(ctx context.Context, evt *WatcherEvent) { 419 select { 420 case <-ctx.Done(): 421 case pw.outputEvtChan <- *evt: 422 } 423 } 424 425 type WatcherEventChange struct { 426 pre, post *user.User 427 name *user.Name 428 } 429 430 func NewAddWatcherEventChange(resource *user.User) *WatcherEventChange { 431 return &WatcherEventChange{ 432 post: resource, 433 name: resource.GetName(), 434 } 435 } 436 437 func NewModifyWatcherEventChange(current, previous *user.User) *WatcherEventChange { 438 return &WatcherEventChange{ 439 pre: previous, 440 post: current, 441 name: current.GetName(), 442 } 443 } 444 445 func NewDeleteWatcherEventChange(deleted *user.User) *WatcherEventChange { 446 return &WatcherEventChange{ 447 pre: deleted, 448 name: deleted.GetName(), 449 } 450 } 451 452 func (c *WatcherEventChange) GetName() *user.Name { 453 return c.name 454 } 455 456 func (c *WatcherEventChange) GetRawName() gotenresource.Name { 457 return c.name 458 } 459 460 func (c *WatcherEventChange) IsAdd() bool { 461 return c.pre == nil && c.post != nil 462 } 463 464 func (c *WatcherEventChange) IsModify() bool { 465 return c.pre != nil && c.post != nil 466 } 467 468 func (c *WatcherEventChange) IsDelete() bool { 469 return c.pre != nil && c.post == nil 470 } 471 472 func (c *WatcherEventChange) GetAdded() *user.User { 473 if c.IsAdd() { 474 return c.post 475 } 476 return nil 477 } 478 479 func (c *WatcherEventChange) GetDeleted() *user.User { 480 if c.IsDelete() { 481 return c.pre 482 } 483 return nil 484 } 485 486 func (c *WatcherEventChange) GetPrevious() *user.User { 487 if c.IsModify() { 488 return c.pre 489 } 490 return nil 491 } 492 493 func (c *WatcherEventChange) GetCurrent() *user.User { 494 if c.IsAdd() || c.IsModify() { 495 return c.post 496 } 497 return nil 498 } 499 500 func (c *WatcherEventChange) GetRawAdded() gotenresource.Resource { 501 return c.GetAdded() 502 } 503 504 func (c *WatcherEventChange) GetRawDeleted() gotenresource.Resource { 505 return c.GetDeleted() 506 } 507 508 func (c *WatcherEventChange) GetRawPrevious() gotenresource.Resource { 509 return c.GetPrevious() 510 } 511 512 func (c *WatcherEventChange) GetRawCurrent() gotenresource.Resource { 513 return c.GetCurrent() 514 } 515 516 type WatcherEvent struct { 517 gotenaccess.WatcherEventBase 518 Changes []*WatcherEventChange 519 } 520 521 func (e *WatcherEvent) GetAt(index int) *WatcherEventChange { 522 if e == nil || len(e.Changes) <= index { 523 return nil 524 } 525 return e.Changes[index] 526 } 527 528 func (e *WatcherEvent) GetRawAt(index int) gotenaccess.WatcherEventChange { 529 return e.GetAt(index) 530 } 531 532 func (e *WatcherEvent) Length() int { 533 if e == nil { 534 return 0 535 } 536 return len(e.Changes) 537 } 538 539 func (e *WatcherEvent) AppendChange(change *WatcherEventChange) { 540 e.Changes = append(e.Changes, change) 541 } 542 543 func (e *WatcherEvent) AppendRawChange(change gotenaccess.WatcherEventChange) { 544 e.Changes = append(e.Changes, change.(*WatcherEventChange)) 545 } 546 547 // Merge makes a shallow merge of two events 548 func (e *WatcherEvent) Merge(src *WatcherEvent) { 549 if src == nil { 550 return 551 } 552 if src.LostSync() || src.Resync() { 553 e.WatcherEventBase = src.WatcherEventBase 554 e.Changes = src.Changes 555 return 556 } 557 if !e.WatcherEventBase.Resync() { 558 e.WatcherEventBase = src.WatcherEventBase 559 e.Changes = append(e.Changes, src.Changes...) 560 return 561 } 562 563 // merge non-resync into resync 564 byName := make(map[user.Name]*user.User, len(src.Changes)+len(e.Changes)) 565 for _, currentChange := range e.Changes { 566 user := currentChange.GetCurrent() 567 byName[*user.GetName()] = user 568 } 569 for _, change := range src.Changes { 570 if change.IsDelete() { 571 delete(byName, *change.GetName()) 572 } else { 573 user := change.GetCurrent() 574 byName[*user.GetName()] = user 575 } 576 } 577 e.Changes = make([]*WatcherEventChange, 0, len(byName)) 578 for _, user := range byName { 579 e.Changes = append(e.Changes, NewAddWatcherEventChange(user)) 580 } 581 } 582 583 func (qws *queryWatcherState) processSnapshot() []*WatcherEventChange { 584 watcherEventChanges := make([]*WatcherEventChange, 0, len(qws.pendingSnapshot)) 585 prevCache := qws.cache 586 qws.cache = map[user.Name]*user.User{} 587 588 for _, change := range qws.pendingSnapshot { 589 var currentItem *user.User 590 switch item := change.ChangeType.(type) { 591 case *user.UserChange_Added_: 592 currentItem = item.Added.GetUser() 593 case *user.UserChange_Current_: 594 currentItem = item.Current.GetUser() 595 } 596 name := *currentItem.GetName() 597 qws.cache[name] = currentItem 598 previous, exists := prevCache[name] 599 if !exists { 600 watcherEventChanges = append(watcherEventChanges, NewAddWatcherEventChange(currentItem)) 601 } else { 602 if !proto.Equal(currentItem, previous) { 603 watcherEventChanges = append(watcherEventChanges, NewModifyWatcherEventChange(currentItem, previous)) 604 } 605 delete(prevCache, name) 606 } 607 } 608 // remaining items 609 for _, resource := range prevCache { 610 watcherEventChanges = append(watcherEventChanges, NewDeleteWatcherEventChange(resource)) 611 } 612 qws.pendingSnapshot = nil 613 qws.inSnapshot = false 614 return watcherEventChanges 615 } 616 617 func (qws *queryWatcherState) transform(rawChanges []*user.UserChange) []*WatcherEventChange { 618 watcherEventChanges := make([]*WatcherEventChange, 0, len(rawChanges)) 619 620 for _, change := range rawChanges { 621 switch item := change.ChangeType.(type) { 622 case *user.UserChange_Added_: 623 newItem := item.Added.GetUser() 624 qws.cache[*newItem.GetName()] = newItem 625 watcherEventChanges = append(watcherEventChanges, NewAddWatcherEventChange(newItem)) 626 case *user.UserChange_Modified_: 627 updatedItem := item.Modified.GetUser() 628 previousItem := qws.cache[*updatedItem.GetName()] 629 qws.cache[*updatedItem.GetName()] = updatedItem 630 watcherEventChanges = append(watcherEventChanges, NewModifyWatcherEventChange(updatedItem, previousItem)) 631 case *user.UserChange_Current_: 632 currentItem := item.Current.GetUser() 633 previousItem := qws.cache[*currentItem.GetName()] 634 qws.cache[*currentItem.GetName()] = currentItem 635 if previousItem != nil { 636 watcherEventChanges = append(watcherEventChanges, NewModifyWatcherEventChange(currentItem, previousItem)) 637 } else { 638 watcherEventChanges = append(watcherEventChanges, NewAddWatcherEventChange(currentItem)) 639 } 640 case *user.UserChange_Removed_: 641 deletedItem := qws.cache[*item.Removed.Name] 642 delete(qws.cache, *item.Removed.Name) 643 watcherEventChanges = append(watcherEventChanges, NewDeleteWatcherEventChange(deletedItem)) 644 } 645 } 646 return watcherEventChanges 647 } 648 649 func (qws *queryWatcherState) computeSize(pendingChanges []*user.UserChange) int64 { 650 size := int64(len(qws.cache)) 651 for _, pendingChange := range append(qws.pendingSnapshot, pendingChanges...) { 652 name := *pendingChange.GetUserName() 653 if pendingChange.IsDelete() { 654 if _, exists := qws.cache[name]; exists { 655 size-- 656 } 657 } else { 658 if _, exists := qws.cache[name]; !exists { 659 size++ 660 } 661 } 662 } 663 return size 664 } 665 666 func init() { 667 gotenaccess.GetRegistry().RegisterWatcherConstructor(user.GetDescriptor(), func(cc grpc.ClientConnInterface, params *gotenaccess.WatcherConfigParams, filters ...gotenaccess.WatcherFilterParams) gotenaccess.Watcher { 668 cfg := &WatcherConfig{ 669 WatcherConfigBase: params.CfgBase, 670 WatchType: params.WatchType, 671 View: params.View, 672 ChunkSize: params.ChunkSize, 673 } 674 if params.FieldMask != nil { 675 cfg.FieldMask = params.FieldMask.(*user.User_FieldMask) 676 } 677 if params.OrderBy != nil { 678 cfg.OrderBy = params.OrderBy.(*user.OrderBy) 679 } 680 typedFilters := make([]*WatcherFilterParams, 0, len(filters)) 681 for _, filter := range filters { 682 typedFilters = append(typedFilters, filter.(*WatcherFilterParams)) 683 } 684 return NewWatcher(user_client.NewUserServiceClient(cc), cfg, typedFilters...) 685 }) 686 gotenaccess.GetRegistry().RegisterWatcherFilterConstructor(user.GetDescriptor(), func(filter gotenresource.Filter, parent gotenresource.Name) gotenaccess.WatcherFilterParams { 687 params := &WatcherFilterParams{} 688 if filter != nil { 689 params.Filter = filter.(*user.Filter) 690 } 691 return params 692 }) 693 }