@@ -25,6 +25,7 @@ package deployment
2525import (
2626 "fmt"
2727 "reflect"
28+ "sync"
2829 "sync/atomic"
2930 "time"
3031
@@ -83,9 +84,13 @@ const (
8384// Deployment is the in process state of an ArangoDeployment.
8485type Deployment struct {
8586 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
8994
9095 eventCh chan * deploymentEvent
9196 stopCh chan struct {}
@@ -112,20 +117,20 @@ func New(config Config, deps Dependencies, apiObject *api.ArangoDeployment) (*De
112117 }
113118 d := & Deployment {
114119 apiObject : apiObject ,
115- status : * (apiObject .Status .DeepCopy ()),
116120 config : config ,
117121 deps : deps ,
118122 eventCh : make (chan * deploymentEvent , deploymentEventQueueSize ),
119123 stopCh : make (chan struct {}),
120124 eventsCli : deps .KubeCli .Core ().Events (apiObject .GetNamespace ()),
121125 clientCache : newClientCache (deps .KubeCli , apiObject ),
122126 }
127+ d .status .last = * (apiObject .Status .DeepCopy ())
123128 d .reconciler = reconcile .NewReconciler (deps .Log , d )
124129 d .resilience = resilience .NewResilience (deps .Log , d )
125130 d .resources = resources .NewResources (deps .Log , d )
126- if d .status .AcceptedSpec == nil {
131+ if d .status .last . AcceptedSpec == nil {
127132 // 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 ()
129134 }
130135
131136 go d .run ()
@@ -185,7 +190,7 @@ func (d *Deployment) send(ev *deploymentEvent) {
185190func (d * Deployment ) run () {
186191 log := d .deps .Log
187192
188- if d .status . Phase == api .DeploymentPhaseNone {
193+ if d .GetPhase () == api .DeploymentPhaseNone {
189194 // Create secrets
190195 if err := d .resources .EnsureSecrets (); err != nil {
191196 d .CreateEvent (k8sutil .NewErrorEvent ("Failed to create secrets" , err , d .GetAPIObject ()))
@@ -211,8 +216,9 @@ func (d *Deployment) run() {
211216 d .CreateEvent (k8sutil .NewErrorEvent ("Failed to create pods" , err , d .GetAPIObject ()))
212217 }
213218
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 {
216222 log .Warn ().Err (err ).Msg ("update initial CR status failed" )
217223 }
218224 log .Info ().Msg ("start running..." )
@@ -277,13 +283,14 @@ func (d *Deployment) handleArangoDeploymentUpdatedEvent() error {
277283 }
278284
279285 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 ()
282289 }
283290 newAPIObject := current .DeepCopy ()
284291 newAPIObject .Spec .SetDefaultsFrom (specBefore )
285292 newAPIObject .Spec .SetDefaults (d .apiObject .GetName ())
286- newAPIObject .Status = d . status
293+ newAPIObject .Status = status
287294 resetFields := specBefore .ResetImmutableFields (& newAPIObject .Spec )
288295 if len (resetFields ) > 0 {
289296 log .Debug ().Strs ("fields" , resetFields ).Msg ("Found modified immutable fields" )
@@ -309,9 +316,12 @@ func (d *Deployment) handleArangoDeploymentUpdatedEvent() error {
309316 return maskAny (fmt .Errorf ("failed to update ArangoDeployment spec: %v" , err ))
310317 }
311318 // 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+ }
315325 }
316326
317327 // Notify cluster of desired server count
@@ -351,7 +361,7 @@ func (d *Deployment) updateCRStatus(force ...bool) error {
351361 attempt := 0
352362 for {
353363 attempt ++
354- update .Status = d .status
364+ update .Status = d .status . last
355365 if update .GetDeletionTimestamp () == nil {
356366 ensureFinalizers (update )
357367 }
@@ -388,7 +398,7 @@ func (d *Deployment) updateCRSpec(newSpec api.DeploymentSpec) error {
388398 for {
389399 attempt ++
390400 update .Spec = newSpec
391- update .Status = d .status
401+ update .Status = d .status . last
392402 ns := d .apiObject .GetNamespace ()
393403 newAPIObject , err := d .deps .DatabaseCRCli .DatabaseV1alpha ().ArangoDeployments (ns ).Update (update )
394404 if err == nil {
@@ -417,7 +427,7 @@ func (d *Deployment) updateCRSpec(newSpec api.DeploymentSpec) error {
417427// Since there is no recovery from a failed deployment, use with care!
418428func (d * Deployment ) failOnError (err error , msg string ) {
419429 log .Error ().Err (err ).Msg (msg )
420- d .status .Reason = err .Error ()
430+ d .status .last . Reason = err .Error ()
421431 d .reportFailedStatus ()
422432}
423433
@@ -428,7 +438,7 @@ func (d *Deployment) reportFailedStatus() {
428438 log .Info ().Msg ("deployment failed. Reporting failed reason..." )
429439
430440 op := func () error {
431- d .status .Phase = api .DeploymentPhaseFailed
441+ d .status .last . Phase = api .DeploymentPhaseFailed
432442 err := d .updateCRStatus ()
433443 if err == nil || k8sutil .IsNotFound (err ) {
434444 // Status has been updated
0 commit comments