2022-10-18 13:56:23 -07:00
// Copyright (c) HashiCorp, Inc.
2022-07-07 12:13:17 -04:00
// SPDX-License-Identifier: BUSL-1.1
2023-02-02 17:15:17 -08:00
2020-08-12 00:40:39 -04:00
package worker
import (
"context"
2021-07-28 06:31:16 -04:00
"errors"
2022-06-09 16:04:13 -04:00
"fmt"
2020-08-12 00:40:39 -04:00
"math/rand"
"time"
2022-05-09 12:45:10 -07:00
"github.com/hashicorp/boundary/internal/daemon/worker/common"
"github.com/hashicorp/boundary/internal/daemon/worker/session"
2023-09-12 08:15:34 -04:00
"github.com/hashicorp/boundary/internal/event"
2022-08-12 11:08:25 -04:00
pb "github.com/hashicorp/boundary/internal/gen/controller/servers"
2020-09-12 21:54:18 -07:00
pbs "github.com/hashicorp/boundary/internal/gen/controller/servers/services"
2022-09-12 16:10:03 -04:00
"github.com/hashicorp/boundary/internal/server"
2024-06-20 10:56:55 -04:00
"github.com/hashicorp/boundary/sdk/pbs/plugin"
2022-09-12 16:10:03 -04:00
"github.com/hashicorp/boundary/version"
2023-09-12 13:20:49 -04:00
"github.com/hashicorp/go-secure-stdlib/parseutil"
2022-06-09 16:42:56 -04:00
"github.com/hashicorp/go-secure-stdlib/strutil"
2022-12-12 13:48:28 -05:00
"google.golang.org/grpc/connectivity"
2020-08-12 00:40:39 -04:00
)
2022-08-03 19:29:17 -04:00
var firstStatusCheckPostHooks [] func ( context . Context , * Worker ) error
2023-01-10 08:10:54 -05:00
var downstreamWorkersFactory func ( ctx context . Context , workerId string , ver string ) ( downstreamers , error )
2022-08-12 11:08:25 -04:00
2023-01-31 15:53:11 -05:00
var checkHCPBUpstreams func ( w * Worker ) bool
2020-09-03 13:24:34 -04:00
type LastStatusInformation struct {
* pbs . StatusResponse
2022-06-09 16:04:13 -04:00
StatusTime time . Time
2022-06-09 16:39:20 -04:00
LastCalculatedUpstreams [] string
2020-09-03 13:24:34 -04:00
}
2023-04-28 12:24:42 -07:00
func ( w * Worker ) startStatusTicking ( cancelCtx context . Context , sessionManager session . Manager , addrReceivers * [] addressReceiver , recorderManager recorderManager ) {
2021-07-28 06:31:16 -04:00
const op = "worker.(Worker).startStatusTicking"
r := rand . New ( rand . NewSource ( time . Now (). UnixNano ()))
// This function exists to desynchronize calls to controllers from
2021-08-18 15:30:55 +02:00
// workers, so we aren't always getting status updates at the exact same
2021-07-28 06:31:16 -04:00
// intervals, to ease the load on the DB.
getRandomInterval := func () time . Duration {
// 0 to 0.5 adjustment to the base
f := r . Float64 () / 2
// Half a chance to be faster, not slower
if r . Float32 () > 0.5 {
f = - 1 * f
2020-08-12 00:40:39 -04:00
}
2021-08-18 15:30:55 +02:00
return common . StatusInterval + time . Duration ( f * float64 ( time . Second ))
2021-07-28 06:31:16 -04:00
}
2020-08-12 00:40:39 -04:00
2021-07-28 06:31:16 -04:00
timer := time . NewTimer ( 0 )
for {
select {
case <- cancelCtx . Done ():
2021-08-18 15:30:55 +02:00
event . WriteSysEvent ( w . baseContext , op , "status ticking shutting down" )
2021-07-28 06:31:16 -04:00
return
2020-08-12 00:40:39 -04:00
2021-07-28 06:31:16 -04:00
case <- timer . C :
2022-07-07 12:13:17 -04:00
// If we've never managed to successfully authenticate then we won't have
// any session information anyways and this will produce a ton of noise in
// observability, so skip calling the function and retry in a short duration
2022-08-03 19:29:17 -04:00
if w . everAuthenticated . Load () == authenticationStatusNeverAuthenticated {
2022-07-07 12:13:17 -04:00
timer . Reset ( 10 * time . Millisecond )
continue
}
2023-04-28 12:24:42 -07:00
w . sendWorkerStatus ( cancelCtx , sessionManager , addrReceivers , recorderManager )
2021-07-28 06:31:16 -04:00
timer . Reset ( getRandomInterval ())
2021-01-21 16:27:20 -05:00
}
2021-07-28 06:31:16 -04:00
}
2021-01-21 16:27:20 -05:00
}
2021-06-28 17:32:13 -07:00
// LastStatusSuccess reports the last time we sent a successful
// status request.
2021-01-21 16:27:20 -05:00
func ( w * Worker ) LastStatusSuccess () * LastStatusInformation {
2022-08-12 11:08:25 -04:00
s , ok := w . lastStatusSuccess . Load ().( * LastStatusInformation )
if ! ok {
return nil
}
return s
2021-01-21 16:27:20 -05:00
}
2021-07-14 09:14:24 -07:00
// WaitForNextSuccessfulStatusUpdate waits for the next successful status. It's
// used by testing (and in the future, shutdown) in place of a more opaque and
// possibly unnecessarily long sleep for things like initial controller
// check-in, etc.
//
// The timeout is aligned with the worker's status grace period. A nil error
// means the status was sent successfully.
func ( w * Worker ) WaitForNextSuccessfulStatusUpdate () error {
2021-07-28 06:31:16 -04:00
const op = "worker.(Worker).WaitForNextSuccessfulStatusUpdate"
2021-07-14 09:14:24 -07:00
waitStatusStart := time . Now ()
2022-11-10 13:14:57 -05:00
ctx , cancel := context . WithTimeout ( w . baseContext , time . Duration ( w . successfulStatusGracePeriod . Load ()))
2021-07-14 09:14:24 -07:00
defer cancel ()
2021-07-28 06:31:16 -04:00
event . WriteSysEvent ( ctx , op , "waiting for next status report to controller" )
2021-07-14 09:14:24 -07:00
for {
select {
case <- time . After ( time . Second ):
// pass
case <- ctx . Done ():
2021-07-28 06:31:16 -04:00
event . WriteError ( ctx , op , ctx . Err (), event . WithInfoMsg ( "error waiting for next status report to controller" ))
2021-07-14 09:14:24 -07:00
return ctx . Err ()
}
2024-08-27 11:00:04 -07:00
if w . lastSuccessfulStatusTime (). After ( waitStatusStart ) {
2021-07-14 09:14:24 -07:00
break
}
}
2021-07-28 06:31:16 -04:00
event . WriteSysEvent ( ctx , op , "next worker status update sent successfully" )
2021-07-14 09:14:24 -07:00
return nil
}
2023-04-28 12:24:42 -07:00
func ( w * Worker ) sendWorkerStatus ( cancelCtx context . Context , sessionManager session . Manager , addressReceivers * [] addressReceiver , recorderManager recorderManager ) {
2021-07-28 06:31:16 -04:00
const op = "worker.(Worker).sendWorkerStatus"
2022-09-01 14:13:42 -04:00
w . statusLock . Lock ()
defer w . statusLock . Unlock ()
2022-04-27 16:25:05 -04:00
2023-04-28 12:24:42 -07:00
// Collect the different session ids that are being monitored by this worker
var monitoredSessionIds [] string
if recorderManager != nil {
recSessIds , err := recorderManager . SessionsManaged ( cancelCtx )
if err != nil {
event . WriteError ( cancelCtx , op , err , event . WithInfoMsg ( "error getting session ids from recorderManager" ))
} else {
monitoredSessionIds = append ( monitoredSessionIds , recSessIds ... )
}
}
2021-01-21 16:27:20 -05:00
// First send info as-is. We'll perform cleanup duties after we
// get cancel/job change info back.
var activeJobs [] * pbs . JobStatus
2023-04-28 12:24:42 -07:00
for _ , sid := range monitoredSessionIds {
activeJobs = append ( activeJobs , & pbs . JobStatus {
Job : & pbs . Job {
Type : pbs . JOBTYPE_JOBTYPE_MONITOR_SESSION ,
JobInfo : & pbs . Job_MonitorSessionInfo {
MonitorSessionInfo : & pbs . MonitorSessionJobInfo {
SessionId : sid ,
Status : pbs . SESSIONSTATUS_SESSIONSTATUS_ACTIVE ,
},
},
},
})
}
2021-01-21 16:27:20 -05:00
// Range over known sessions and collect info
2022-07-06 13:37:49 -07:00
sessionManager . ForEachLocalSession ( func ( s session . Session ) bool {
2021-01-21 16:27:20 -05:00
var jobInfo pbs . SessionJobInfo
2022-07-06 13:37:49 -07:00
status := s . GetStatus ()
sessionId := s . GetId ()
2022-11-10 13:14:57 -05:00
localConnections := s . GetLocalConnections ()
connections := make ([] * pbs . Connection , 0 , len ( localConnections ))
for k , v := range localConnections {
2021-01-21 16:27:20 -05:00
connections = append ( connections , & pbs . Connection {
ConnectionId : k ,
2021-08-18 15:30:55 +02:00
Status : v . Status ,
2022-10-06 16:52:26 +01:00
BytesUp : v . BytesUp (),
BytesDown : v . BytesDown (),
2021-01-21 16:27:20 -05:00
})
}
jobInfo . SessionId = sessionId
activeJobs = append ( activeJobs , & pbs . JobStatus {
Job : & pbs . Job {
Type : pbs . JOBTYPE_JOBTYPE_SESSION ,
JobInfo : & pbs . Job_SessionInfo {
SessionInfo : & pbs . SessionJobInfo {
SessionId : sessionId ,
Status : status ,
Connections : connections ,
2020-09-03 13:24:34 -04:00
},
2021-01-21 16:27:20 -05:00
},
},
})
return true
})
2020-09-03 13:24:34 -04:00
2024-09-30 13:51:55 -05:00
clientCon := w . GrpcClientConn . Load ()
2021-01-21 16:27:20 -05:00
// Send status information
2024-09-30 13:51:55 -05:00
client := pbs . NewServerCoordinationServiceClient ( clientCon )
2022-06-23 12:16:16 -07:00
var tags [] * pb . TagPair
2021-01-21 16:27:20 -05:00
// If we're not going to request a tag update, no reason to have these
// marshaled on every status call.
if w . updateTags . Load () {
2022-06-23 12:16:16 -07:00
tags = w . tags . Load ().([] * pb . TagPair )
2021-01-21 16:27:20 -05:00
}
2022-11-10 13:14:57 -05:00
statusCtx , statusCancel := context . WithTimeout ( cancelCtx , time . Duration ( w . statusCallTimeoutDuration . Load ()))
2021-06-28 17:32:13 -07:00
defer statusCancel ()
2022-05-02 10:32:33 -04:00
2022-06-07 16:43:07 -04:00
keyId := w . WorkerAuthCurrentKeyId . Load ()
2023-03-30 16:20:28 -04:00
switch {
case w . conf . RawConfig . Worker . Name == "" && keyId == "" :
event . WriteError ( cancelCtx , op , errors . New ( "worker name and keyId are both empty; at least one is needed to identify a worker" ),
2022-06-06 14:25:14 -04:00
event . WithInfoMsg ( "error making status request to controller" ))
}
2024-06-20 10:56:55 -04:00
var storageBucketCredentialStates map [ string ] * plugin . StorageBucketCredentialState
if w . RecordingStorage != nil {
storageBucketCredentialStates = w . RecordingStorage . GetStorageBucketCredentialStates ()
// If the local storage state is unknown, and we have recording storage set, get the state from the recording storage
// and set it on the worker. This is done once to ensure that the worker has the correct state for the first status
// call.
if w . localStorageState . Load () == server . UnknownLocalStorageState {
w . localStorageState . Store ( w . RecordingStorage . GetLocalStorageState ( cancelCtx ))
}
2024-02-20 11:36:18 -05:00
}
2022-09-08 13:19:21 -04:00
versionInfo := version . Get ()
2024-01-12 11:58:12 -05:00
connectionState := w . downstreamConnManager . Connected ()
2021-06-28 17:32:13 -07:00
result , err := client . Status ( statusCtx , & pbs . StatusRequest {
2021-01-21 16:27:20 -05:00
Jobs : activeJobs ,
2022-06-23 12:16:16 -07:00
WorkerStatus : & pb . ServerWorkerStatus {
2024-06-20 10:56:55 -04:00
Name : w . conf . RawConfig . Worker . Name ,
Description : w . conf . RawConfig . Worker . Description ,
Address : w . conf . RawConfig . Worker . PublicAddr ,
Tags : tags ,
KeyId : keyId ,
ReleaseVersion : versionInfo . FullVersionNumber ( false ),
OperationalState : w . operationalState . Load ().( server . OperationalState ). String (),
LocalStorageState : w . localStorageState . Load ().( server . LocalStorageState ). String (),
StorageBucketCredentialStates : storageBucketCredentialStates ,
2021-01-21 16:27:20 -05:00
},
2023-02-13 13:50:08 -08:00
ConnectedWorkerKeyIdentifiers : connectionState . AllKeyIds (),
ConnectedUnmappedWorkerKeyIdentifiers : connectionState . UnmappedKeyIds (),
ConnectedWorkerPublicIds : connectionState . WorkerIds (),
UpdateTags : w . updateTags . Load (),
2021-01-21 16:27:20 -05:00
})
if err != nil {
2024-09-30 13:51:55 -05:00
event . WriteError ( cancelCtx , op , err , event . WithInfoMsg ( "error making status request to controller" , "controller_address" , clientCon . Target ()))
2021-06-28 17:32:13 -07:00
// Check for last successful status. Ignore nil last status, this probably
// means that we've never connected to a controller, and as such probably
// don't have any sessions to worry about anyway.
//
// If a length of time has passed since we've been able to communicate, we
2022-03-09 13:16:52 -05:00
// want to start terminating all connections as a "break glass" kind of
2021-06-28 17:32:13 -07:00
// scenario, as there will be no way we can really tell if these
// connections should continue to exist.
if isPastGrace , lastStatusTime , gracePeriod := w . isPastGrace (); isPastGrace {
2022-10-18 13:56:23 -07:00
event . WriteError ( cancelCtx , op ,
2021-07-28 06:31:16 -04:00
errors . New ( "status error grace period has expired, canceling all sessions on worker" ),
event . WithInfo ( "last_status_time" , lastStatusTime . String (), "grace_period" , gracePeriod ),
2021-06-28 17:32:13 -07:00
)
2022-03-09 13:16:52 -05:00
// Cancel connections if grace period has expired. These Connections will be closed in the
// database on the next successful status report, or via the Controller’ s dead Worker cleanup connections job.
2022-07-06 13:37:49 -07:00
sessionManager . ForEachLocalSession ( func ( s session . Session ) bool {
for _ , connId := range s . CancelAllLocalConnections () {
event . WriteSysEvent ( cancelCtx , op , "terminated connection due to status grace period expiration" , "session_id" , s . GetId (), "connection_id" , connId )
2022-03-09 13:16:52 -05:00
}
return true
})
2022-12-12 13:48:28 -05:00
// In the case that the control plane has gone down and come up with different IPs,
// append initial upstreams/ cluster addr to the resolver to try
2024-02-21 16:32:26 -05:00
if w . GrpcClientConn . Load (). GetState () == connectivity . TransientFailure {
2022-12-12 13:48:28 -05:00
lastStatus := w . lastStatusSuccess . Load ().( * LastStatusInformation )
2023-01-12 12:02:08 -05:00
if lastStatus != nil && lastStatus . LastCalculatedUpstreams != nil {
addrs := lastStatus . LastCalculatedUpstreams
if len ( w . conf . RawConfig . Worker . InitialUpstreams ) > 0 {
addrs = append ( addrs , w . conf . RawConfig . Worker . InitialUpstreams ... )
2023-02-01 13:20:07 -05:00
} else if HandleHcpbClusterId != nil && len ( w . conf . RawConfig . HcpbClusterId ) > 0 {
2023-09-12 13:20:49 -04:00
clusterId , err := parseutil . ParsePath ( w . conf . RawConfig . HcpbClusterId )
if err != nil && ! errors . Is ( err , parseutil . ErrNotAUrl ) {
event . WriteError ( cancelCtx , op , err , event . WithInfoMsg ( "failed to parse HCP Boundary cluster ID" ))
} else {
clusterAddress := HandleHcpbClusterId ( clusterId )
addrs = append ( addrs , clusterAddress )
}
2023-01-12 12:02:08 -05:00
}
addrs = strutil . RemoveDuplicates ( addrs , false )
if strutil . EquivalentSlices ( lastStatus . LastCalculatedUpstreams , addrs ) {
// Nothing to update
return
}
w . updateAddresses ( cancelCtx , addrs , addressReceivers )
lastStatus . LastCalculatedUpstreams = addrs
w . lastStatusSuccess . Store ( lastStatus )
2022-12-12 13:48:28 -05:00
}
}
2022-03-09 13:16:52 -05:00
// Exit out of status function; our work here is done and we don't need to create closeConnection requests
return
2021-06-28 17:32:13 -07:00
}
2022-08-03 19:29:17 -04:00
// Standard cleanup: Run through current jobs. Cancel connections
// for any canceling session or any session that is expired.
w . cleanupConnections ( cancelCtx , false , sessionManager )
return
}
w . updateTags . Store ( false )
2022-11-03 14:50:48 -07:00
2023-02-13 13:50:08 -08:00
if authorized := result . GetAuthorizedDownstreamWorkers (); authorized != nil {
2023-02-10 15:26:27 -08:00
connectionState . DisconnectMissingWorkers ( authorized . GetWorkerPublicIds ())
2023-02-13 13:50:08 -08:00
connectionState . DisconnectMissingUnmappedKeyIds ( authorized . GetUnmappedWorkerKeyIdentifiers ())
} else if authorized := result . GetAuthorizedWorkers (); authorized != nil {
connectionState . DisconnectAllMissingKeyIds ( authorized . GetWorkerKeyIdentifiers ())
2022-11-03 14:50:48 -07:00
}
2022-08-03 19:29:17 -04:00
var addrs [] string
// This may be empty if we are in a multiple hop scenario
if len ( result . CalculatedUpstreams ) > 0 {
addrs = make ([] string , 0 , len ( result . CalculatedUpstreams ))
for _ , v := range result . CalculatedUpstreams {
addrs = append ( addrs , v . Address )
}
2023-01-31 15:53:11 -05:00
} else if checkHCPBUpstreams != nil && checkHCPBUpstreams ( w ) {
2022-08-03 19:29:17 -04:00
// This is a worker that is one hop away from managed workers, so attempt to get that list
2022-11-10 13:14:57 -05:00
hcpbWorkersCtx , hcpbWorkersCancel := context . WithTimeout ( cancelCtx , time . Duration ( w . statusCallTimeoutDuration . Load ()))
2022-08-03 19:29:17 -04:00
defer hcpbWorkersCancel ()
workersResp , err := client . ListHcpbWorkers ( hcpbWorkersCtx , & pbs . ListHcpbWorkersRequest {})
if err != nil {
event . WriteError ( hcpbWorkersCtx , op , err , event . WithInfoMsg ( "error fetching managed worker information" ))
} else {
addrs = make ([] string , 0 , len ( workersResp . Workers ))
for _ , v := range workersResp . Workers {
2022-07-25 12:52:21 -07:00
addrs = append ( addrs , v . Address )
2022-06-01 01:09:56 -04:00
}
2022-08-03 19:29:17 -04:00
}
}
2022-07-07 19:02:16 -04:00
2022-12-12 13:48:28 -05:00
w . updateAddresses ( cancelCtx , addrs , addressReceivers )
2022-08-12 11:08:25 -04:00
2022-08-03 19:29:17 -04:00
w . lastStatusSuccess . Store ( & LastStatusInformation { StatusResponse : result , StatusTime : time . Now (), LastCalculatedUpstreams : addrs })
2023-04-28 12:24:42 -07:00
var nonActiveMonitoredSessionIds [] string
2022-08-03 19:29:17 -04:00
for _ , request := range result . GetJobsRequests () {
switch request . GetRequestType () {
case pbs . CHANGETYPE_CHANGETYPE_UPDATE_STATE :
switch request . GetJob (). GetType () {
2023-04-28 12:24:42 -07:00
case pbs . JOBTYPE_JOBTYPE_MONITOR_SESSION :
si := request . GetJob (). GetMonitorSessionInfo ()
if si != nil && si . Status != pbs . SESSIONSTATUS_SESSIONSTATUS_ACTIVE {
nonActiveMonitoredSessionIds = append ( nonActiveMonitoredSessionIds , si . GetSessionId ())
}
2022-08-03 19:29:17 -04:00
case pbs . JOBTYPE_JOBTYPE_SESSION :
sessInfo := request . GetJob (). GetSessionInfo ()
sessionId := sessInfo . GetSessionId ()
si := sessionManager . Get ( sessionId )
if si == nil {
2022-10-18 13:56:23 -07:00
event . WriteError ( cancelCtx , op , errors . New ( "session change requested but could not find local information for it" ), event . WithInfo ( "session_id" , sessionId ))
2022-08-03 19:29:17 -04:00
continue
}
si . ApplyLocalStatus ( sessInfo . GetStatus ())
// Update connection state if there are any connections in
// the request.
for _ , conn := range sessInfo . GetConnections () {
if err := si . ApplyLocalConnectionStatus ( conn . GetConnectionId (), conn . GetStatus ()); err != nil {
2022-10-18 13:56:23 -07:00
event . WriteError ( cancelCtx , op , err , event . WithInfo ( "connection_id" , conn . GetConnectionId ()))
2021-07-14 09:14:24 -07:00
}
2020-09-21 02:22:59 -04:00
}
2021-01-21 16:27:20 -05:00
}
}
}
2023-04-28 12:24:42 -07:00
if recorderManager != nil {
if err := recorderManager . ReauthorizeAllExcept ( cancelCtx , nonActiveMonitoredSessionIds ); err != nil {
event . WriteError ( cancelCtx , op , err )
}
}
2020-09-21 02:22:59 -04:00
2021-06-28 17:32:13 -07:00
// Standard cleanup: Run through current jobs. Cancel connections
// for any canceling session or any session that is expired.
2022-07-06 13:37:49 -07:00
w . cleanupConnections ( cancelCtx , false , sessionManager )
2022-08-03 19:29:17 -04:00
// If we have post hooks for after the first status check, run them now
2024-02-13 10:54:48 -05:00
if w . everAuthenticated . CompareAndSwap ( authenticationStatusFirstAuthentication , authenticationStatusFirstStatusRpcSuccessful ) {
2022-08-12 11:08:25 -04:00
if downstreamWorkersFactory != nil {
2024-03-07 14:46:10 -05:00
downstreamWorkers , err := downstreamWorkersFactory ( cancelCtx , w . LastStatusSuccess (). WorkerId , versionInfo . FullVersionNumber ( false ))
2022-08-12 11:08:25 -04:00
if err != nil {
event . WriteError ( cancelCtx , op , err )
w . conf . ServerSideShutdownCh <- struct {}{}
return
}
2024-03-07 14:46:10 -05:00
w . downstreamWorkers . Store ( & downstreamersContainer { downstreamers : downstreamWorkers })
2022-08-12 11:08:25 -04:00
}
2022-08-03 19:29:17 -04:00
for _ , fn := range firstStatusCheckPostHooks {
if err := fn ( cancelCtx , w ); err != nil {
// If we can't verify status we can't be expected to behave
// properly so error and trigger shutdown
event . WriteError ( cancelCtx , op , fmt . Errorf ( "error running first status check post hook: %w" , err ))
// We don't use a non-blocking select here to ensure that it
// happens; we should catch blocks in tests but we want to
// ensure the signal is being listened to
w . conf . ServerSideShutdownCh <- struct {}{}
return
}
}
}
2021-06-28 17:32:13 -07:00
}
2022-12-12 13:48:28 -05:00
// Update address receivers and dialing listeners with new addrs
func ( w * Worker ) updateAddresses ( cancelCtx context . Context , addrs [] string , addressReceivers * [] addressReceiver ) {
const op = "worker.(Worker).updateAddrs"
if len ( addrs ) > 0 {
lastStatus := w . lastStatusSuccess . Load ().( * LastStatusInformation )
// Compare upstreams; update resolver if there is a difference, and emit an event with old and new addresses
if lastStatus != nil && ! strutil . EquivalentSlices ( lastStatus . LastCalculatedUpstreams , addrs ) {
upstreamsMessage := fmt . Sprintf ( "Upstreams has changed; old upstreams were: %s, new upstreams are: %s" , lastStatus . LastCalculatedUpstreams , addrs )
event . WriteSysEvent ( cancelCtx , op , upstreamsMessage )
for _ , as := range * addressReceivers {
as . SetAddresses ( addrs )
}
} else if lastStatus == nil {
for _ , as := range * addressReceivers {
as . SetAddresses ( addrs )
}
event . WriteSysEvent ( cancelCtx , op , fmt . Sprintf ( "Upstreams after first status set to: %s" , addrs ))
}
}
2022-12-13 14:25:40 -05:00
// regardless of whether or not it's a new address, we need to set
2023-01-10 08:10:54 -05:00
// them for secondary connections
2022-12-13 14:25:40 -05:00
for _ , as := range * addressReceivers {
switch {
2023-01-10 08:10:54 -05:00
case as . Type () == secondaryConnectionReceiverType :
2022-12-13 14:25:40 -05:00
tmpAddrs := make ([] string , len ( addrs ))
copy ( tmpAddrs , addrs )
if len ( tmpAddrs ) == 0 {
tmpAddrs = append ( tmpAddrs , w . conf . RawConfig . Worker . InitialUpstreams ... )
}
as . SetAddresses ( tmpAddrs )
}
}
2022-12-12 13:48:28 -05:00
}
2022-07-06 13:37:49 -07:00
// cleanupConnections walks all sessions and shuts down all proxy connections.
// After the local connections are terminated, they are requested to be marked
// close on the controller.
2021-06-28 17:32:13 -07:00
// Additionally, sessions without connections are cleaned up from the
// local worker's state.
//
// Use ignoreSessionState to ignore the state checks, this closes all
2022-07-06 13:37:49 -07:00
// connections, regardless of whether or not the session is still active.
func ( w * Worker ) cleanupConnections ( cancelCtx context . Context , ignoreSessionState bool , sessionManager session . Manager ) {
2021-07-28 06:31:16 -04:00
const op = "worker.(Worker).cleanupConnections"
2022-10-05 13:20:35 +01:00
closeInfo := make ( map [ string ] * session . ConnectionCloseData )
2021-01-21 16:27:20 -05:00
cleanSessionIds := make ([] string , 0 )
2022-07-06 13:37:49 -07:00
sessionManager . ForEachLocalSession ( func ( s session . Session ) bool {
2021-01-21 16:27:20 -05:00
switch {
2021-06-28 17:32:13 -07:00
case ignoreSessionState ,
2022-07-06 13:37:49 -07:00
s . GetStatus () == pbs . SESSIONSTATUS_SESSIONSTATUS_CANCELING ,
s . GetStatus () == pbs . SESSIONSTATUS_SESSIONSTATUS_TERMINATED ,
time . Until ( s . GetExpiration ()) < 0 :
2021-07-14 09:14:24 -07:00
// Cancel connections without regard to individual connection
// state.
2022-07-06 13:37:49 -07:00
closedIds := s . CancelAllLocalConnections ()
2022-10-05 13:20:35 +01:00
localConns := s . GetLocalConnections ()
2021-07-14 09:14:24 -07:00
for _ , connId := range closedIds {
2022-10-06 17:37:50 +01:00
var bytesUp , bytesDown int64
2022-10-05 13:20:35 +01:00
connInfo , ok := localConns [ connId ]
if ok {
bytesUp = connInfo . BytesUp ()
bytesDown = connInfo . BytesDown ()
}
closeInfo [ connId ] = & session . ConnectionCloseData {
SessionId : s . GetId (),
BytesUp : bytesUp ,
BytesDown : bytesDown ,
}
2022-07-06 13:37:49 -07:00
event . WriteSysEvent ( cancelCtx , op , "terminated connection due to cancellation or expiration" , "session_id" , s . GetId (), "connection_id" , connId )
2021-01-21 16:27:20 -05:00
}
2021-07-14 09:14:24 -07:00
2022-07-06 13:37:49 -07:00
// If the session is no longer valid and all connections
2021-01-21 16:27:20 -05:00
// are marked closed, clean up the session.
2021-07-14 09:14:24 -07:00
if len ( closedIds ) == 0 {
2022-07-06 13:37:49 -07:00
cleanSessionIds = append ( cleanSessionIds , s . GetId ())
2020-08-12 00:40:39 -04:00
}
2021-07-14 09:14:24 -07:00
default :
// Cancel connections *with* regard to individual connection
// state (ie: only ones that the controller has requested be
// terminated).
2022-07-06 13:37:49 -07:00
for _ , connId := range s . CancelOpenLocalConnections () {
2022-10-05 13:20:35 +01:00
closeInfo [ connId ] = & session . ConnectionCloseData { SessionId : s . GetId ()}
2022-07-06 13:37:49 -07:00
event . WriteSysEvent ( cancelCtx , op , "terminated connection due to cancellation or expiration" , "session_id" , s . GetId (), "connection_id" , connId )
2021-07-14 09:14:24 -07:00
}
2020-08-12 00:40:39 -04:00
}
2021-06-28 17:32:13 -07:00
2021-01-21 16:27:20 -05:00
return true
})
2020-08-12 00:40:39 -04:00
2021-01-21 16:27:20 -05:00
// Note that we won't clean these from the info map until the
// next time we run this function
if len ( closeInfo ) > 0 {
2021-06-28 17:32:13 -07:00
// Call out to a helper to send the connection close requests to the
// controller, and set the close time. This functionality is shared with
// post-close functionality in the proxy handler.
2022-07-06 13:37:49 -07:00
_ = sessionManager . RequestCloseConnections ( cancelCtx , closeInfo )
2021-01-21 16:27:20 -05:00
}
// Forget sessions where the session is expired/canceled and all
// connections are canceled and marked closed
2022-07-06 13:37:49 -07:00
sessionManager . DeleteLocalSession ( cleanSessionIds )
2021-07-14 09:14:24 -07:00
}
2021-06-28 17:32:13 -07:00
func ( w * Worker ) lastSuccessfulStatusTime () time . Time {
lastStatus := w . LastStatusSuccess ()
if lastStatus == nil {
return w . workerStartTime
}
return lastStatus . StatusTime
}
func ( w * Worker ) isPastGrace () ( bool , time . Time , time . Duration ) {
t := w . lastSuccessfulStatusTime ()
2022-11-10 13:14:57 -05:00
u := time . Duration ( w . successfulStatusGracePeriod . Load ())
2021-06-28 17:32:13 -07:00
v := time . Since ( t )
return v > u , t , u
}