@@ -793,18 +793,25 @@ func (context *ContextImpl) RefreshService(serviceName string) (*rest_model.Serv
793793 return serviceDetail , nil
794794}
795795
796- func (context * ContextImpl ) updateTokenOnAllErs (apiSession apis.ApiSession ) {
796+ // updateTokenOnAllErs synchronously propagates API session tokens to all connected edge routers.
797+ // Collects and returns any router update failures as a combined error rather than logging
798+ // them individually. This enables callers to determine if token propagation was successful
799+ // across the entire router connection pool.
800+ func (context * ContextImpl ) updateTokenOnAllErs (apiSession apis.ApiSession ) error {
801+ var routerErrors []error
797802 if apiSession .RequiresRouterTokenUpdate () {
798803 for tpl := range context .routerConnections .IterBuffered () {
799804 erConn := tpl .Val
800805 erKey := tpl .Key
801- go func () {
802- if err := erConn .UpdateToken (apiSession .GetToken (), 10 * time .Second ); err != nil {
803- pfxlog . Logger (). WithError ( err ). WithField ( "er" , erKey ). Warn ( "error updating apiSession token to connected ER" )
804- }
805- }()
806+
807+ if err := erConn .UpdateToken (apiSession .GetToken (), 10 * time .Second ); err != nil {
808+ routerError := fmt . Errorf ( "failed to update token on ER %s: %w" , erKey , err )
809+ routerErrors = append ( routerErrors , routerError )
810+ }
806811 }
807812 }
813+
814+ return errors .Join (routerErrors ... )
808815}
809816
810817func (context * ContextImpl ) runRefreshes () {
@@ -865,7 +872,11 @@ func (context *ContextImpl) runRefreshes() {
865872 refreshAt = exp .Add (- 10 * time .Second )
866873 log .Debugf ("apiSession refreshed, new expiration[%s]" , * exp )
867874
868- context .updateTokenOnAllErs (newApiSession )
875+ updateErr := context .updateTokenOnAllErs (newApiSession )
876+
877+ if updateErr != nil {
878+ pfxlog .Logger ().WithError (updateErr ).Warn ("error updating current api session token on edge routers" )
879+ }
869880 }
870881
871882 case <- svcRefreshTick .C :
@@ -998,6 +1009,87 @@ func (context *ContextImpl) Authenticate() error {
9981009 return context .authenticate ()
9991010}
10001011
1012+ // ConnectAllAvailableErs discovers and establishes connections to all edge routers
1013+ // accessible to the current identity. Filters routers by supported protocols and
1014+ // waits for connection attempts to complete. Returns any connection failures
1015+ // as a combined error.
1016+ func (context * ContextImpl ) ConnectAllAvailableErs () error {
1017+ if err := context .ensureApiSession (); err != nil {
1018+ return fmt .Errorf ("failed to establish api session (%w)" , err )
1019+ }
1020+
1021+ ers , err := context .CtrlClt .GetAvailableERs ()
1022+
1023+ if err != nil {
1024+ return fmt .Errorf ("failed to get available edge routers: %w" , err )
1025+ }
1026+
1027+ resultCh := make (chan * edgeRouterConnResult , len (ers ))
1028+
1029+ for _ , er := range ers {
1030+ for _ , addr := range er .SupportedProtocols {
1031+ isSupported := context .options .isEdgeRouterUrlAccepted (addr )
1032+
1033+ if isSupported {
1034+ addr = strings .Replace (addr , "//" , "" , 1 )
1035+ go context .handleConnectEdgeRouter (* er .Name , addr , resultCh )
1036+ }
1037+
1038+ }
1039+ }
1040+
1041+ expectedResults := len (ers )
1042+
1043+ var results []* edgeRouterConnResult
1044+ select {
1045+ case result := <- resultCh :
1046+ results = append (results , result )
1047+
1048+ if len (results ) == expectedResults {
1049+ break
1050+ }
1051+ case <- time .After (10 * time .Second ):
1052+ return fmt .Errorf ("timed out waiting for edge router connections got %d expected %d" , len (results ), expectedResults )
1053+ }
1054+
1055+ var resultErrs []error
1056+ for _ , result := range results {
1057+ if result .err != nil {
1058+ resultErrs = append (resultErrs , result .err )
1059+ }
1060+ }
1061+
1062+ return errors .Join (resultErrs ... )
1063+ }
1064+
1065+ // RefreshApiSession attempts to refresh the API session and propagate the new token
1066+ // to connected edge routers. Returns two separate error values: the first indicates
1067+ // functional refresh failures (session expired, authentication errors) that affect
1068+ // the refresh operation itself, while the second contains edge router token update
1069+ // failures that don't prevent the refresh from succeeding.
1070+ func (context * ContextImpl ) RefreshApiSession () (error , error ) {
1071+ newApiSession , err := context .CtrlClt .Refresh ()
1072+ if err == nil {
1073+ updateErrs := context .updateTokenOnAllErs (newApiSession )
1074+ return nil , updateErrs
1075+ }
1076+
1077+ unauthorizedErr := & current_api_session.GetCurrentAPISessionUnauthorized {}
1078+ if errors .As (err , & unauthorizedErr ) {
1079+ logrus .Info ("previous apiSession expired" )
1080+ return backoff .Permanent (err ), nil
1081+ }
1082+
1083+ oidcErr := & oidc.Error {}
1084+ if errors .As (err , & oidcErr ) {
1085+ logrus .Info ("oidc error, re-authenticating" )
1086+ return backoff .Permanent (err ), nil
1087+ }
1088+
1089+ logrus .WithError (err ).Infof ("unable to refresh apiSession, error type %T, will retry" , err )
1090+ return err , nil
1091+ }
1092+
10011093func (context * ContextImpl ) RefreshApiSessionWithBackoff () error {
10021094 expBackoff := backoff .NewExponentialBackOff ()
10031095
@@ -1006,26 +1098,12 @@ func (context *ContextImpl) RefreshApiSessionWithBackoff() error {
10061098 expBackoff .MaxElapsedTime = 24 * time .Hour
10071099
10081100 operation := func () error {
1009- newApiSession , err := context .CtrlClt .Refresh ()
1010- if err == nil {
1011- context .updateTokenOnAllErs (newApiSession )
1012- return nil
1101+ functionalErr , erUpdateErrs := context .RefreshApiSession ()
1102+ if erUpdateErrs != nil {
1103+ pfxlog .Logger ().WithError (erUpdateErrs ).Warn ("errors during api session token update on all connected edge routers" )
10131104 }
10141105
1015- unauthorizedErr := & current_api_session.GetCurrentAPISessionUnauthorized {}
1016- if errors .As (err , & unauthorizedErr ) {
1017- logrus .Info ("previous apiSession expired" )
1018- return backoff .Permanent (err )
1019- }
1020-
1021- oidcErr := & oidc.Error {}
1022- if errors .As (err , & oidcErr ) {
1023- logrus .Info ("oidc error, re-authenticating" )
1024- return backoff .Permanent (err )
1025- }
1026-
1027- logrus .WithError (err ).Infof ("unable to refresh apiSession, error type %T, will retry" , err )
1028- return err
1106+ return functionalErr
10291107 }
10301108
10311109 return backoff .Retry (operation , expBackoff )
@@ -1083,7 +1161,12 @@ func (context *ContextImpl) authenticateMfa(code string) error {
10831161 if err != nil {
10841162 return err
10851163 }
1086- context .updateTokenOnAllErs (newApiSession )
1164+
1165+ updateErr := context .updateTokenOnAllErs (newApiSession )
1166+
1167+ if updateErr != nil {
1168+ pfxlog .Logger ().WithError (updateErr ).Warn ("error updating current api session token on edge routers" )
1169+ }
10871170
10881171 apiSession := context .CtrlClt .GetCurrentApiSession ()
10891172
0 commit comments