| 492 | } |
| 493 | |
| 494 | func (c *TunnelSrcCoordController) SyncDestinations(destinations []uuid.UUID) { |
| 495 | c.mu.Lock() |
| 496 | defer c.mu.Unlock() |
| 497 | toAdd := make(map[uuid.UUID]struct{}) |
| 498 | toRemove := maps.Clone(c.dests) |
| 499 | all := make(map[uuid.UUID]struct{}) |
| 500 | for _, dest := range destinations { |
| 501 | all[dest] = struct{}{} |
| 502 | delete(toRemove, dest) |
| 503 | if _, ok := c.dests[dest]; !ok { |
| 504 | toAdd[dest] = struct{}{} |
| 505 | } |
| 506 | } |
| 507 | c.dests = all |
| 508 | if c.coordination == nil { |
| 509 | return |
| 510 | } |
| 511 | var err error |
| 512 | defer func() { |
| 513 | if err != nil { |
| 514 | c.coordination.SendErr(err) |
| 515 | cErr := c.coordination.client.Close() // don't gracefully disconnect |
| 516 | if cErr != nil { |
| 517 | c.Logger.Debug(context.Background(), |
| 518 | "failed to close coordinator client during sync destinations", |
| 519 | slog.Error(cErr)) |
| 520 | } |
| 521 | c.coordination = nil |
| 522 | } |
| 523 | }() |
| 524 | for dest := range toAdd { |
| 525 | c.Coordinatee.SetTunnelDestination(dest) |
| 526 | err = c.coordination.SendRequest( |
| 527 | &proto.CoordinateRequest{ |
| 528 | AddTunnel: &proto.CoordinateRequest_Tunnel{Id: UUIDToByteSlice(dest)}, |
| 529 | }) |
| 530 | if err != nil { |
| 531 | return |
| 532 | } |
| 533 | } |
| 534 | for dest := range toRemove { |
| 535 | err = c.coordination.SendRequest( |
| 536 | &proto.CoordinateRequest{ |
| 537 | RemoveTunnel: &proto.CoordinateRequest_Tunnel{Id: UUIDToByteSlice(dest)}, |
| 538 | }) |
| 539 | if err != nil { |
| 540 | return |
| 541 | } |
| 542 | } |
| 543 | } |
| 544 | |
| 545 | // NewAgentCoordinationController creates a CoordinationController for Coder Agents, which never |
| 546 | // create tunnels and always send ReadyToHandshake acknowledgements. |