@@ -21,129 +21,174 @@ import (
21
21
)
22
22
23
23
const (
24
- latestVersion = "latest"
25
- disableAutoUpdate = "disabled"
26
- unknownVersion = "Unknown"
24
+ latestVersion = "latest"
27
25
)
28
26
29
27
type UpdateManager struct {
30
- ctx context.Context
31
- cancel context.CancelFunc
32
- version string
33
- latestVersion string
34
- update * version.Update
35
28
lastTrigger time.Time
36
29
statusRecorder * peer.Status
37
- mutex sync.Mutex
38
- updateChannel chan string
39
- doneChannel chan struct {}
30
+ mgmUpdateChan chan struct {}
31
+ updateChannel chan struct {}
32
+ wg sync.WaitGroup
33
+
34
+ cancel context.CancelFunc
35
+ update * version.Update
36
+
37
+ expectedVersion string
38
+ expectedVersionMutex sync.Mutex
40
39
}
41
40
42
- func NewUpdateManager (ctx context.Context , statusRecorder * peer.Status ) * UpdateManager {
43
- update := version .NewUpdate ("nb/client" )
44
- ctx , cancel := context .WithCancel (ctx )
41
+ func NewUpdateManager (statusRecorder * peer.Status ) * UpdateManager {
45
42
manager := & UpdateManager {
46
- update : update ,
47
- lastTrigger : time .Now ().Add (- 10 * time .Minute ),
48
43
statusRecorder : statusRecorder ,
49
- ctx : ctx ,
50
- cancel : cancel ,
51
- version : disableAutoUpdate ,
52
- latestVersion : unknownVersion ,
53
- updateChannel : make (chan string , 4 ),
54
- doneChannel : make (chan struct {}),
55
- }
56
- update .SetDaemonVersion (version .NetbirdVersion ())
57
- update .SetOnUpdateChannel (manager .updateChannel )
58
- go manager .UpdateLoop ()
44
+ mgmUpdateChan : make (chan struct {}, 1 ),
45
+ updateChannel : make (chan struct {}, 1 ),
46
+ }
59
47
return manager
60
48
}
61
49
50
+ func (u * UpdateManager ) Start (ctx context.Context ) {
51
+ if u .cancel != nil {
52
+ log .Errorf ("UpdateManager already started" )
53
+ return
54
+ }
55
+
56
+ u .update = version .NewUpdate ("nb/client" )
57
+ u .update .SetDaemonVersion (version .NetbirdVersion ())
58
+ u .update .SetOnUpdateListener (func () {
59
+ select {
60
+ case u .updateChannel <- struct {}{}:
61
+ default :
62
+ }
63
+ })
64
+
65
+ ctx , cancel := context .WithCancel (ctx )
66
+ u .cancel = cancel
67
+
68
+ u .wg .Add (1 )
69
+ go u .updateLoop (ctx )
70
+ }
71
+
62
72
func (u * UpdateManager ) SetVersion (v string ) {
63
- u .mutex .Lock ()
64
- if u .version != v {
65
- log .Tracef ("Auto-update version set to %s" , v )
66
- u .version = v
67
- u .mutex .Unlock ()
68
- u .updateChannel <- unknownVersion
69
- } else {
70
- u .mutex .Unlock ()
73
+ if u .cancel == nil {
74
+ log .Errorf ("UpdateManager not started" )
75
+ return
76
+ }
77
+
78
+ u .expectedVersionMutex .Lock ()
79
+ defer u .expectedVersionMutex .Unlock ()
80
+ if u .expectedVersion == v {
81
+ return
82
+ }
83
+
84
+ u .expectedVersion = v
85
+
86
+ select {
87
+ case u .mgmUpdateChan <- struct {}{}:
88
+ default :
71
89
}
72
90
}
73
91
74
92
func (u * UpdateManager ) Stop () {
93
+ if u .cancel == nil {
94
+ return
95
+ }
96
+
75
97
u .cancel ()
76
- u .mutex .Lock ()
77
- defer u .mutex .Unlock ()
78
98
if u .update != nil {
79
99
u .update .StopWatch ()
80
100
u .update = nil
81
101
}
82
- <- u .doneChannel
102
+
103
+ u .wg .Wait ()
83
104
}
84
105
85
- func (u * UpdateManager ) UpdateLoop () {
106
+ func (u * UpdateManager ) updateLoop (ctx context.Context ) {
107
+ defer u .wg .Done ()
108
+
86
109
for {
87
110
select {
88
- case <- u .ctx .Done ():
89
- u .doneChannel <- struct {}{}
111
+ case <- ctx .Done ():
90
112
return
91
- case latestVersion := <- u .updateChannel :
92
- u .mutex .Lock ()
93
- if latestVersion != unknownVersion {
94
- u .latestVersion = latestVersion
95
- }
96
- u .mutex .Unlock ()
97
- ctx , cancel := context .WithDeadline (u .ctx , time .Now ().Add (time .Minute ))
98
- u .CheckForUpdates (ctx )
99
- cancel ()
113
+ case <- u .mgmUpdateChan :
114
+ case <- u .updateChannel :
100
115
}
116
+
117
+ u .handleUpdate (ctx )
101
118
}
102
119
}
103
120
104
- func (u * UpdateManager ) CheckForUpdates (ctx context.Context ) {
105
- if u .version == disableAutoUpdate {
106
- log .Trace ("Skipped checking for updates, auto-update is disabled" )
107
- return
108
- }
109
- currentVersionString := version .NetbirdVersion ()
110
- updateVersionString := u .version
111
- if updateVersionString == latestVersion || updateVersionString == "" {
112
- if u .latestVersion == unknownVersion {
121
+ func (u * UpdateManager ) handleUpdate (ctx context.Context ) {
122
+ var updateVersion * v.Version
123
+
124
+ u .expectedVersionMutex .Lock ()
125
+ expectedVersion := u .expectedVersion
126
+ u .expectedVersionMutex .Unlock ()
127
+
128
+ // Resolve "latest" to actual version
129
+ if expectedVersion == latestVersion {
130
+ if ! u .isVersionAvailable () {
113
131
log .Tracef ("Latest version not fetched yet" )
114
132
return
115
133
}
116
- updateVersionString = u .latestVersion
134
+ updateVersion = u .update .LatestVersion ()
135
+ } else {
136
+ var err error
137
+ updateVersion , err = v .NewSemver (expectedVersion )
138
+ if err != nil {
139
+ log .Errorf ("Failed to parse latest version: %v" , err )
140
+ return
141
+ }
142
+ }
143
+
144
+ if ! u .shouldUpdate (updateVersion ) {
145
+ return
146
+ }
147
+
148
+ ctx , cancel := context .WithDeadline (ctx , time .Now ().Add (time .Minute ))
149
+ defer cancel ()
150
+
151
+ u .lastTrigger = time .Now ()
152
+ log .Debugf ("Auto-update triggered, current version: %s, target version: %s" , version .NetbirdVersion (), updateVersion )
153
+ u .statusRecorder .PublishEvent (
154
+ cProto .SystemEvent_INFO ,
155
+ cProto .SystemEvent_SYSTEM ,
156
+ "Automatically updating client" ,
157
+ "Your client version is older than auto-update version set in Management, updating client now." ,
158
+ nil ,
159
+ )
160
+
161
+ err := u .triggerUpdate (ctx , updateVersion .String ())
162
+ if err != nil {
163
+ log .Errorf ("Error triggering auto-update: %v" , err )
117
164
}
165
+ }
166
+
167
+ func (u * UpdateManager ) shouldUpdate (updateVersion * v.Version ) bool {
168
+ currentVersionString := version .NetbirdVersion ()
118
169
currentVersion , err := v .NewVersion (currentVersionString )
119
170
if err != nil {
120
171
log .Errorf ("Error checking for update, error parsing version `%s`: %v" , currentVersionString , err )
121
- return
172
+ return false
122
173
}
123
- updateVersion , err := v .NewVersion (updateVersionString )
124
- if err != nil {
125
- log .Errorf ("Error checking for update, error parsing version `%s`: %v" , updateVersionString , err )
126
- return
174
+ if currentVersion .GreaterThanOrEqual (updateVersion ) {
175
+ log .Debugf ("Current version (%s) is equal to or higher than auto-update version (%s)" , currentVersionString , updateVersion )
176
+ return false
127
177
}
128
- if currentVersion .LessThan (updateVersion ) {
129
- if u .lastTrigger .Add (5 * time .Minute ).Before (time .Now ()) {
130
- u .lastTrigger = time .Now ()
131
- log .Debugf ("Auto-update triggered, current version: %s, target version: %s" , currentVersionString , updateVersionString )
132
- u .statusRecorder .PublishEvent (
133
- cProto .SystemEvent_INFO ,
134
- cProto .SystemEvent_SYSTEM ,
135
- "Automatically updating client" ,
136
- "Your client version is older than auto-update version set in Management, updating client now." ,
137
- nil ,
138
- )
139
- err = u .triggerUpdate (ctx , updateVersionString )
140
- if err != nil {
141
- log .Errorf ("Error triggering auto-update: %v" , err )
142
- }
143
- }
144
- } else {
145
- log .Debugf ("Current version (%s) is equal to or higher than auto-update version (%s)" , currentVersionString , updateVersionString )
178
+
179
+ if time .Since (u .lastTrigger ) < 5 * time .Minute {
180
+ log .Tracef ("No need to update" )
181
+ return false
182
+ }
183
+
184
+ return true
185
+ }
186
+
187
+ func (u * UpdateManager ) isVersionAvailable () bool {
188
+ if u .update .LatestVersion () == nil {
189
+ return false
146
190
}
191
+ return true
147
192
}
148
193
149
194
func downloadFileToTemporaryDir (ctx context.Context , fileURL string ) (string , error ) { //nolint:unused
0 commit comments