@@ -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