mirror of
https://github.com/netbirdio/netbird.git
synced 2026-04-25 11:46:40 +00:00
Fix deadlock issues
This commit is contained in:
@@ -244,7 +244,6 @@ func NewEngine(
|
|||||||
statusRecorder: statusRecorder,
|
statusRecorder: statusRecorder,
|
||||||
checks: checks,
|
checks: checks,
|
||||||
connSemaphore: semaphoregroup.NewSemaphoreGroup(connInitLimit),
|
connSemaphore: semaphoregroup.NewSemaphoreGroup(connInitLimit),
|
||||||
updateManager: updatemanager.NewUpdateManager(clientCtx, statusRecorder),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sm := profilemanager.NewServiceManager("")
|
sm := profilemanager.NewServiceManager("")
|
||||||
@@ -705,7 +704,14 @@ func (e *Engine) handleSync(update *mgmProto.SyncResponse) error {
|
|||||||
e.syncMsgMux.Lock()
|
e.syncMsgMux.Lock()
|
||||||
defer e.syncMsgMux.Unlock()
|
defer e.syncMsgMux.Unlock()
|
||||||
|
|
||||||
if update.GetAutoUpdateVersion() != "skip" {
|
if e.updateManager == nil && update.GetAutoUpdateVersion() != "disabled" {
|
||||||
|
e.updateManager = updatemanager.NewUpdateManager(e.statusRecorder)
|
||||||
|
e.updateManager.Start(e.ctx)
|
||||||
|
} else if e.updateManager != nil && update.GetAutoUpdateVersion() == "disabled" {
|
||||||
|
e.updateManager.Stop()
|
||||||
|
e.updateManager = nil
|
||||||
|
}
|
||||||
|
if e.updateManager != nil {
|
||||||
e.updateManager.SetVersion(update.GetAutoUpdateVersion())
|
e.updateManager.SetVersion(update.GetAutoUpdateVersion())
|
||||||
}
|
}
|
||||||
if update.GetNetbirdConfig() != nil {
|
if update.GetNetbirdConfig() != nil {
|
||||||
|
|||||||
@@ -22,113 +22,134 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
latestVersion = "latest"
|
latestVersion = "latest"
|
||||||
disableAutoUpdate = "disabled"
|
|
||||||
unknownVersion = "Unknown"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type UpdateManager struct {
|
type UpdateManager struct {
|
||||||
ctx context.Context
|
|
||||||
cancel context.CancelFunc
|
|
||||||
version string
|
|
||||||
latestVersion string
|
|
||||||
update *version.Update
|
|
||||||
lastTrigger time.Time
|
lastTrigger time.Time
|
||||||
statusRecorder *peer.Status
|
statusRecorder *peer.Status
|
||||||
mutex sync.Mutex
|
mgmUpdateChan chan struct{}
|
||||||
updateChannel chan string
|
updateChannel chan struct{}
|
||||||
doneChannel chan struct{}
|
wg sync.WaitGroup
|
||||||
|
|
||||||
|
cancel context.CancelFunc
|
||||||
|
update *version.Update
|
||||||
|
|
||||||
|
expectedVersion string
|
||||||
|
expectedVersionMutex sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewUpdateManager(ctx context.Context, statusRecorder *peer.Status) *UpdateManager {
|
func NewUpdateManager(statusRecorder *peer.Status) *UpdateManager {
|
||||||
update := version.NewUpdate("nb/client")
|
|
||||||
ctx, cancel := context.WithCancel(ctx)
|
|
||||||
manager := &UpdateManager{
|
manager := &UpdateManager{
|
||||||
update: update,
|
|
||||||
lastTrigger: time.Now().Add(-10 * time.Minute),
|
|
||||||
statusRecorder: statusRecorder,
|
statusRecorder: statusRecorder,
|
||||||
ctx: ctx,
|
mgmUpdateChan: make(chan struct{}, 1),
|
||||||
cancel: cancel,
|
updateChannel: make(chan struct{}, 1),
|
||||||
version: disableAutoUpdate,
|
|
||||||
latestVersion: unknownVersion,
|
|
||||||
updateChannel: make(chan string, 4),
|
|
||||||
doneChannel: make(chan struct{}),
|
|
||||||
}
|
}
|
||||||
update.SetDaemonVersion(version.NetbirdVersion())
|
|
||||||
update.SetOnUpdateChannel(manager.updateChannel)
|
|
||||||
go manager.UpdateLoop()
|
|
||||||
return manager
|
return manager
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *UpdateManager) Start(ctx context.Context) {
|
||||||
|
if u.cancel != nil {
|
||||||
|
log.Errorf("UpdateManager already started")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
u.update = version.NewUpdate("nb/client")
|
||||||
|
u.update.SetDaemonVersion(version.NetbirdVersion())
|
||||||
|
u.update.SetOnUpdateListener(func() {
|
||||||
|
select {
|
||||||
|
case u.updateChannel <- struct{}{}:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
|
u.cancel = cancel
|
||||||
|
|
||||||
|
u.wg.Add(1)
|
||||||
|
go u.updateLoop(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
func (u *UpdateManager) SetVersion(v string) {
|
func (u *UpdateManager) SetVersion(v string) {
|
||||||
u.mutex.Lock()
|
if u.cancel == nil {
|
||||||
if u.version != v {
|
log.Errorf("UpdateManager not started")
|
||||||
log.Tracef("Auto-update version set to %s", v)
|
return
|
||||||
u.version = v
|
}
|
||||||
u.mutex.Unlock()
|
|
||||||
u.updateChannel <- unknownVersion
|
u.expectedVersionMutex.Lock()
|
||||||
} else {
|
defer u.expectedVersionMutex.Unlock()
|
||||||
u.mutex.Unlock()
|
if u.expectedVersion == v {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
u.expectedVersion = v
|
||||||
|
|
||||||
|
select {
|
||||||
|
case u.mgmUpdateChan <- struct{}{}:
|
||||||
|
default:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *UpdateManager) Stop() {
|
func (u *UpdateManager) Stop() {
|
||||||
|
if u.cancel == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
u.cancel()
|
u.cancel()
|
||||||
u.mutex.Lock()
|
|
||||||
defer u.mutex.Unlock()
|
|
||||||
if u.update != nil {
|
if u.update != nil {
|
||||||
u.update.StopWatch()
|
u.update.StopWatch()
|
||||||
u.update = nil
|
u.update = nil
|
||||||
}
|
}
|
||||||
<-u.doneChannel
|
|
||||||
|
u.wg.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *UpdateManager) UpdateLoop() {
|
func (u *UpdateManager) updateLoop(ctx context.Context) {
|
||||||
|
defer u.wg.Done()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-u.ctx.Done():
|
case <-ctx.Done():
|
||||||
u.doneChannel <- struct{}{}
|
|
||||||
return
|
return
|
||||||
case latestVersion := <-u.updateChannel:
|
case <-u.mgmUpdateChan:
|
||||||
u.mutex.Lock()
|
case <-u.updateChannel:
|
||||||
if latestVersion != unknownVersion {
|
|
||||||
u.latestVersion = latestVersion
|
|
||||||
}
|
|
||||||
u.mutex.Unlock()
|
|
||||||
ctx, cancel := context.WithDeadline(u.ctx, time.Now().Add(time.Minute))
|
|
||||||
u.CheckForUpdates(ctx)
|
|
||||||
cancel()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u.handleUpdate(ctx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *UpdateManager) CheckForUpdates(ctx context.Context) {
|
func (u *UpdateManager) handleUpdate(ctx context.Context) {
|
||||||
if u.version == disableAutoUpdate {
|
var updateVersion *v.Version
|
||||||
log.Trace("Skipped checking for updates, auto-update is disabled")
|
|
||||||
return
|
u.expectedVersionMutex.Lock()
|
||||||
}
|
expectedVersion := u.expectedVersion
|
||||||
currentVersionString := version.NetbirdVersion()
|
u.expectedVersionMutex.Unlock()
|
||||||
updateVersionString := u.version
|
|
||||||
if updateVersionString == latestVersion || updateVersionString == "" {
|
// Resolve "latest" to actual version
|
||||||
if u.latestVersion == unknownVersion {
|
if expectedVersion == latestVersion {
|
||||||
|
if !u.isVersionAvailable() {
|
||||||
log.Tracef("Latest version not fetched yet")
|
log.Tracef("Latest version not fetched yet")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
updateVersionString = u.latestVersion
|
updateVersion = u.update.LatestVersion()
|
||||||
}
|
} else {
|
||||||
currentVersion, err := v.NewVersion(currentVersionString)
|
var err error
|
||||||
|
updateVersion, err = v.NewSemver(expectedVersion)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Error checking for update, error parsing version `%s`: %v", currentVersionString, err)
|
log.Errorf("Failed to parse latest version: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
updateVersion, err := v.NewVersion(updateVersionString)
|
}
|
||||||
if err != nil {
|
|
||||||
log.Errorf("Error checking for update, error parsing version `%s`: %v", updateVersionString, err)
|
if !u.shouldUpdate(updateVersion) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if currentVersion.LessThan(updateVersion) {
|
|
||||||
if u.lastTrigger.Add(5 * time.Minute).Before(time.Now()) {
|
ctx, cancel := context.WithDeadline(ctx, time.Now().Add(time.Minute))
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
u.lastTrigger = time.Now()
|
u.lastTrigger = time.Now()
|
||||||
log.Debugf("Auto-update triggered, current version: %s, target version: %s", currentVersionString, updateVersionString)
|
log.Debugf("Auto-update triggered, current version: %s, target version: %s", version.NetbirdVersion(), updateVersion)
|
||||||
u.statusRecorder.PublishEvent(
|
u.statusRecorder.PublishEvent(
|
||||||
cProto.SystemEvent_INFO,
|
cProto.SystemEvent_INFO,
|
||||||
cProto.SystemEvent_SYSTEM,
|
cProto.SystemEvent_SYSTEM,
|
||||||
@@ -136,14 +157,38 @@ func (u *UpdateManager) CheckForUpdates(ctx context.Context) {
|
|||||||
"Your client version is older than auto-update version set in Management, updating client now.",
|
"Your client version is older than auto-update version set in Management, updating client now.",
|
||||||
nil,
|
nil,
|
||||||
)
|
)
|
||||||
err = u.triggerUpdate(ctx, updateVersionString)
|
|
||||||
|
err := u.triggerUpdate(ctx, updateVersion.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Error triggering auto-update: %v", err)
|
log.Errorf("Error triggering auto-update: %v", err)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *UpdateManager) shouldUpdate(updateVersion *v.Version) bool {
|
||||||
|
currentVersionString := version.NetbirdVersion()
|
||||||
|
currentVersion, err := v.NewVersion(currentVersionString)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Error checking for update, error parsing version `%s`: %v", currentVersionString, err)
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
} else {
|
if currentVersion.GreaterThanOrEqual(updateVersion) {
|
||||||
log.Debugf("Current version (%s) is equal to or higher than auto-update version (%s)", currentVersionString, updateVersionString)
|
log.Debugf("Current version (%s) is equal to or higher than auto-update version (%s)", currentVersionString, updateVersion)
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if time.Since(u.lastTrigger) < 5*time.Minute {
|
||||||
|
log.Tracef("No need to update")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *UpdateManager) isVersionAvailable() bool {
|
||||||
|
if u.update.LatestVersion() == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func downloadFileToTemporaryDir(ctx context.Context, fileURL string) (string, error) { //nolint:unused
|
func downloadFileToTemporaryDir(ctx context.Context, fileURL string) (string, error) { //nolint:unused
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ type Update struct {
|
|||||||
fetchDone chan struct{}
|
fetchDone chan struct{}
|
||||||
|
|
||||||
onUpdateListener func()
|
onUpdateListener func()
|
||||||
onUpdateChannel chan string
|
|
||||||
listenerLock sync.Mutex
|
listenerLock sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,11 +41,8 @@ func NewUpdate(httpAgent string) *Update {
|
|||||||
currentVersion, _ = goversion.NewVersion("0.0.0")
|
currentVersion, _ = goversion.NewVersion("0.0.0")
|
||||||
}
|
}
|
||||||
|
|
||||||
latestAvailable, _ := goversion.NewVersion("0.0.0")
|
|
||||||
|
|
||||||
u := &Update{
|
u := &Update{
|
||||||
httpAgent: httpAgent,
|
httpAgent: httpAgent,
|
||||||
latestAvailable: latestAvailable,
|
|
||||||
uiVersion: currentVersion,
|
uiVersion: currentVersion,
|
||||||
fetchTicker: time.NewTicker(fetchPeriod),
|
fetchTicker: time.NewTicker(fetchPeriod),
|
||||||
fetchDone: make(chan struct{}),
|
fetchDone: make(chan struct{}),
|
||||||
@@ -95,15 +91,10 @@ func (u *Update) SetOnUpdateListener(updateFn func()) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *Update) SetOnUpdateChannel(updateChannel chan string) {
|
func (u *Update) LatestVersion() *goversion.Version {
|
||||||
u.listenerLock.Lock()
|
|
||||||
defer u.listenerLock.Unlock()
|
|
||||||
u.onUpdateChannel = updateChannel
|
|
||||||
if u.isUpdateAvailable() {
|
|
||||||
u.versionsLock.Lock()
|
u.versionsLock.Lock()
|
||||||
defer u.versionsLock.Unlock()
|
defer u.versionsLock.Unlock()
|
||||||
u.onUpdateChannel <- u.latestAvailable.String()
|
return u.latestAvailable
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *Update) startFetcher() {
|
func (u *Update) startFetcher() {
|
||||||
@@ -181,9 +172,6 @@ func (u *Update) checkUpdate() bool {
|
|||||||
|
|
||||||
u.listenerLock.Lock()
|
u.listenerLock.Lock()
|
||||||
defer u.listenerLock.Unlock()
|
defer u.listenerLock.Unlock()
|
||||||
if u.onUpdateChannel != nil {
|
|
||||||
u.onUpdateChannel <- u.latestAvailable.String()
|
|
||||||
}
|
|
||||||
if u.onUpdateListener == nil {
|
if u.onUpdateListener == nil {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -196,6 +184,10 @@ func (u *Update) isUpdateAvailable() bool {
|
|||||||
u.versionsLock.Lock()
|
u.versionsLock.Lock()
|
||||||
defer u.versionsLock.Unlock()
|
defer u.versionsLock.Unlock()
|
||||||
|
|
||||||
|
if u.latestAvailable == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
if u.latestAvailable.GreaterThan(u.uiVersion) {
|
if u.latestAvailable.GreaterThan(u.uiVersion) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user