@@ -25,6 +25,7 @@ package deployment
25
25
import (
26
26
"fmt"
27
27
"reflect"
28
+ "sync"
28
29
"sync/atomic"
29
30
"time"
30
31
@@ -83,9 +84,13 @@ const (
83
84
// Deployment is the in process state of an ArangoDeployment.
84
85
type Deployment struct {
85
86
apiObject * api.ArangoDeployment // API object
86
- status api.DeploymentStatus // Internal status of the CR
87
- config Config
88
- deps Dependencies
87
+ status struct {
88
+ mutex sync.Mutex
89
+ version int32
90
+ last api.DeploymentStatus // Internal status copy of the CR
91
+ }
92
+ config Config
93
+ deps Dependencies
89
94
90
95
eventCh chan * deploymentEvent
91
96
stopCh chan struct {}
@@ -112,20 +117,20 @@ func New(config Config, deps Dependencies, apiObject *api.ArangoDeployment) (*De
112
117
}
113
118
d := & Deployment {
114
119
apiObject : apiObject ,
115
- status : * (apiObject .Status .DeepCopy ()),
116
120
config : config ,
117
121
deps : deps ,
118
122
eventCh : make (chan * deploymentEvent , deploymentEventQueueSize ),
119
123
stopCh : make (chan struct {}),
120
124
eventsCli : deps .KubeCli .Core ().Events (apiObject .GetNamespace ()),
121
125
clientCache : newClientCache (deps .KubeCli , apiObject ),
122
126
}
127
+ d .status .last = * (apiObject .Status .DeepCopy ())
123
128
d .reconciler = reconcile .NewReconciler (deps .Log , d )
124
129
d .resilience = resilience .NewResilience (deps .Log , d )
125
130
d .resources = resources .NewResources (deps .Log , d )
126
- if d .status .AcceptedSpec == nil {
131
+ if d .status .last . AcceptedSpec == nil {
127
132
// We've validated the spec, so let's use it from now.
128
- d .status .AcceptedSpec = apiObject .Spec .DeepCopy ()
133
+ d .status .last . AcceptedSpec = apiObject .Spec .DeepCopy ()
129
134
}
130
135
131
136
go d .run ()
@@ -185,7 +190,7 @@ func (d *Deployment) send(ev *deploymentEvent) {
185
190
func (d * Deployment ) run () {
186
191
log := d .deps .Log
187
192
188
- if d .status . Phase == api .DeploymentPhaseNone {
193
+ if d .GetPhase () == api .DeploymentPhaseNone {
189
194
// Create secrets
190
195
if err := d .resources .EnsureSecrets (); err != nil {
191
196
d .CreateEvent (k8sutil .NewErrorEvent ("Failed to create secrets" , err , d .GetAPIObject ()))
@@ -211,8 +216,9 @@ func (d *Deployment) run() {
211
216
d .CreateEvent (k8sutil .NewErrorEvent ("Failed to create pods" , err , d .GetAPIObject ()))
212
217
}
213
218
214
- d .status .Phase = api .DeploymentPhaseRunning
215
- if err := d .updateCRStatus (); err != nil {
219
+ status , lastVersion := d .GetStatus ()
220
+ status .Phase = api .DeploymentPhaseRunning
221
+ if err := d .UpdateStatus (status , lastVersion ); err != nil {
216
222
log .Warn ().Err (err ).Msg ("update initial CR status failed" )
217
223
}
218
224
log .Info ().Msg ("start running..." )
@@ -277,13 +283,14 @@ func (d *Deployment) handleArangoDeploymentUpdatedEvent() error {
277
283
}
278
284
279
285
specBefore := d .apiObject .Spec
280
- if d .status .AcceptedSpec != nil {
281
- specBefore = * d .status .AcceptedSpec
286
+ status := d .status .last
287
+ if d .status .last .AcceptedSpec != nil {
288
+ specBefore = * status .AcceptedSpec .DeepCopy ()
282
289
}
283
290
newAPIObject := current .DeepCopy ()
284
291
newAPIObject .Spec .SetDefaultsFrom (specBefore )
285
292
newAPIObject .Spec .SetDefaults (d .apiObject .GetName ())
286
- newAPIObject .Status = d . status
293
+ newAPIObject .Status = status
287
294
resetFields := specBefore .ResetImmutableFields (& newAPIObject .Spec )
288
295
if len (resetFields ) > 0 {
289
296
log .Debug ().Strs ("fields" , resetFields ).Msg ("Found modified immutable fields" )
@@ -309,9 +316,12 @@ func (d *Deployment) handleArangoDeploymentUpdatedEvent() error {
309
316
return maskAny (fmt .Errorf ("failed to update ArangoDeployment spec: %v" , err ))
310
317
}
311
318
// Save updated accepted spec
312
- d .status .AcceptedSpec = newAPIObject .Spec .DeepCopy ()
313
- if err := d .updateCRStatus (); err != nil {
314
- return maskAny (fmt .Errorf ("failed to update ArangoDeployment status: %v" , err ))
319
+ {
320
+ status , lastVersion := d .GetStatus ()
321
+ status .AcceptedSpec = newAPIObject .Spec .DeepCopy ()
322
+ if err := d .UpdateStatus (status , lastVersion ); err != nil {
323
+ return maskAny (fmt .Errorf ("failed to update ArangoDeployment status: %v" , err ))
324
+ }
315
325
}
316
326
317
327
// Notify cluster of desired server count
@@ -351,7 +361,7 @@ func (d *Deployment) updateCRStatus(force ...bool) error {
351
361
attempt := 0
352
362
for {
353
363
attempt ++
354
- update .Status = d .status
364
+ update .Status = d .status . last
355
365
if update .GetDeletionTimestamp () == nil {
356
366
ensureFinalizers (update )
357
367
}
@@ -388,7 +398,7 @@ func (d *Deployment) updateCRSpec(newSpec api.DeploymentSpec) error {
388
398
for {
389
399
attempt ++
390
400
update .Spec = newSpec
391
- update .Status = d .status
401
+ update .Status = d .status . last
392
402
ns := d .apiObject .GetNamespace ()
393
403
newAPIObject , err := d .deps .DatabaseCRCli .DatabaseV1alpha ().ArangoDeployments (ns ).Update (update )
394
404
if err == nil {
@@ -417,7 +427,7 @@ func (d *Deployment) updateCRSpec(newSpec api.DeploymentSpec) error {
417
427
// Since there is no recovery from a failed deployment, use with care!
418
428
func (d * Deployment ) failOnError (err error , msg string ) {
419
429
log .Error ().Err (err ).Msg (msg )
420
- d .status .Reason = err .Error ()
430
+ d .status .last . Reason = err .Error ()
421
431
d .reportFailedStatus ()
422
432
}
423
433
@@ -428,7 +438,7 @@ func (d *Deployment) reportFailedStatus() {
428
438
log .Info ().Msg ("deployment failed. Reporting failed reason..." )
429
439
430
440
op := func () error {
431
- d .status .Phase = api .DeploymentPhaseFailed
441
+ d .status .last . Phase = api .DeploymentPhaseFailed
432
442
err := d .updateCRStatus ()
433
443
if err == nil || k8sutil .IsNotFound (err ) {
434
444
// Status has been updated
0 commit comments