• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mendersoftware / mender / 881506220

pending completion
881506220

push

gitlab-ci

web-flow
Merge pull request #1232 from merlin-northern/coveralls_publish_fix

chore: coveralls publish fix.

6346 of 10897 relevant lines covered (58.24%)

32.2 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

52.1
/app/state.go
1
// Copyright 2022 Northern.tech AS
2
//
3
//        Licensed under the Apache License, Version 2.0 (the "License");
4
//        you may not use this file except in compliance with the License.
5
//        You may obtain a copy of the License at
6
//
7
//            http://www.apache.org/licenses/LICENSE-2.0
8
//
9
//        Unless required by applicable law or agreed to in writing, software
10
//        distributed under the License is distributed on an "AS IS" BASIS,
11
//        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
//        See the License for the specific language governing permissions and
13
//        limitations under the License.
14
package app
15

16
import (
17
        "fmt"
18
        "io"
19
        "os"
20
        "sync"
21
        "time"
22

23
        "github.com/pkg/errors"
24
        log "github.com/sirupsen/logrus"
25

26
        "github.com/mendersoftware/mender/client"
27
        "github.com/mendersoftware/mender/conf"
28
        "github.com/mendersoftware/mender/datastore"
29
        "github.com/mendersoftware/mender/installer"
30
        "github.com/mendersoftware/mender/store"
31
)
32

33
const (
34
        errMsgDependencyNotSatisfiedF = "Artifact dependency %q not satisfied " +
35
                "by currently installed artifact (%v != %v)."
36
)
37

38
type timerPool sync.Pool
39

40
func (t *timerPool) Get(wait time.Duration) *time.Timer {
28✔
41
        timer := (*sync.Pool)(t).Get().(*time.Timer)
28✔
42
        // Make sure timer is properly reset before use
28✔
43
        if !timer.Stop() {
56✔
44
                select {
28✔
45
                case <-timer.C:
×
46
                default:
28✔
47
                }
48
        }
49
        timer.Reset(wait)
28✔
50
        return timer
28✔
51
}
52

53
func (t *timerPool) Put(timer *time.Timer) {
27✔
54
        if !timer.Stop() {
54✔
55
                select {
27✔
56
                case <-timer.C:
×
57
                default:
27✔
58
                }
59
        }
60
        (*sync.Pool)(t).Put(timer)
27✔
61
}
62

63
func newTimerPool() *timerPool {
234✔
64
        return &timerPool{
234✔
65
                // New returns a new stopped timer (the timer must be reset before use)
234✔
66
                New: func() interface{} {
262✔
67
                        t := time.NewTimer(time.Hour)
28✔
68
                        if !t.Stop() {
28✔
69
                                select {
×
70
                                case <-t.C:
×
71
                                default:
×
72
                                }
73
                        }
74
                        return t
28✔
75
                },
76
        }
77
}
78

79
var timers = newTimerPool()
80

81
// StateContext carrying over data that may be used by all state handlers
82
type StateContext struct {
83
        // data store access
84
        Rebooter                   installer.Rebooter
85
        Store                      store.Store
86
        WakeupChan                 chan bool
87
        lastUpdateCheckAttempt     time.Time
88
        lastInventoryUpdateAttempt time.Time
89
        fetchInstallAttempts       int
90
        controlMapFetchAttempts    int
91
        inventoryUpdateAttempts    int
92
        nextAttemptAt              time.Time
93
        pauseReported              map[string]bool
94
}
95

96
type StateRunner interface {
97
        // Set runner's state to 's'
98
        SetNextState(s State)
99
        // Obtain runner's state
100
        GetCurrentState() State
101
        // Run the currently set state with this context
102
        TransitionState(next State, ctx *StateContext) (State, bool)
103
}
104

105
type StateCollection struct {
106
        CheckWait       *checkWaitState
107
        Final           *finalState
108
        Idle            *idleState
109
        Init            *initState
110
        InventoryUpdate *inventoryUpdateState
111
        UpdateCheck     *updateCheckState
112
}
113

114
// Exposed state variables.
115
var States = StateCollection{
116
        CheckWait: NewCheckWaitState().(*checkWaitState),
117
        Final: &finalState{
118
                baseState{
119
                        id: datastore.MenderStateDone,
120
                        t:  ToNone,
121
                },
122
        },
123
        Init: &initState{
124
                baseState{
125
                        id: datastore.MenderStateInit,
126
                        t:  ToNone,
127
                },
128
        },
129
        Idle: &idleState{
130
                baseState{
131
                        id: datastore.MenderStateIdle,
132
                        t:  ToIdle,
133
                },
134
        },
135
        InventoryUpdate: NewInventoryUpdateState().(*inventoryUpdateState),
136
        UpdateCheck: &updateCheckState{
137
                baseState{
138
                        id: datastore.MenderStateUpdateCheck,
139
                        t:  ToSync,
140
                },
141
        },
142
}
143

144
type State interface {
145
        // Perform state action, returns next state and boolean flag indicating if
146
        // execution was cancelled or not
147
        Handle(ctx *StateContext, c Controller) (State, bool)
148
        HandleError(ctx *StateContext, c Controller, err menderError) (State, bool)
149
        // Cancel state action, returns true if action was cancelled
150
        Cancel() bool
151
        // Return numeric state ID
152
        Id() datastore.MenderState
153
        // Return transition
154
        Transition() Transition
155
        SetTransition(t Transition)
156
}
157

158
type WaitState interface {
159
        Cancel() bool
160
        Wake() bool
161
        Wait(next, same State, wait time.Duration, wakeup chan bool) (State, bool)
162
}
163

164
type UpdateState interface {
165
        State
166
        Update() *datastore.UpdateInfo
167
        // Signals whether this state is allowed to loop indefinitely. This is
168
        // used to track state transitions (see StateDataStoreCount in
169
        // datastore.go). States that can loop indefinitely are assumed to have
170
        // other means to break out of loops (such as server control), and are
171
        // not counted when updating the state count.
172
        PermitLooping() bool
173
}
174

175
// baseState is a helper state with some convenience methods
176
type baseState struct {
177
        id datastore.MenderState
178
        t  Transition
179
}
180

181
func (b *baseState) Id() datastore.MenderState {
28✔
182
        return b.id
28✔
183
}
28✔
184

185
func (b *baseState) Cancel() bool {
×
186
        return false
×
187
}
×
188

189
func (b *baseState) Transition() Transition {
28✔
190
        return b.t
28✔
191
}
28✔
192

193
func (b *baseState) SetTransition(tran Transition) {
×
194
        b.t = tran
×
195
}
×
196

197
func (b *baseState) String() string {
20✔
198
        return b.id.String()
20✔
199
}
20✔
200

201
func (b *baseState) HandleError(ctx *StateContext, c Controller, err menderError) (State, bool) {
×
202
        log.Error(err.Error())
×
203
        return NewErrorState(err), false
×
204
}
×
205

206
type waitState struct {
207
        baseState
208
        cancel chan bool
209
        wakeup chan bool
210
}
211

212
func NewWaitState(id datastore.MenderState, t Transition) *waitState {
234✔
213
        return &waitState{
234✔
214
                baseState: baseState{id: id, t: t},
234✔
215
                cancel:    make(chan bool),
234✔
216
                // wakeup: is a global channel shared between all states through the `StateContext`.
234✔
217
        }
234✔
218
}
234✔
219

220
// Wait performs wait for time `wait` and return state (`next`, false) after the wait
221
// has completed. If wait was interrupted returns (`same`, true)
222
func (ws *waitState) Wait(next, same State,
223
        wait time.Duration, wakeup chan bool) (State, bool) {
28✔
224
        timer := timers.Get(wait)
28✔
225
        ws.wakeup = wakeup
28✔
226

28✔
227
        defer timers.Put(timer)
28✔
228
        select {
28✔
229
        case <-timer.C:
27✔
230
                log.Debug("Wait complete")
27✔
231
                return next, false
27✔
232
        case <-ws.wakeup:
20✔
233
                log.Info("Forced wake-up from sleep")
20✔
234
                return next, false
20✔
235
        case <-ws.cancel:
×
236
                log.Info("Wait canceled")
×
237
        }
238
        return same, true
×
239
}
240

241
func (ws *waitState) Wake() bool {
×
242
        ws.wakeup <- true
×
243
        return true
×
244
}
×
245

246
func (ws *waitState) Cancel() bool {
×
247
        ws.cancel <- true
×
248
        return true
×
249
}
×
250

251
type updateState struct {
252
        baseState
253
        update datastore.UpdateInfo
254
}
255

256
func NewUpdateState(id datastore.MenderState, t Transition, u *datastore.UpdateInfo) *updateState {
23✔
257
        return &updateState{
23✔
258
                baseState: baseState{id: id, t: t},
23✔
259
                update:    *u,
23✔
260
        }
23✔
261
}
23✔
262

263
func (us *updateState) Update() *datastore.UpdateInfo {
23✔
264
        return &us.update
23✔
265
}
23✔
266

267
func (us *updateState) HandleError(ctx *StateContext, c Controller, err menderError) (State, bool) {
7✔
268
        log.Error(err.Error())
7✔
269

7✔
270
        // Default for most update states. Some states will override this.
7✔
271
        if us.Update().SupportsRollback == datastore.RollbackSupported {
7✔
272
                return NewUpdateRollbackState(us.Update()), false
×
273
        } else {
7✔
274
                setBrokenArtifactFlag(ctx.Store, us.Update().ArtifactName())
7✔
275
                return NewUpdateErrorState(err, us.Update()), false
7✔
276
        }
7✔
277
}
278

279
func (us *updateState) PermitLooping() bool {
23✔
280
        // By default, states should not permit looping so that the client can
23✔
281
        // break out of Update Module or reboot loops.
23✔
282
        return false
23✔
283
}
23✔
284

285
type idleState struct {
286
        baseState
287
}
288

289
func (i *idleState) Handle(ctx *StateContext, c Controller) (State, bool) {
28✔
290
        // stop deployment logging
28✔
291
        _ = DeploymentLogger.Disable()
28✔
292

28✔
293
        // cleanup state-data if any data is still present after an update
28✔
294
        _ = RemoveStateData(ctx.Store)
28✔
295

28✔
296
        // Remove the expired UpdateControlMaps from the expired pool
28✔
297
        c.GetControlMapPool().ClearExpired()
28✔
298

28✔
299
        return States.CheckWait, false
28✔
300
}
28✔
301

302
type initState struct {
303
        baseState
304
}
305

306
func (i *initState) Handle(ctx *StateContext, c Controller) (State, bool) {
28✔
307
        // restore previous state information
28✔
308
        sd, sdErr := datastore.LoadStateData(ctx.Store)
28✔
309

28✔
310
        // handle easy case first: no previous state stored,
28✔
311
        // means no update was in progress; we should continue from idle
28✔
312
        if sdErr != nil && os.IsNotExist(sdErr) {
53✔
313
                log.Debug("No state data stored")
25✔
314
                return States.Idle, false
25✔
315
        }
25✔
316

317
        if sdErr != nil && sdErr != datastore.MaximumStateDataStoreCountExceeded {
3✔
318
                log.Errorf("Failed to restore state data: %v", sdErr)
×
319
                me := NewFatalError(errors.Wrapf(sdErr, "failed to restore state data"))
×
320
                return NewUpdateErrorState(me, &datastore.UpdateInfo{
×
321
                        ID: "unknown",
×
322
                }), false
×
323
        }
×
324

325
        log.Infof("Handling loaded state: %s", sd.Name)
3✔
326

3✔
327
        if err := DeploymentLogger.Enable(sd.UpdateInfo.ID); err != nil {
3✔
328
                // just log error
×
329
                log.Errorf("Failed to enable deployment logger: %s", err)
×
330
        }
×
331

332
        if sdErr == datastore.MaximumStateDataStoreCountExceeded {
3✔
333
                // State argument not needed since we already know that maximum
×
334
                // count was exceeded and a different state will be returned.
×
335
                return handleStateDataError(ctx, nil, false, sd.Name, &sd.UpdateInfo, sdErr)
×
336
        }
×
337

338
        msg := fmt.Sprintf("Mender shut down in state: %s", sd.Name)
3✔
339
        switch sd.Name {
3✔
340
        case datastore.MenderStateReboot:
1✔
341
        case datastore.MenderStateRollbackReboot:
×
342
                // Interruption is expected in these, don't produce error.
×
343
                log.Info(msg)
×
344
        default:
2✔
345
                log.Error(msg)
2✔
346
        }
347

348
        // Used in some cases below. Doesn't mean that there must be an error.
349
        me := NewFatalError(errors.New(msg))
3✔
350

3✔
351
        // We need to restore our payload handlers.
3✔
352
        err := c.RestoreInstallersFromTypeList(sd.UpdateInfo.Artifact.PayloadTypes)
3✔
353
        if err != nil {
3✔
354
                // Getting an error here is *really* bad. It means that we
×
355
                // cannot recover *anything*. Report big bad failure.
×
356
                return NewUpdateStatusReportState(&sd.UpdateInfo, client.StatusFailure), false
×
357
        }
×
358

359
        return i.getNextState(ctx, &sd, me)
3✔
360
}
361

362
func (i *initState) getNextState(ctx *StateContext, sd *datastore.StateData,
363
        maybeErr menderError) (State, bool) {
3✔
364

3✔
365
        // check last known state
3✔
366
        switch sd.Name {
3✔
367

368
        // The update never got a chance to even start. Go straight to Idle
369
        // state, and it will be picked up again at the next polling interval.
370
        case datastore.MenderStateUpdateFetch:
×
371
                err := RemoveStateData(ctx.Store)
×
372
                if err != nil {
×
373
                        return NewErrorState(NewFatalError(err)), false
×
374
                }
×
375
                return States.Idle, false
×
376

377
        // Go straight to cleanup if we rebooted from Download state. This is
378
        // important so that artifact scripts from that state do not get to run,
379
        // since they have not yet been signature checked.
380
        case datastore.MenderStateUpdateStore,
381
                datastore.MenderStateUpdateAfterStore:
1✔
382

1✔
383
                return NewUpdateCleanupState(&sd.UpdateInfo, client.StatusFailure), false
1✔
384

385
        // After reboot into new update.
386
        case datastore.MenderStateReboot:
1✔
387
                return NewUpdateVerifyRebootState(&sd.UpdateInfo), false
1✔
388

389
        // VerifyRollbackReboot must be retried if interrupted, in order to
390
        // possibly go back and RollbackReboot again.
391
        case datastore.MenderStateRollbackReboot,
392
                datastore.MenderStateVerifyRollbackReboot,
393
                datastore.MenderStateAfterRollbackReboot:
×
394

×
395
                return NewUpdateVerifyRollbackRebootState(&sd.UpdateInfo), false
×
396

397
        // Rerun commits in subsequent payloads
398
        case datastore.MenderStateUpdateAfterFirstCommit:
×
399
                return NewUpdateAfterFirstCommitState(&sd.UpdateInfo), false
×
400

401
        // Rerun commit-leave
402
        case datastore.MenderStateUpdateAfterCommit:
×
403
                return NewUpdateAfterCommitState(&sd.UpdateInfo), false
×
404

405
        // Error state (ArtifactFailure) should be retried.
406
        case datastore.MenderStateUpdateError:
×
407
                return NewUpdateErrorState(maybeErr, &sd.UpdateInfo), false
×
408

409
        // Cleanup state should be retried.
410
        case datastore.MenderStateUpdateCleanup:
×
411
                return NewUpdateCleanupState(&sd.UpdateInfo, client.StatusFailure), false
×
412

413
        // Status reports should be retried. It is possible that the original
414
        // status report had a different status than Failure, but worst case
415
        // this is simply a wrong report, the device will be fine, and the logs
416
        // will reveal what happened.
417
        case datastore.MenderStateUpdateStatusReport,
418
                datastore.MenderStateStatusReportRetry:
×
419

×
420
                return NewUpdateStatusReportState(&sd.UpdateInfo, client.StatusFailure), false
×
421

422
        // Historical state. This state is not used anymore in current
423
        // clients. In the past it was used at the very end of the update
424
        // process if there were errors during reporting. But the handling was
425
        // wrong and hence this state was removed. Should we encounter it (which
426
        // is unlikely, but possible), we should be at the very end of an
427
        // update, and should just go back to Idle.
428
        case datastore.MenderStateReportStatusError:
×
429
                return States.Idle, false
×
430

431
        // All other states go to either error or rollback state, depending on
432
        // what's supported.
433
        default:
1✔
434
                if sd.UpdateInfo.SupportsRollback == datastore.RollbackSupported {
1✔
435
                        return NewUpdateRollbackState(&sd.UpdateInfo), false
×
436
                } else {
1✔
437
                        setBrokenArtifactFlag(ctx.Store, sd.UpdateInfo.ArtifactName())
1✔
438
                        return NewUpdateErrorState(maybeErr, &sd.UpdateInfo), false
1✔
439
                }
1✔
440
        }
441
}
442

443
type updateCommitState struct {
444
        *updateState
445
        reportTries int
446
}
447

448
func NewUpdateCommitState(update *datastore.UpdateInfo) UpdateState {
16✔
449
        return &updateCommitState{
16✔
450
                updateState: NewUpdateState(datastore.MenderStateUpdateCommit,
16✔
451
                        ToArtifactCommit_Enter, update),
16✔
452
        }
16✔
453
}
16✔
454

455
func (uc *updateCommitState) Handle(ctx *StateContext, c Controller) (State, bool) {
13✔
456
        var err error
13✔
457

13✔
458
        // start deployment logging
13✔
459
        if err = DeploymentLogger.Enable(uc.Update().ID); err != nil {
13✔
460
                log.Errorf("Can not enable deployment logger: %s", err)
×
461
        }
×
462

463
        log.Debug("Handle update commit state")
13✔
464

13✔
465
        // check if state scripts version is supported
13✔
466
        if err = c.CheckScriptsCompatibility(); err != nil {
13✔
467
                merr := NewTransientError(errors.Errorf("update commit failed: %s", err.Error()))
×
468
                return uc.HandleError(ctx, c, merr)
×
469
        }
×
470

471
        // Clear our authorization in order to force reauthorization, since the
472
        // update may have changed the conditions for authorization, such as
473
        // keys or the identity script, and this will check that it works.
474
        c.ClearAuthorization()
13✔
475

13✔
476
        // A last status report to the server before committing. This is most
13✔
477
        // likely a repeat of the previous status, but the real motivation
13✔
478
        // behind it is to find out whether the server cancelled the deployment
13✔
479
        // while we were installing/rebooting, and whether network is working.
13✔
480
        merr := sendDeploymentStatus(uc.Update(), client.StatusInstalling, &uc.reportTries, c)
13✔
481
        if merr != nil {
13✔
482
                log.Errorf("Failed to send status report to server: %s", merr.Error())
×
483
                if merr.IsFatal() {
×
484
                        return uc.HandleError(ctx, c, merr)
×
485
                } else {
×
486
                        return NewUpdatePreCommitStatusReportRetryState(uc, uc.reportTries), false
×
487
                }
×
488
        }
489

490
        // Commit first payload only. After this commit it is no longer possible
491
        // to roll back, so the rest (if any) will be committed in the next
492
        // state.
493
        installers := c.GetInstallers()
13✔
494
        if len(installers) == 0 {
13✔
495
                log.Info("Installing empty artifact")
×
496
        } else {
13✔
497
                err = installers[0].CommitUpdate()
13✔
498
                if err != nil {
13✔
499
                        // we need to perform roll-back here; one scenario is when
×
500
                        // u-boot fw utils won't work after update; at this point
×
501
                        // without rolling-back it won't be possible to perform new
×
502
                        // update
×
503
                        merr := NewTransientError(errors.Errorf("update commit failed: %s", err.Error()))
×
504
                        return uc.HandleError(ctx, c, merr)
×
505
                }
×
506
        }
507

508
        // If the client migrated the database, we still need the old database
509
        // information if we are to roll back. However, after the commit above,
510
        // it is too late to roll back, so indicate that DB schema migration is
511
        // now permanent, if there was one.
512
        uc.Update().HasDBSchemaUpdate = false
13✔
513

13✔
514
        // And then store the data together with the new artifact name,
13✔
515
        // indicating that we have now migrated to a new artifact!
13✔
516
        err = datastore.StoreStateDataAndTransaction(ctx.Store,
13✔
517
                datastore.StateData{
13✔
518
                        Name:       uc.Id(),
13✔
519
                        UpdateInfo: *uc.Update(),
13✔
520
                },
13✔
521
                true,
13✔
522
                func(txn store.Transaction) error {
26✔
523
                        ud := uc.Update()
13✔
524
                        return datastore.CommitArtifactData(txn, ud.ArtifactName(), ud.ArtifactGroup(),
13✔
525
                                ud.ArtifactTypeInfoProvides(), ud.ArtifactClearsProvides())
13✔
526
                })
13✔
527
        if err != nil {
13✔
528
                log.Error("Could not write state data to persistent storage: ", err.Error())
×
529
                return handleStateDataError(ctx, NewUpdateErrorState(NewTransientError(err), uc.Update()),
×
530
                        false, datastore.MenderStateUpdateInstall, uc.Update(), err)
×
531
        }
×
532

533
        // Do rest of update commits now; then post commit-tasks
534
        return NewUpdateAfterFirstCommitState(uc.Update()), false
13✔
535
}
536

537
type updatePreCommitStatusReportRetryState struct {
538
        waitState
539
        returnToState State
540
        reportTries   int
541
}
542

543
func NewUpdatePreCommitStatusReportRetryState(returnToState State, reportTries int) State {
×
544
        return &updatePreCommitStatusReportRetryState{
×
545
                waitState: waitState{
×
546
                        baseState: baseState{
×
547
                                id: datastore.MenderStateUpdatePreCommitStatusReportRetry,
×
548
                                t:  ToArtifactCommit_Enter,
×
549
                        },
×
550
                },
×
551
                returnToState: returnToState,
×
552
                reportTries:   reportTries,
×
553
        }
×
554
}
×
555

556
func (usr *updatePreCommitStatusReportRetryState) Handle(
557
        ctx *StateContext,
558
        c Controller,
559
) (State, bool) {
×
560
        maxTrySending :=
×
561
                maxSendingAttempts(c.GetUpdatePollInterval(),
×
562
                        c.GetRetryPollInterval(), minReportSendRetries)
×
563
        // we are always initializing with triesSending = 1
×
564
        maxTrySending++
×
565

×
566
        if usr.reportTries < maxTrySending {
×
567
                return usr.Wait(usr.returnToState, usr, c.GetRetryPollInterval(), ctx.WakeupChan)
×
568
        }
×
569
        return usr.returnToState.HandleError(ctx, c,
×
570
                NewTransientError(errors.New("Tried sending status report maximum number of times.")))
×
571
}
572

573
type updateAfterFirstCommitState struct {
574
        *updateState
575
}
576

577
func NewUpdateAfterFirstCommitState(update *datastore.UpdateInfo) State {
13✔
578
        return &updateAfterFirstCommitState{
13✔
579
                updateState: NewUpdateState(datastore.MenderStateUpdateAfterFirstCommit,
13✔
580
                        ToNone, update),
13✔
581
        }
13✔
582
}
13✔
583

584
func (uc *updateAfterFirstCommitState) Handle(ctx *StateContext, c Controller) (State, bool) {
13✔
585
        // This state exists to run Commit for payloads after the first
13✔
586
        // one. After the first commit it is too late to roll back, which means
13✔
587
        // that this state has both different error handling and different
13✔
588
        // spontaneous reboot handling than the first commit state.
13✔
589

13✔
590
        var firstErr error
13✔
591

13✔
592
        installers := c.GetInstallers()
13✔
593
        if len(installers) > 1 {
13✔
594
                for _, i := range installers[1:] {
×
595
                        err := i.CommitUpdate()
×
596
                        if err != nil {
×
597
                                log.Errorf("Error committing %s payload: %s", i.GetType(), err.Error())
×
598
                                if firstErr == nil {
×
599
                                        firstErr = err
×
600
                                }
×
601
                        }
602
                }
603
        }
604

605
        if firstErr != nil {
13✔
606
                merr := NewTransientError(errors.Errorf("update commit failed: %s", firstErr.Error()))
×
607
                return uc.HandleError(ctx, c, merr)
×
608
        }
×
609

610
        // Move on to post-commit tasks.
611
        return NewUpdateAfterCommitState(uc.Update()), false
13✔
612
}
613

614
func (uc *updateAfterFirstCommitState) HandleError(
615
        ctx *StateContext,
616
        c Controller,
617
        merr menderError,
618
) (State, bool) {
×
619
        log.Error(merr.Error())
×
620

×
621
        // Too late to back out now. Just report the error, but do not try to roll back.
×
622
        setBrokenArtifactFlag(ctx.Store, uc.Update().ArtifactName())
×
623
        return NewUpdateCleanupState(uc.Update(), client.StatusFailure), false
×
624
}
×
625

626
type updateAfterCommitState struct {
627
        *updateState
628
}
629

630
func NewUpdateAfterCommitState(update *datastore.UpdateInfo) State {
13✔
631
        return &updateAfterCommitState{
13✔
632
                updateState: NewUpdateState(datastore.MenderStateUpdateAfterCommit,
13✔
633
                        ToArtifactCommit_Leave, update),
13✔
634
        }
13✔
635
}
13✔
636

637
func (uc *updateAfterCommitState) Handle(ctx *StateContext, c Controller) (State, bool) {
13✔
638
        // This state only exists to rerun Commit_Leave scripts in the event of
13✔
639
        // spontaneous shutdowns, so there is nothing else to do in this state.
13✔
640

13✔
641
        // update is committed; clean up
13✔
642
        return NewUpdateCleanupState(uc.Update(), client.StatusSuccess), false
13✔
643
}
13✔
644

645
func (uc *updateAfterCommitState) HandleError(
646
        ctx *StateContext,
647
        c Controller,
648
        merr menderError,
649
) (State, bool) {
×
650
        log.Error(merr.Error())
×
651

×
652
        // Too late to back out now. Just report the error, but do not try to roll back.
×
653
        setBrokenArtifactFlag(ctx.Store, uc.Update().ArtifactName())
×
654
        return NewUpdateCleanupState(uc.Update(), client.StatusFailure), false
×
655
}
×
656

657
type updateCheckState struct {
658
        baseState
659
}
660

661
func (u *updateCheckState) Handle(ctx *StateContext, c Controller) (State, bool) {
27✔
662
        log.Debugf("Handle update check state")
27✔
663

27✔
664
        update, err := c.CheckUpdate()
27✔
665

27✔
666
        if err != nil {
53✔
667
                if errors.Is(err, client.ErrNoDeploymentAvailable) {
52✔
668
                        return States.CheckWait, false
26✔
669
                }
26✔
670

671
                log.Errorf("Update check failed: %s", err)
×
672

×
673
                if update != nil {
×
674
                        // If there is an error, but we got the update info, it
×
675
                        // means there is something wrong with the payload
×
676
                        // itself, not the network. Fail the update immediately,
×
677
                        // because this is not something we expect to recover
×
678
                        // from.
×
679
                        _ = DeploymentLogger.Enable(update.ID)
×
680
                        log.Error(err.Error())
×
681
                        return NewUpdateStatusReportState(update, client.StatusFailure), false
×
682
                }
×
683

684
                return NewErrorState(err), false
×
685
        }
686

687
        if update != nil {
40✔
688
                return NewUpdateFetchState(update), false
20✔
689
        }
20✔
690
        return States.CheckWait, false
×
691
}
692

693
type updateFetchState struct {
694
        baseState
695
        update datastore.UpdateInfo
696
}
697

698
func NewUpdateFetchState(update *datastore.UpdateInfo) State {
20✔
699
        return &updateFetchState{
20✔
700
                baseState: baseState{
20✔
701
                        id: datastore.MenderStateUpdateFetch,
20✔
702
                        t:  ToDownload_Enter,
20✔
703
                },
20✔
704
                update: *update,
20✔
705
        }
20✔
706
}
20✔
707

708
func (u *updateFetchState) Handle(ctx *StateContext, c Controller) (State, bool) {
20✔
709
        // start deployment logging
20✔
710
        if err := DeploymentLogger.Enable(u.update.ID); err != nil {
20✔
711
                return NewUpdateStatusReportState(&u.update, client.StatusFailure), false
×
712
        }
×
713

714
        log.Debugf("Handling update fetch state")
20✔
715

20✔
716
        merr := c.ReportUpdateStatus(&u.update, client.StatusDownloading)
20✔
717
        if merr != nil && merr.IsFatal() {
20✔
718
                return NewUpdateStatusReportState(&u.update, client.StatusFailure), false
×
719
        }
×
720

721
        in, _, err := c.FetchUpdate(u.update.URI())
20✔
722
        if err != nil {
20✔
723
                log.Errorf("Update fetch failed: %s", err)
×
724
                return NewFetchStoreRetryState(u, &u.update, err), false
×
725
        }
×
726

727
        return NewUpdateStoreState(in, &u.update), false
20✔
728
}
729

730
func (uf *updateFetchState) Update() *datastore.UpdateInfo {
20✔
731
        return &uf.update
20✔
732
}
20✔
733

734
func (uf *updateFetchState) PermitLooping() bool {
20✔
735
        return false
20✔
736
}
20✔
737

738
type updateStoreState struct {
739
        *updateState
740
        // reader for obtaining image data
741
        imagein io.ReadCloser
742
}
743

744
func NewUpdateStoreState(in io.ReadCloser, update *datastore.UpdateInfo) State {
20✔
745
        return &updateStoreState{
20✔
746
                NewUpdateState(datastore.MenderStateUpdateStore,
20✔
747
                        ToDownload_Enter, update),
20✔
748
                in,
20✔
749
        }
20✔
750
}
20✔
751

752
// Checks that the artifact name and compatible devices matches between the
753
// artifact header and the response. The installer argument holds a reference
754
// to the artifact reader for the update stream.
755
func (u *updateStoreState) verifyUpdateResponseAndHeader(
756
        installer *installer.Installer) error {
20✔
757
        if installer.GetArtifactName() != u.Update().ArtifactName() {
20✔
758
                return errors.Errorf("Artifact name in artifact is not what "+
×
759
                        "the server claims (%s != %s).",
×
760
                        installer.GetArtifactName(), u.Update().ArtifactName())
×
761
        }
×
762

763
        for _, rspDev := range u.Update().CompatibleDevices() {
40✔
764
                isEqual := false
20✔
765
                for _, artDev := range installer.GetCompatibleDevices() {
40✔
766
                        if artDev == rspDev {
40✔
767
                                isEqual = true
20✔
768
                                break
20✔
769
                        }
770
                }
771
                if !isEqual {
20✔
772
                        return errors.Errorf("Compatible devices in artifact "+
×
773
                                "is not what the server claims (%v != %v).",
×
774
                                installer.GetCompatibleDevices(),
×
775
                                u.Update().CompatibleDevices())
×
776
                }
×
777
        }
778
        return nil
20✔
779
}
780

781
func (u *updateStoreState) Handle(ctx *StateContext, c Controller) (State, bool) {
20✔
782

20✔
783
        // make sure to close the stream with image data
20✔
784
        defer u.imagein.Close()
20✔
785

20✔
786
        // start deployment logging
20✔
787
        if err := DeploymentLogger.Enable(u.update.ID); err != nil {
20✔
788
                return NewUpdateStatusReportState(&u.update, client.StatusFailure), false
×
789
        }
×
790

791
        log.Debugf("Handling update install state")
20✔
792

20✔
793
        merr := c.ReportUpdateStatus(&u.update, client.StatusDownloading)
20✔
794
        if merr != nil && merr.IsFatal() {
20✔
795
                return NewUpdateStatusReportState(&u.update, client.StatusFailure), false
×
796
        }
×
797

798
        installer, err := c.ReadArtifactHeaders(u.imagein)
20✔
799
        if err != nil {
20✔
800
                log.Errorf("Fetching Artifact headers failed: %s", err)
×
801
                return NewUpdateStatusReportState(&u.update, client.StatusFailure), false
×
802
        }
×
803

804
        installers := c.GetInstallers()
20✔
805
        u.update.Artifact.PayloadTypes = make([]string, len(installers))
20✔
806
        for n, i := range installers {
40✔
807
                u.update.Artifact.PayloadTypes[n] = i.GetType()
20✔
808
        }
20✔
809

810
        // Verify that response from update request matches artifact header.
811
        if err := u.verifyUpdateResponseAndHeader(installer); err != nil {
20✔
812
                log.Error(err.Error())
×
813
                return NewUpdateStatusReportState(u.Update(),
×
814
                        client.StatusFailure), false
×
815
        }
×
816

817
        err = u.maybeVerifyArtifactDependsAndProvides(ctx, installer, c)
20✔
818
        if err != nil {
20✔
819
                return NewUpdateStatusReportState(u.Update(),
×
820
                        client.StatusFailure), false
×
821
        }
×
822

823
        // Store state so that all the payload handlers are recorded there. This
824
        // is important since they need to call their Cleanup functions after we
825
        // have started the download.
826
        err = datastore.StoreStateData(ctx.Store, datastore.StateData{
20✔
827
                Name:       u.Id(),
20✔
828
                UpdateInfo: u.update,
20✔
829
        }, true)
20✔
830
        if err != nil {
20✔
831
                log.Error("Could not write state data to persistent storage: ", err.Error())
×
832
                return handleStateDataError(ctx, NewUpdateCleanupState(&u.update, client.StatusFailure),
×
833
                        false, u.Id(), &u.update, err)
×
834
        }
×
835

836
        err = installer.StorePayloads()
20✔
837
        if err != nil {
20✔
838
                log.Errorf("Artifact install failed: %s", err)
×
839
                return NewUpdateCleanupState(&u.update, client.StatusFailure), false
×
840
        }
×
841

842
        ok, state, cancelled := u.handleSupportsRollback(ctx, c)
20✔
843
        if !ok {
20✔
844
                return state, cancelled
×
845
        }
×
846

847
        // restart counter so that we are able to retry next time
848
        ctx.fetchInstallAttempts = 0
20✔
849

20✔
850
        // check if update is not aborted
20✔
851
        // this step is needed as installing might take a while and we might end up with
20✔
852
        // proceeding with already cancelled update
20✔
853
        merr = c.ReportUpdateStatus(&u.update, client.StatusDownloading)
20✔
854
        if merr != nil && merr.IsFatal() {
20✔
855
                return NewUpdateErrorState(merr, &u.update), false
×
856
        }
×
857

858
        return NewUpdateAfterStoreState(&u.update), false
20✔
859
}
860

861
func (u *updateStoreState) maybeVerifyArtifactDependsAndProvides(
862
        ctx *StateContext, installer *installer.Installer, c Controller) error {
20✔
863
        // For artifact version >= 3 we need to fetch the artifact provides of
20✔
864
        // the previous artifact from the datastore, and verify that the
20✔
865
        // provides match artifact dependencies.
20✔
866
        if depends, err := installer.GetArtifactDepends(); err != nil {
20✔
867
                log.Error("Failed to extract artifact dependencies from " +
×
868
                        "header: " + err.Error())
×
869
        } else if depends != nil {
40✔
870
                var provides map[string]string
20✔
871
                // load header-info provides
20✔
872
                provides, err := datastore.LoadProvides(ctx.Store)
20✔
873
                if err != nil {
20✔
874
                        log.Error(err.Error())
×
875
                        return err
×
876
                }
×
877
                if provides, err = verifyAndSetArtifactNameInProvides(
20✔
878
                        provides,
20✔
879
                        c.GetCurrentArtifactName,
20✔
880
                ); err != nil {
20✔
881
                        log.Error(err.Error())
×
882
                        return err
×
883
                }
×
884
                if err = verifyArtifactDependencies(
20✔
885
                        depends, provides); err != nil {
20✔
886
                        log.Error(err.Error())
×
887
                        return err
×
888
                }
×
889
        }
890

891
        // Update the UpdateInfo provides info if artifact v3.
892
        if provides, err := installer.GetArtifactProvides(); err != nil {
20✔
893
                log.Error("Failed to extract artifact provides from " +
×
894
                        "header: " + err.Error())
×
895
                return err
×
896
        } else if provides != nil {
40✔
897
                if _, ok := provides["artifact_name"]; !ok {
20✔
898
                        log.Error("Missing required \"ArtifactName\" from " +
×
899
                                "artifact dependencies")
×
900
                        return err
×
901
                }
×
902
                delete(provides, "artifact_name")
20✔
903
                if grp, ok := provides["artifact_group"]; ok {
20✔
904
                        u.update.Artifact.ArtifactGroup = grp
×
905
                        // remove duplication
×
906
                        delete(provides, "artifact_group")
×
907
                }
×
908
                u.update.Artifact.TypeInfoProvides = provides
20✔
909
        }
910

911
        u.update.Artifact.ClearsArtifactProvides = installer.GetArtifactClearsProvides()
20✔
912

20✔
913
        return nil
20✔
914
}
915

916
func (u *updateStoreState) handleSupportsRollback(
917
        ctx *StateContext,
918
        c Controller,
919
) (bool, State, bool) {
20✔
920
        for _, i := range c.GetInstallers() {
40✔
921
                supportsRollback, err := i.SupportsRollback()
20✔
922
                if err != nil {
20✔
923
                        log.Errorf("Could not determine if module supports rollback: %s", err.Error())
×
924
                        state, cancelled := NewUpdateErrorState(NewTransientError(err), &u.update), false
×
925
                        return false, state, cancelled
×
926
                }
×
927
                if supportsRollback {
20✔
928
                        err = u.update.SupportsRollback.Set(datastore.RollbackSupported)
×
929
                } else {
20✔
930
                        err = u.update.SupportsRollback.Set(datastore.RollbackNotSupported)
20✔
931
                }
20✔
932
                if err != nil {
20✔
933
                        log.Errorf("Could update module rollback support status: %s", err.Error())
×
934
                        state, cancelled := NewUpdateErrorState(NewTransientError(err), &u.update), false
×
935
                        return false, state, cancelled
×
936
                }
×
937
        }
938

939
        // Make sure SupportsRollback status is stored
940
        err := datastore.StoreStateData(ctx.Store, datastore.StateData{
20✔
941
                Name:       u.Id(),
20✔
942
                UpdateInfo: u.update,
20✔
943
        }, true)
20✔
944
        if err != nil {
20✔
945
                log.Error("Could not write state data to persistent storage: ", err.Error())
×
946
                state, cancelled := handleStateDataError(
×
947
                        ctx,
×
948
                        NewUpdateErrorState(NewTransientError(err), &u.update),
×
949
                        false,
×
950
                        u.Id(),
×
951
                        &u.update,
×
952
                        err,
×
953
                )
×
954
                return false, state, cancelled
×
955
        }
×
956

957
        return true, nil, false
20✔
958
}
959

960
func (is *updateStoreState) HandleError(
961
        ctx *StateContext,
962
        c Controller,
963
        merr menderError,
964
) (State, bool) {
×
965
        log.Error(merr.Error())
×
966
        return NewUpdateCleanupState(is.Update(), client.StatusFailure), false
×
967
}
×
968

969
type updateAfterStoreState struct {
970
        *updateState
971
}
972

973
func NewUpdateAfterStoreState(update *datastore.UpdateInfo) State {
20✔
974
        return &updateAfterStoreState{
20✔
975
                updateState: NewUpdateState(datastore.MenderStateUpdateAfterStore,
20✔
976
                        ToDownload_Leave, update),
20✔
977
        }
20✔
978
}
20✔
979

980
func (s *updateAfterStoreState) Handle(ctx *StateContext, c Controller) (State, bool) {
20✔
981
        // This state only exists to run Download_Leave.
20✔
982
        return NewFetchControlMapState(NewUpdateInstallState(s.Update()), nil), false
20✔
983
}
20✔
984

985
func (s *updateAfterStoreState) HandleError(
986
        ctx *StateContext,
987
        c Controller,
988
        merr menderError,
989
) (State, bool) {
×
990
        log.Error(merr.Error())
×
991
        return NewUpdateCleanupState(s.Update(), client.StatusFailure), false
×
992
}
×
993

994
type updateInstallState struct {
995
        *updateState
996
}
997

998
func NewUpdateInstallState(update *datastore.UpdateInfo) UpdateState {
20✔
999
        return &updateInstallState{
20✔
1000
                updateState: NewUpdateState(datastore.MenderStateUpdateInstall,
20✔
1001
                        ToArtifactInstall, update),
20✔
1002
        }
20✔
1003
}
20✔
1004

1005
func (is *updateInstallState) Handle(ctx *StateContext, c Controller) (State, bool) {
17✔
1006
        // start deployment logging
17✔
1007
        if err := DeploymentLogger.Enable(is.Update().ID); err != nil {
17✔
1008
                return NewUpdateErrorState(NewTransientError(err), is.Update()), false
×
1009
        }
×
1010

1011
        merr := c.ReportUpdateStatus(is.Update(), client.StatusInstalling)
17✔
1012
        if merr != nil && merr.IsFatal() {
17✔
1013
                return is.HandleError(ctx, c, merr)
×
1014
        }
×
1015

1016
        // If download was successful, install update, which for dual rootfs
1017
        // means marking inactive partition as the active one.
1018
        for _, i := range c.GetInstallers() {
34✔
1019
                if err := i.InstallUpdate(); err != nil {
17✔
1020
                        return is.HandleError(ctx, c, NewTransientError(err))
×
1021
                }
×
1022
        }
1023

1024
        ok, state, cancelled := is.handleRebootType(ctx, c)
17✔
1025
        if !ok {
17✔
1026
                return state, cancelled
×
1027
        }
×
1028

1029
        for n := range c.GetInstallers() {
34✔
1030
                rebootRequested, err := is.Update().RebootRequested.Get(n)
17✔
1031
                if err != nil {
17✔
1032
                        return is.HandleError(ctx, c, NewTransientError(errors.Wrap(
×
1033
                                err, "Unable to get requested reboot type")))
×
1034
                }
×
1035
                switch rebootRequested {
17✔
1036

1037
                case datastore.RebootTypeNone:
×
1038
                        // Do nothing.
1039

1040
                case datastore.RebootTypeCustom, datastore.RebootTypeAutomatic:
17✔
1041
                        // Go to reboot state if at least one payload requested it.
17✔
1042
                        return is.createRebootState(), false
17✔
1043

1044
                default:
×
1045
                        return is.HandleError(ctx, c, NewTransientError(errors.New(
×
1046
                                "Unknown reboot type stored in database. Not continuing")))
×
1047
                }
1048
        }
1049

1050
        // No reboot requests, go to commit state.
1051
        return NewFetchControlMapState(NewUpdateCommitState(is.Update()), nil), false
×
1052
}
1053

1054
func (is *updateInstallState) createRebootState() State {
17✔
1055
        return NewFetchControlMapState(NewUpdateRebootState(is.Update()),
17✔
1056
                NewUpdateRebootPauseRequestedState(is.Update()))
17✔
1057
}
17✔
1058

1059
func (is *updateInstallState) handleRebootType(
1060
        ctx *StateContext,
1061
        c Controller,
1062
) (bool, State, bool) {
17✔
1063
        for n, i := range c.GetInstallers() {
34✔
1064
                needsReboot, err := i.NeedsReboot()
17✔
1065
                if err != nil {
17✔
1066
                        state, cancelled := is.HandleError(ctx, c, NewTransientError(err))
×
1067
                        return false, state, cancelled
×
1068
                }
×
1069
                switch needsReboot {
17✔
1070
                case installer.NoReboot:
×
1071
                        err = is.Update().RebootRequested.Set(n, datastore.RebootTypeNone)
×
1072
                case installer.RebootRequired:
17✔
1073
                        err = is.Update().RebootRequested.Set(n, datastore.RebootTypeCustom)
17✔
1074
                case installer.AutomaticReboot:
×
1075
                        err = is.Update().RebootRequested.Set(n, datastore.RebootTypeAutomatic)
×
1076
                default:
×
1077
                        state, cancelled := is.HandleError(ctx, c, NewFatalError(errors.New(
×
1078
                                "Unknown reply from NeedsReboot. Should not happen")))
×
1079
                        return false, state, cancelled
×
1080
                }
1081
                if err != nil {
17✔
1082
                        state, cancelled := is.HandleError(ctx, c, NewTransientError(errors.Wrap(
×
1083
                                err, "Unable to store requested reboot type")))
×
1084
                        return false, state, cancelled
×
1085
                }
×
1086
        }
1087
        // Make sure RebootRequested status is stored
1088
        err := datastore.StoreStateData(ctx.Store, datastore.StateData{
17✔
1089
                Name:       datastore.MenderStateUpdateInstall,
17✔
1090
                UpdateInfo: *is.Update(),
17✔
1091
        }, true)
17✔
1092
        if err != nil {
17✔
1093
                log.Error("Could not write state data to persistent storage: ", err.Error())
×
1094
                state, cancelled := is.HandleError(ctx, c, NewTransientError(err))
×
1095
                state, cancelled = handleStateDataError(ctx, state, cancelled,
×
1096
                        datastore.MenderStateUpdateInstall, is.Update(), err)
×
1097
                return false, state, cancelled
×
1098
        }
×
1099

1100
        return true, nil, false
17✔
1101
}
1102

1103
type fetchStoreRetryState struct {
1104
        baseState
1105
        WaitState
1106
        from   State
1107
        update datastore.UpdateInfo
1108
        err    error
1109
}
1110

1111
func NewFetchStoreRetryState(from State, update *datastore.UpdateInfo,
1112
        err error) State {
×
1113
        return &fetchStoreRetryState{
×
1114
                baseState: baseState{
×
1115
                        id: datastore.MenderStateFetchStoreRetryWait,
×
1116
                        t:  ToDownload_Enter,
×
1117
                },
×
1118
                WaitState: NewWaitState(datastore.MenderStateFetchStoreRetryWait, ToDownload_Enter),
×
1119
                from:      from,
×
1120
                update:    *update,
×
1121
                err:       err,
×
1122
        }
×
1123
}
×
1124

1125
func (fir *fetchStoreRetryState) Cancel() bool {
×
1126
        return fir.WaitState.Cancel()
×
1127
}
×
1128
func (fir *fetchStoreRetryState) Handle(ctx *StateContext, c Controller) (State, bool) {
×
1129
        log.Debugf("Handle fetch install retry state")
×
1130

×
1131
        intvl, err := client.GetExponentialBackoffTime(
×
1132
                ctx.fetchInstallAttempts,
×
1133
                c.GetUpdatePollInterval(),
×
1134
                c.GetRetryPollCount(),
×
1135
        )
×
1136
        if err != nil {
×
1137
                if fir.err != nil {
×
1138
                        return NewUpdateErrorState(
×
1139
                                NewTransientError(errors.Wrap(fir.err, err.Error())),
×
1140
                                &fir.update), false
×
1141
                }
×
1142
                return NewUpdateErrorState(
×
1143
                        NewTransientError(err), &fir.update), false
×
1144
        }
1145

1146
        ctx.fetchInstallAttempts++
×
1147

×
1148
        log.Debugf("Wait %v before next fetch/install attempt", intvl)
×
1149
        return fir.Wait(NewUpdateFetchState(&fir.update), fir, intvl, ctx.WakeupChan)
×
1150
}
1151

1152
type inventoryUpdateRetry struct {
1153
        baseState
1154
        WaitState
1155
        from State
1156
        err  error
1157
}
1158

1159
func NewInventoryUpdateRetryState(from State,
1160
        err error) State {
1✔
1161
        return &inventoryUpdateRetry{
1✔
1162
                baseState: baseState{
1✔
1163
                        id: datastore.MenderStateInventoryUpdateRetryWait,
1✔
1164
                        t:  ToSync,
1✔
1165
                },
1✔
1166
                WaitState: NewWaitState(datastore.MenderStateInventoryUpdateRetryWait, ToSync),
1✔
1167
                from:      from,
1✔
1168
                err:       err,
1✔
1169
        }
1✔
1170
}
1✔
1171

1172
func (fir *inventoryUpdateRetry) Cancel() bool {
×
1173
        return fir.WaitState.Cancel()
×
1174
}
×
1175

1176
func (fir *inventoryUpdateRetry) Handle(ctx *StateContext, c Controller) (State, bool) {
1✔
1177
        if ctx.nextAttemptAt.After(time.Now()) {
1✔
1178
                remainingWaitDuration := time.Until(ctx.nextAttemptAt)
×
1179
                configuredInterval := c.GetRetryPollInterval()
×
1180
                if remainingWaitDuration.Seconds() > configuredInterval.Seconds() {
×
1181
                        remainingWaitDuration = configuredInterval
×
1182
                }
×
1183
                log.Infof("Handle update inventory retry state: not the time yet; %ds/%v remaining.",
×
1184
                        remainingWaitDuration/time.Second, time.Until(ctx.nextAttemptAt))
×
1185
                return fir.Wait(NewInventoryUpdateState(), fir, remainingWaitDuration, ctx.WakeupChan)
×
1186
        }
1187
        log.Infof("Handle update inventory retry state try: %d", ctx.inventoryUpdateAttempts)
1✔
1188
        err := c.InventoryRefresh()
1✔
1189
        if err != nil {
2✔
1190
                log.Warnf("Failed to refresh inventory: %v", err)
1✔
1191
                if errors.Cause(err) == errNoArtifactName {
1✔
1192
                        return NewErrorState(NewTransientError(err)), false
×
1193
                }
×
1194
        } else {
×
1195
                return States.CheckWait, false
×
1196
        }
×
1197

1198
        interval, err := client.GetExponentialBackoffTime(
1✔
1199
                ctx.inventoryUpdateAttempts,
1✔
1200
                c.GetRetryPollInterval(),
1✔
1201
                c.GetRetryPollCount(),
1✔
1202
        )
1✔
1203
        ctx.nextAttemptAt = time.Now().Add(interval)
1✔
1204
        if err != nil {
1✔
1205
                log.Infof("Handle update inventory retry state: failed to send inventory: %s", err.Error())
×
1206
                return States.CheckWait, false
×
1207
        }
×
1208

1209
        ctx.inventoryUpdateAttempts++
1✔
1210

1✔
1211
        configuredInterval := c.GetRetryPollInterval()
1✔
1212
        if interval.Seconds() > configuredInterval.Seconds() {
2✔
1213
                interval = configuredInterval
1✔
1214
        }
1✔
1215
        log.Infof("Wait %v before next inventory update attempt in %v",
1✔
1216
                interval, time.Until(ctx.nextAttemptAt))
1✔
1217
        return fir.Wait(NewInventoryUpdateState(), fir, interval, ctx.WakeupChan)
1✔
1218
}
1219

1220
type checkWaitState struct {
1221
        baseState
1222
        WaitState
1223
}
1224

1225
func NewCheckWaitState() State {
234✔
1226
        return &checkWaitState{
234✔
1227
                baseState: baseState{
234✔
1228
                        id: datastore.MenderStateCheckWait,
234✔
1229
                        t:  ToIdle,
234✔
1230
                },
234✔
1231
                WaitState: NewWaitState(datastore.MenderStateCheckWait, ToIdle),
234✔
1232
        }
234✔
1233
}
234✔
1234

1235
func (cw *checkWaitState) Cancel() bool {
×
1236
        return cw.WaitState.Cancel()
×
1237
}
×
1238

1239
func (cw *checkWaitState) Handle(ctx *StateContext, c Controller) (State, bool) {
28✔
1240

28✔
1241
        log.Debugf("Handle check wait state")
28✔
1242
        // Inventory should be sent at first try
28✔
1243
        if ctx.lastInventoryUpdateAttempt.IsZero() {
56✔
1244
                return States.InventoryUpdate, false
28✔
1245
        }
28✔
1246

1247
        nextUpdateCheck := ctx.lastUpdateCheckAttempt.Add(c.GetUpdatePollInterval())
27✔
1248
        nextInventoryCheck := ctx.lastInventoryUpdateAttempt.Add(c.GetInventoryPollInterval())
27✔
1249

27✔
1250
        if nextUpdateCheck.Before(nextInventoryCheck) {
54✔
1251
                return cw.Wait(
27✔
1252
                        States.UpdateCheck,
27✔
1253
                        cw,
27✔
1254
                        time.Until(nextUpdateCheck),
27✔
1255
                        ctx.WakeupChan,
27✔
1256
                )
27✔
1257
        }
27✔
1258

1259
        return cw.Wait(
27✔
1260
                States.InventoryUpdate,
27✔
1261
                cw,
27✔
1262
                time.Until(nextInventoryCheck),
27✔
1263
                ctx.WakeupChan,
27✔
1264
        )
27✔
1265
}
1266

1267
type inventoryUpdateState struct {
1268
        baseState
1269
}
1270

1271
func NewInventoryUpdateState() State {
234✔
1272
        return &inventoryUpdateState{
234✔
1273
                baseState: baseState{
234✔
1274
                        id: datastore.MenderStateInventoryUpdate,
234✔
1275
                        t:  ToSync,
234✔
1276
                },
234✔
1277
        }
234✔
1278
}
234✔
1279

1280
func (iu *inventoryUpdateState) Handle(ctx *StateContext, c Controller) (State, bool) {
28✔
1281

28✔
1282
        err := c.InventoryRefresh()
28✔
1283
        if err != nil {
29✔
1284
                log.Warnf("Failed to refresh inventory: %v", err)
1✔
1285
                if errors.Cause(err) == errNoArtifactName {
1✔
1286
                        return NewErrorState(NewTransientError(err)), false
×
1287
                }
×
1288
                return NewInventoryUpdateRetryState(iu, err), false
1✔
1289
        } else {
27✔
1290
                log.Debugf("Inventory refresh complete")
27✔
1291
        }
27✔
1292

1293
        // Reset the counter when done with retrying
1294
        ctx.inventoryUpdateAttempts = 0
27✔
1295

27✔
1296
        return States.CheckWait, false
27✔
1297
}
1298

1299
type errorState struct {
1300
        baseState
1301
        cause menderError
1302
}
1303

1304
func NewErrorState(err menderError) State {
×
1305
        if err == nil {
×
1306
                err = NewFatalError(errors.New("general error"))
×
1307
        }
×
1308

1309
        return &errorState{
×
1310
                baseState{
×
1311
                        id: datastore.MenderStateError,
×
1312
                        t:  ToError,
×
1313
                },
×
1314
                err,
×
1315
        }
×
1316
}
1317

1318
func (e *errorState) Handle(ctx *StateContext, c Controller) (State, bool) {
×
1319
        // stop deployment logging
×
1320
        _ = DeploymentLogger.Disable()
×
1321

×
1322
        log.Infof("Handling error state, current error: %v", e.cause.Error())
×
1323
        // decide if error is transient, exit for now
×
1324
        if e.cause.IsFatal() {
×
1325
                return States.Final, false
×
1326
        }
×
1327
        return States.Idle, false
×
1328
}
1329

1330
func (e *errorState) IsFatal() bool {
×
1331
        return e.cause.IsFatal()
×
1332
}
×
1333

1334
type updateErrorState struct {
1335
        errorState
1336
        update datastore.UpdateInfo
1337
}
1338

1339
func NewUpdateErrorState(err menderError, update *datastore.UpdateInfo) State {
8✔
1340
        return &updateErrorState{
8✔
1341
                errorState{
8✔
1342
                        baseState{id: datastore.MenderStateUpdateError, t: ToArtifactFailure},
8✔
1343
                        err,
8✔
1344
                },
8✔
1345
                *update,
8✔
1346
        }
8✔
1347
}
8✔
1348

1349
func (ue *updateErrorState) Handle(ctx *StateContext, c Controller) (State, bool) {
8✔
1350

8✔
1351
        log.Debug("Handle update error state")
8✔
1352

8✔
1353
        for _, i := range c.GetInstallers() {
16✔
1354
                err := i.Failure()
8✔
1355
                if err != nil {
8✔
1356
                        log.Errorf("ArtifactFailure failed: %s", err.Error())
×
1357
                }
×
1358
        }
1359

1360
        return NewUpdateCleanupState(&ue.update, client.StatusFailure), false
8✔
1361
}
1362

1363
func (ue *updateErrorState) Update() *datastore.UpdateInfo {
8✔
1364
        return &ue.update
8✔
1365
}
8✔
1366

1367
func (ue *updateErrorState) PermitLooping() bool {
8✔
1368
        return false
8✔
1369
}
8✔
1370

1371
type updateCleanupState struct {
1372
        *updateState
1373
        status string
1374
}
1375

1376
func NewUpdateCleanupState(update *datastore.UpdateInfo, status string) State {
20✔
1377
        var transition Transition
20✔
1378
        if status == client.StatusSuccess {
33✔
1379
                transition = ToNone
13✔
1380
        } else {
22✔
1381
                transition = ToError
9✔
1382
        }
9✔
1383
        return &updateCleanupState{
20✔
1384
                updateState: NewUpdateState(datastore.MenderStateUpdateCleanup,
20✔
1385
                        transition, update),
20✔
1386
                status: status,
20✔
1387
        }
20✔
1388
}
1389

1390
func (s *updateCleanupState) Handle(ctx *StateContext, c Controller) (State, bool) {
20✔
1391
        if err := DeploymentLogger.Enable(s.Update().ID); err != nil {
20✔
1392
                log.Errorf("Can not enable deployment logger: %s", err)
×
1393
        }
×
1394

1395
        log.Debug("Handling Cleanup state")
20✔
1396

20✔
1397
        var lastError error
20✔
1398
        for _, i := range c.GetInstallers() {
40✔
1399
                err := i.Cleanup()
20✔
1400
                if err != nil {
20✔
1401
                        log.Errorf("Cleanup failed: %s", err.Error())
×
1402
                        lastError = err
×
1403
                        // Nothing we can do about it though. Just continue.
×
1404
                }
×
1405
        }
1406

1407
        if lastError != nil {
20✔
1408
                s.status = client.StatusFailure
×
1409
        }
×
1410

1411
        // Remove Update Control Maps that match this deployment
1412
        c.GetControlMapPool().DeleteAllPriorities(s.Update().ID)
20✔
1413

20✔
1414
        // Zero-time forces an inventory update on next wait
20✔
1415
        ctx.lastInventoryUpdateAttempt = time.Time{}
20✔
1416

20✔
1417
        // Reset the control map pause context
20✔
1418
        ctx.pauseReported = make(map[string]bool)
20✔
1419

20✔
1420
        // Cleanup is done, report outcome.
20✔
1421
        return NewUpdateStatusReportState(s.Update(), s.status), false
20✔
1422
}
1423

1424
// Wrapper for mandatory update state reporting. The state handler will attempt
1425
// to report state for a number of times. In case of recurring failure, the
1426
// update is deemed as failed.
1427
type updateStatusReportState struct {
1428
        *updateState
1429
        status             string
1430
        triesSendingReport int
1431
        triesSendingLogs   int
1432
        logs               []byte
1433
}
1434

1435
func NewUpdateStatusReportState(update *datastore.UpdateInfo, status string) State {
20✔
1436
        return &updateStatusReportState{
20✔
1437
                updateState: NewUpdateState(datastore.MenderStateUpdateStatusReport,
20✔
1438
                        ToNone, update),
20✔
1439
                status: status,
20✔
1440
        }
20✔
1441
}
20✔
1442

1443
func sendDeploymentLogs(update *datastore.UpdateInfo, sentTries *int,
1444
        logs []byte, c Controller) menderError {
9✔
1445
        if logs == nil {
18✔
1446
                var err error
9✔
1447
                logs, err = DeploymentLogger.GetLogs(update.ID)
9✔
1448
                if err != nil {
9✔
1449
                        log.Errorf("Failed to get deployment logs for deployment [%v]: %v",
×
1450
                                update.ID, err)
×
1451
                        // there is nothing more we can do here
×
1452
                        return NewFatalError(errors.New("can not get deployment logs from file"))
×
1453
                }
×
1454
        }
1455

1456
        *sentTries++
9✔
1457

9✔
1458
        if err := c.UploadLog(update, logs); err != nil {
9✔
1459
                // we got error while sending deployment logs to server;
×
1460
                log.Errorf("Failed to report deployment logs: %v", err)
×
1461
                return NewTransientError(errors.Wrapf(err, "failed to send deployment logs"))
×
1462
        }
×
1463
        return nil
9✔
1464
}
1465

1466
func sendDeploymentStatus(update *datastore.UpdateInfo, status string,
1467
        tries *int, c Controller) menderError {
20✔
1468
        // check if the report was already sent
20✔
1469
        *tries++
20✔
1470
        if err := c.ReportUpdateStatus(update, status); err != nil {
20✔
1471
                return err
×
1472
        }
×
1473
        return nil
20✔
1474
}
1475

1476
func (usr *updateStatusReportState) Handle(ctx *StateContext, c Controller) (State, bool) {
20✔
1477

20✔
1478
        // start deployment logging; no error checking
20✔
1479
        // we can do nothing here; either we will have the logs or not...
20✔
1480
        _ = DeploymentLogger.Enable(usr.Update().ID)
20✔
1481

20✔
1482
        log.Debug("Handling update status report state")
20✔
1483

20✔
1484
        if err := sendDeploymentStatus(usr.Update(), usr.status,
20✔
1485
                &usr.triesSendingReport, c); err != nil {
20✔
1486

×
1487
                log.Errorf("Failed to send status to server: %v", err)
×
1488
                if err.IsFatal() {
×
1489
                        // There is no point in retrying, and there is nothing
×
1490
                        // we can do.
×
1491
                        return States.Idle, false
×
1492
                }
×
1493
                return NewUpdateStatusReportRetryState(usr, usr.Update(), usr.status,
×
1494
                        usr.triesSendingReport), false
×
1495
        }
1496

1497
        if usr.status == client.StatusFailure {
29✔
1498
                log.Debugf("Attempting to upload deployment logs for failed update")
9✔
1499
                if err := sendDeploymentLogs(usr.Update(),
9✔
1500
                        &usr.triesSendingLogs, usr.logs, c); err != nil {
9✔
1501

×
1502
                        log.Errorf("Failed to send deployment logs to server: %v", err)
×
1503
                        if err.IsFatal() {
×
1504
                                // There is no point in retrying, and there is nothing
×
1505
                                // we can do.
×
1506
                                return States.Idle, false
×
1507
                        }
×
1508
                        return NewUpdateStatusReportRetryState(usr, usr.Update(), usr.status,
×
1509
                                usr.triesSendingLogs), false
×
1510
                }
1511
        }
1512

1513
        log.Debug("Reporting complete")
20✔
1514
        // stop deployment logging as the update is completed at this point
20✔
1515
        _ = DeploymentLogger.Disable()
20✔
1516

20✔
1517
        return States.Idle, false
20✔
1518
}
1519

1520
type updateStatusReportRetryState struct {
1521
        baseState
1522
        WaitState
1523
        reportState  State
1524
        update       datastore.UpdateInfo
1525
        status       string
1526
        triesSending int
1527
}
1528

1529
func NewUpdateStatusReportRetryState(reportState State,
1530
        update *datastore.UpdateInfo, status string, tries int) State {
×
1531
        return &updateStatusReportRetryState{
×
1532
                baseState: baseState{
×
1533
                        id: datastore.MenderStateStatusReportRetry,
×
1534
                        t:  ToNone,
×
1535
                },
×
1536
                WaitState:    NewWaitState(datastore.MenderStateStatusReportRetry, ToNone),
×
1537
                reportState:  reportState,
×
1538
                update:       *update,
×
1539
                status:       status,
×
1540
                triesSending: tries,
×
1541
        }
×
1542
}
×
1543

1544
func (usr *updateStatusReportRetryState) Cancel() bool {
×
1545
        return usr.WaitState.Cancel()
×
1546
}
×
1547

1548
// retry no more than 10 times
1549
var maxSendingAttemptsRoof = 10
1550

1551
func Min(a, b int) int {
×
1552
        if a < b {
×
1553
                return a
×
1554
        }
×
1555
        return b
×
1556
}
1557

1558
// try to send failed report at least minRetries times or keep trying every
1559
// 'retryPollInterval' for the duration of two 'updatePollInterval', or a
1560
// maximum of 10 times
1561
func maxSendingAttempts(upi, rpi time.Duration, minRetries int) int {
×
1562
        if rpi == 0 {
×
1563
                return minRetries
×
1564
        }
×
1565
        max := int(upi / rpi)
×
1566
        if max <= minRetries {
×
1567
                return minRetries
×
1568
        }
×
1569
        return Min(max*2, maxSendingAttemptsRoof)
×
1570
}
1571

1572
// retry at least that many times
1573
var minReportSendRetries = 3
1574

1575
func (usr *updateStatusReportRetryState) Handle(ctx *StateContext, c Controller) (State, bool) {
×
1576
        maxTrySending :=
×
1577
                maxSendingAttempts(c.GetUpdatePollInterval(),
×
1578
                        c.GetRetryPollInterval(), minReportSendRetries)
×
1579
        // we are always initializing with triesSending = 1
×
1580
        maxTrySending++
×
1581

×
1582
        if usr.triesSending < maxTrySending {
×
1583
                return usr.Wait(usr.reportState, usr, c.GetRetryPollInterval(), ctx.WakeupChan)
×
1584
        }
×
1585
        // If we have exhausted every attempt, there is nothing more we can
1586
        // do. The update is over.
1587
        return States.Idle, false
×
1588
}
1589

1590
func (usr *updateStatusReportRetryState) Update() *datastore.UpdateInfo {
×
1591
        return &usr.update
×
1592
}
×
1593

1594
func (usr *updateStatusReportRetryState) PermitLooping() bool {
×
1595
        // This state already has maxSendingAttempts() to limit number of
×
1596
        // invocations.
×
1597
        return true
×
1598
}
×
1599

1600
type updateRebootPauseRequestedState struct {
1601
        *updateState
1602
}
1603

1604
// This state solves a tricky issue. When checking Update Control Maps before
1605
// the reboot state, we want a few things to happen: If a pause is requested, we
1606
// want a spontaneous reboot to act as if the update resumed after a normal
1607
// reboot. This requires that we store datastore.MenderStateReboot in the
1608
// database, so that the recovery logic will work. However, we can not store
1609
// this *before* checking the Update Control Maps, because the action may
1610
// resolve to "fail", and then it would be very bad to resume the update. So the
1611
// maps need to be checked, and then, *only if* there is a pause, we enter the
1612
// state below to store the datastore.MenderStateReboot in the database. Then we
1613
// go back to pausing.
1614
func NewUpdateRebootPauseRequestedState(update *datastore.UpdateInfo) UpdateState {
17✔
1615
        return &updateRebootPauseRequestedState{
17✔
1616
                updateState: NewUpdateState(datastore.MenderStateReboot,
17✔
1617
                        ToArtifactReboot_Enter, update),
17✔
1618
        }
17✔
1619
}
17✔
1620

1621
func (s *updateRebootPauseRequestedState) Handle(ctx *StateContext, c Controller) (State, bool) {
2✔
1622
        // No need to fetch maps from the server. If we entered this state then
2✔
1623
        // it means we just fetched them. Go directly to checking the action
2✔
1624
        // instead. Note that we also pass nil as the pauseState. Since we are
2✔
1625
        // in this state, the next time around, we want a real pause to happen.
2✔
1626
        return NewControlMapState(NewUpdateRebootState(s.Update()), nil), false
2✔
1627
}
2✔
1628

1629
type updateRebootState struct {
1630
        *updateState
1631
}
1632

1633
func NewUpdateRebootState(update *datastore.UpdateInfo) UpdateState {
17✔
1634
        return &updateRebootState{
17✔
1635
                updateState: NewUpdateState(datastore.MenderStateReboot,
17✔
1636
                        ToArtifactReboot_Enter, update),
17✔
1637
        }
17✔
1638
}
17✔
1639

1640
func (e *updateRebootState) Handle(ctx *StateContext, c Controller) (State, bool) {
15✔
1641

15✔
1642
        // start deployment logging
15✔
1643
        if err := DeploymentLogger.Enable(e.Update().ID); err != nil {
15✔
1644
                // just log error; we need to reboot anyway
×
1645
                log.Errorf("Failed to enable deployment logger: %s", err)
×
1646
        }
×
1647

1648
        log.Debug("Handling reboot state")
15✔
1649

15✔
1650
        merr := c.ReportUpdateStatus(e.Update(), client.StatusRebooting)
15✔
1651
        if merr != nil && merr.IsFatal() {
15✔
1652
                return NewUpdateRollbackState(e.Update()), false
×
1653
        }
×
1654

1655
        log.Info("Rebooting device(s)")
15✔
1656

15✔
1657
        systemRebootRequested := false
15✔
1658
        for n, i := range c.GetInstallers() {
30✔
1659
                rebootRequested, err := e.Update().RebootRequested.Get(n)
15✔
1660
                if err != nil {
15✔
1661
                        return e.HandleError(ctx, c, NewTransientError(errors.Wrap(
×
1662
                                err, "Unable to get requested reboot type")))
×
1663
                }
×
1664
                switch rebootRequested {
15✔
1665
                case datastore.RebootTypeCustom:
15✔
1666
                        if err := i.Reboot(); err != nil {
15✔
1667
                                log.Errorf("Error rebooting device: %v", err)
×
1668
                                return NewUpdateRollbackState(e.Update()), false
×
1669
                        }
×
1670

1671
                case datastore.RebootTypeAutomatic:
×
1672
                        systemRebootRequested = true
×
1673
                }
1674
        }
1675

1676
        if systemRebootRequested {
15✔
1677
                // Final system reboot after reboot scripts have run.
×
1678
                err := ctx.Rebooter.Reboot()
×
1679
                // Should never return from Reboot().
×
1680
                return e.HandleError(ctx, c, NewTransientError(errors.Wrap(err, "Could not reboot host")))
×
1681
        }
×
1682

1683
        // We may never get here, if the machine we're on rebooted. However, if
1684
        // we rebooted a peripheral device, we will get here.
1685
        return NewUpdateVerifyRebootState(e.Update()), false
15✔
1686
}
1687

1688
type updateVerifyRebootState struct {
1689
        *updateState
1690
}
1691

1692
func NewUpdateVerifyRebootState(update *datastore.UpdateInfo) State {
16✔
1693
        return &updateVerifyRebootState{
16✔
1694
                updateState: NewUpdateState(datastore.MenderStateAfterReboot,
16✔
1695
                        ToArtifactReboot_Leave, update),
16✔
1696
        }
16✔
1697
}
16✔
1698

1699
func (rs *updateVerifyRebootState) Handle(ctx *StateContext, c Controller) (State, bool) {
16✔
1700
        for _, i := range c.GetInstallers() {
32✔
1701
                err := i.VerifyReboot()
16✔
1702
                if err != nil {
16✔
1703
                        return rs.HandleError(ctx, c, NewTransientError(err))
×
1704
                }
×
1705
        }
1706

1707
        return NewUpdateAfterRebootState(rs.Update()), false
16✔
1708
}
1709

1710
type updateAfterRebootState struct {
1711
        *updateState
1712
}
1713

1714
func NewUpdateAfterRebootState(update *datastore.UpdateInfo) State {
16✔
1715
        return &updateAfterRebootState{
16✔
1716
                updateState: NewUpdateState(datastore.MenderStateAfterReboot,
16✔
1717
                        ToArtifactReboot_Leave, update),
16✔
1718
        }
16✔
1719
}
16✔
1720

1721
func (rs *updateAfterRebootState) Handle(ctx *StateContext,
1722
        c Controller) (State, bool) {
16✔
1723
        // start deployment logging; no error checking
16✔
1724
        // we can do nothing here; either we will have the logs or not...
16✔
1725
        _ = DeploymentLogger.Enable(rs.Update().ID)
16✔
1726

16✔
1727
        // this state is needed to satisfy ToReboot transition Leave() action
16✔
1728
        log.Debug("Handling state after reboot")
16✔
1729

16✔
1730
        return NewFetchControlMapState(NewUpdateCommitState(rs.Update()), nil), false
16✔
1731
}
16✔
1732

1733
type updateRollbackState struct {
1734
        *updateState
1735
}
1736

1737
func NewUpdateRollbackState(update *datastore.UpdateInfo) *updateRollbackState {
×
1738
        return &updateRollbackState{
×
1739
                updateState: NewUpdateState(datastore.MenderStateRollback, ToArtifactRollback, update),
×
1740
        }
×
1741
}
×
1742

1743
func (rs *updateRollbackState) Handle(ctx *StateContext, c Controller) (State, bool) {
×
1744
        // start deployment logging
×
1745
        if err := DeploymentLogger.Enable(rs.Update().ID); err != nil {
×
1746
                // just log error; we need to reboot anyway
×
1747
                log.Errorf("Failed to enable deployment logger: %s", err)
×
1748
        }
×
1749

1750
        log.Info("Performing rollback")
×
1751

×
1752
        // Roll back to original partition and perform reboot
×
1753
        for _, i := range c.GetInstallers() {
×
1754
                if err := i.Rollback(); err != nil {
×
1755
                        log.Errorf("Rollback failed: %s", err)
×
1756
                        return rs.HandleError(ctx, c, NewFatalError(err))
×
1757
                }
×
1758
        }
1759

1760
        // Query the rollback parameter from the Update Module, in case it has
1761
        // not been called previously (MEN-4882)
1762
        rs.queryUpdateModuleReboot(c)
×
1763

×
1764
        for n := range c.GetInstallers() {
×
1765
                rebootRequested, err := rs.Update().RebootRequested.Get(n)
×
1766
                if err != nil {
×
1767
                        // We treat error as RebootTypeNone, since it's possible
×
1768
                        // the modules have not been queried yet.
×
1769
                        rebootRequested = datastore.RebootTypeNone
×
1770
                }
×
1771
                switch rebootRequested {
×
1772

1773
                case datastore.RebootTypeNone:
×
1774
                        // Do nothing.
1775

1776
                case datastore.RebootTypeCustom, datastore.RebootTypeAutomatic:
×
1777
                        // Enter rollback reboot state if at least one payload
×
1778
                        // asked for it.
×
1779
                        log.Debug("Will try to rollback reboot the device")
×
1780
                        return NewUpdateRollbackRebootState(rs.Update()), false
×
1781

1782
                default:
×
1783
                        return rs.HandleError(ctx, c, NewTransientError(errors.New(
×
1784
                                "Unknown reboot type stored in database. Not continuing")))
×
1785
                }
1786
        }
1787

1788
        // if no reboot is needed, just return the error and start over
1789
        return NewUpdateErrorState(NewTransientError(errors.New("update error")),
×
1790
                rs.Update()), false
×
1791
}
1792

1793
func (rs *updateRollbackState) queryUpdateModuleReboot(c Controller) {
×
1794
        for n, i := range c.GetInstallers() {
×
1795
                v, err := rs.Update().RebootRequested.Get(n)
×
1796
                if err != nil {
×
1797
                        log.Debugf(
×
1798
                                "Failed to query the database for the Update Module's"+
×
1799
                                        " ArtifactRollback parameter during updateRollbackState: %s."+
×
1800
                                        "Will query the update module anew.",
×
1801
                                err.Error())
×
1802
                }
×
1803
                switch v {
×
1804
                // If the value is 'RebootRequested', or 'RebootTypeAutomatic',
1805
                // do not risk writing a 'RebootTypeNone', reboot in any case
1806
                case datastore.RebootTypeAutomatic, datastore.RebootTypeCustom:
×
1807
                        continue
×
1808
                default:
×
1809
                        needsReboot, err := i.NeedsReboot()
×
1810
                        if err != nil {
×
1811
                                log.Error(err.Error())
×
1812
                                continue
×
1813
                        }
1814
                        switch needsReboot {
×
1815
                        case installer.NoReboot:
×
1816
                                err = rs.Update().RebootRequested.Set(n, datastore.RebootTypeNone)
×
1817
                        case installer.RebootRequired:
×
1818
                                err = rs.Update().RebootRequested.Set(n, datastore.RebootTypeCustom)
×
1819
                        case installer.AutomaticReboot:
×
1820
                                err = rs.Update().RebootRequested.Set(n, datastore.RebootTypeAutomatic)
×
1821
                        default:
×
1822
                                log.Error("Unknown reboot value returned from Update Module")
×
1823
                        }
1824
                        if err != nil {
×
1825
                                log.Errorf(
×
1826
                                        "Unable to set the value returned from the update module in the database."+
×
1827
                                                " Error: %s",
×
1828
                                        err.Error(),
×
1829
                                )
×
1830
                        }
×
1831
                }
1832
        }
1833
}
1834

1835
func (rs *updateRollbackState) HandleError(
1836
        ctx *StateContext,
1837
        c Controller,
1838
        merr menderError,
1839
) (State, bool) {
×
1840
        log.Error(merr.Error())
×
1841
        setBrokenArtifactFlag(ctx.Store, rs.Update().ArtifactName())
×
1842
        return NewUpdateErrorState(merr, rs.Update()), false
×
1843
}
×
1844

1845
type updateRollbackRebootState struct {
1846
        *updateState
1847
}
1848

1849
func NewUpdateRollbackRebootState(update *datastore.UpdateInfo) State {
×
1850
        return &updateRollbackRebootState{
×
1851
                updateState: NewUpdateState(datastore.MenderStateRollbackReboot,
×
1852
                        ToArtifactRollbackReboot_Enter, update),
×
1853
        }
×
1854
}
×
1855

1856
func (rs *updateRollbackRebootState) Handle(ctx *StateContext, c Controller) (State, bool) {
×
1857
        // start deployment logging
×
1858
        if err := DeploymentLogger.Enable(rs.Update().ID); err != nil {
×
1859
                // just log error; we need to reboot anyway
×
1860
                log.Errorf("Failed to enable deployment logger: %s", err)
×
1861
        }
×
1862

1863
        log.Info("Rebooting device(s) after rollback")
×
1864

×
1865
        systemRebootRequested := false
×
1866
        for n, i := range c.GetInstallers() {
×
1867
                rebootRequested, err := rs.Update().RebootRequested.Get(n)
×
1868
                if err != nil {
×
1869
                        return rs.HandleError(ctx, c, NewTransientError(errors.Wrap(
×
1870
                                err, "Unable to get requested reboot type")))
×
1871
                }
×
1872
                switch rebootRequested {
×
1873
                case datastore.RebootTypeCustom:
×
1874
                        if err := i.RollbackReboot(); err != nil {
×
1875
                                log.Errorf("Error rebooting device: %v", err)
×
1876
                                // Outcome is irrelevant, we will go to the
×
1877
                                // VerifyRollbackReboot state regardless.
×
1878
                        }
×
1879

1880
                case datastore.RebootTypeAutomatic:
×
1881
                        systemRebootRequested = true
×
1882
                }
1883
        }
1884

1885
        if systemRebootRequested {
×
1886
                // Final system reboot after reboot scripts have run.
×
1887
                err := ctx.Rebooter.Reboot()
×
1888
                // Should never return from Reboot().
×
1889
                return rs.HandleError(ctx, c, NewTransientError(errors.Wrap(err, "Could not reboot host")))
×
1890
        }
×
1891

1892
        // We may never get here, if the machine we're on rebooted. However, if
1893
        // we rebooted a peripheral device, we will get here.
1894
        return NewUpdateVerifyRollbackRebootState(rs.Update()), false
×
1895
}
1896

1897
func (rs *updateRollbackRebootState) HandleError(ctx *StateContext,
1898
        c Controller, merr menderError) (State, bool) {
×
1899

×
1900
        // We don't really handle errors here, but instead in the verify state.
×
1901
        return NewUpdateVerifyRollbackRebootState(rs.Update()), false
×
1902
}
×
1903

1904
type updateVerifyRollbackRebootState struct {
1905
        *updateState
1906
}
1907

1908
func NewUpdateVerifyRollbackRebootState(update *datastore.UpdateInfo) State {
×
1909
        return &updateVerifyRollbackRebootState{
×
1910
                updateState: NewUpdateState(datastore.MenderStateVerifyRollbackReboot,
×
1911
                        ToArtifactRollbackReboot_Leave, update),
×
1912
        }
×
1913
}
×
1914

1915
func (rs *updateVerifyRollbackRebootState) Handle(ctx *StateContext, c Controller) (State, bool) {
×
1916
        for _, i := range c.GetInstallers() {
×
1917
                err := i.VerifyRollbackReboot()
×
1918
                if err != nil {
×
1919
                        return rs.HandleError(ctx, c, NewTransientError(err))
×
1920
                }
×
1921
        }
1922

1923
        return NewUpdateAfterRollbackRebootState(rs.Update()), false
×
1924
}
1925

1926
func (rs *updateVerifyRollbackRebootState) HandleError(ctx *StateContext,
1927
        c Controller, merr menderError) (State, bool) {
×
1928

×
1929
        log.Errorf("Rollback reboot failed, will retry. Cause: %s", merr.Error())
×
1930

×
1931
        return NewUpdateRollbackRebootState(rs.Update()), false
×
1932
}
×
1933

1934
type updateAfterRollbackRebootState struct {
1935
        *updateState
1936
}
1937

1938
func NewUpdateAfterRollbackRebootState(update *datastore.UpdateInfo) State {
×
1939
        return &updateAfterRollbackRebootState{
×
1940
                updateState: NewUpdateState(datastore.MenderStateAfterRollbackReboot,
×
1941
                        ToArtifactRollbackReboot_Leave, update),
×
1942
        }
×
1943
}
×
1944

1945
func (rs *updateAfterRollbackRebootState) Handle(ctx *StateContext,
1946
        c Controller) (State, bool) {
×
1947
        // start deployment logging
×
1948
        if err := DeploymentLogger.Enable(rs.Update().ID); err != nil {
×
1949
                // just log error; we need to reboot anyway
×
1950
                log.Errorf("Failed to enable deployment logger: %s", err)
×
1951
        }
×
1952

1953
        // this state is needed to satisfy ToRollbackReboot
1954
        // transition Leave() action
1955
        log.Debug("Handling state after rollback reboot")
×
1956

×
1957
        return NewUpdateErrorState(NewTransientError(errors.New("update error")),
×
1958
                rs.Update()), false
×
1959
}
1960

1961
type finalState struct {
1962
        baseState
1963
}
1964

1965
func (f *finalState) Handle(ctx *StateContext, c Controller) (State, bool) {
×
1966
        panic("reached final state")
×
1967
}
1968

1969
func RemoveStateData(store store.Store) error {
28✔
1970
        if store == nil {
28✔
1971
                return nil
×
1972
        }
×
1973
        return store.Remove(datastore.StateDataKey)
28✔
1974
}
1975

1976
// Returns newState, unless store count is exceeded.
1977
func handleStateDataError(ctx *StateContext, newState State, cancelled bool,
1978
        stateName datastore.MenderState,
1979
        update *datastore.UpdateInfo, err error) (State, bool) {
×
1980

×
1981
        if err == datastore.MaximumStateDataStoreCountExceeded {
×
1982
                log.Errorf("State transition loop detected in state %s: Forcefully aborting "+
×
1983
                        "update. The system is likely to be in an inconsistent "+
×
1984
                        "state after this.", stateName)
×
1985
                setBrokenArtifactFlag(ctx.Store, update.ArtifactName())
×
1986
                return NewUpdateStatusReportState(update, client.StatusFailure), false
×
1987
        }
×
1988

1989
        return newState, cancelled
×
1990
}
1991

1992
func setBrokenArtifactFlag(store store.Store, artName string) {
8✔
1993
        newName := artName + conf.BrokenArtifactSuffix
8✔
1994
        log.Debugf("Setting artifact name to %s", newName)
8✔
1995
        err := store.WriteAll(datastore.ArtifactNameKey, []byte(newName))
8✔
1996
        if err != nil {
8✔
1997
                log.Errorf("Could not write artifact name \"%s\": %s", artName, err.Error())
×
1998
                // No error return, because everyone who calls this function is
×
1999
                // already in an error path.
×
2000
        }
×
2001
}
2002

2003
type controlMapState struct {
2004
        wrappedState UpdateState
2005
        pauseState   UpdateState
2006
        baseState
2007
}
2008

2009
func NewControlMapState(wrapsState, pauseState UpdateState) State {
21✔
2010
        return &controlMapState{
21✔
2011
                wrappedState: wrapsState,
21✔
2012
                pauseState:   pauseState,
21✔
2013
                baseState: baseState{
21✔
2014
                        id: datastore.MenderStateUpdateControl,
21✔
2015
                        t:  ToNone,
21✔
2016
                },
21✔
2017
        }
21✔
2018
}
21✔
2019

2020
func (c *controlMapState) PermitLooping() bool { return true }
×
2021

2022
func (c *controlMapState) pauseName(t Transition) string {
12✔
2023
        switch t {
12✔
2024
        case ToArtifactReboot_Enter:
2✔
2025
                return "pause_before_rebooting"
2✔
2026
        case ToArtifactCommit_Enter:
5✔
2027
                return "pause_before_committing"
5✔
2028
        case ToArtifactInstall:
5✔
2029
                return "pause_before_installing"
5✔
2030
        }
2031
        panic(
×
2032
                fmt.Sprintf(
×
2033
                        "No pause name mapping for: %s. This is a logic error in the state machine code",
×
2034
                        t,
×
2035
                ),
×
2036
        )
×
2037
}
2038

2039
func (c *controlMapState) mapStateToName(t Transition) string {
21✔
2040
        switch t {
21✔
2041
        case ToArtifactReboot_Enter, ToArtifactCommit_Enter:
18✔
2042
                return c.wrappedState.Transition().String()
18✔
2043
        case ToArtifactInstall:
20✔
2044
                return "ArtifactInstall_Enter"
20✔
2045
        }
2046
        panic(
×
2047
                fmt.Sprintf(
×
2048
                        "No state name mapping for: %s. This is a logic error in the state machine code",
×
2049
                        t,
×
2050
                ),
×
2051
        )
×
2052
}
2053

2054
func (c *controlMapState) Handle(ctx *StateContext, controller Controller) (State, bool) {
21✔
2055

21✔
2056
        log.Debugf("Handling update control state")
21✔
2057

21✔
2058
        action := controller.GetControlMapPool().
21✔
2059
                QueryAndUpdate(c.mapStateToName(c.wrappedState.Transition()))
21✔
2060
        log.Debugf("controlMapState action: %s", action)
21✔
2061
        switch action {
21✔
2062
        case "continue":
18✔
2063
                return c.wrappedState, false
18✔
2064
        case "pause":
12✔
2065
                if c.pauseState != nil {
14✔
2066
                        log.Debug("Going to pause state")
2✔
2067
                        return c.pauseState, false
2✔
2068
                }
2✔
2069

2070
                log.Infof("Update Control: Pausing before entering %s state", c.wrappedState.Id())
12✔
2071
                log.Debugf("Reporting update status: %s", c.pauseName(c.wrappedState.Transition()))
12✔
2072
                if !ctx.pauseReported[c.wrappedState.Id().String()] {
24✔
2073
                        ctx.pauseReported[c.wrappedState.Id().String()] = true
12✔
2074
                        merr := controller.ReportUpdateStatus(
12✔
2075
                                c.wrappedState.Update(),
12✔
2076
                                c.pauseName(c.wrappedState.Transition()),
12✔
2077
                        )
12✔
2078
                        if merr != nil && merr.IsFatal() {
12✔
2079
                                return c.wrappedState.HandleError(ctx, controller, merr)
×
2080
                        }
×
2081
                }
2082
                return NewControlMapPauseState(c.wrappedState), false
12✔
2083
        case "fail":
7✔
2084
                log.Infof("Update Control: Forced update failure in %s state", c.wrappedState.Id())
7✔
2085
                return c.wrappedState.HandleError(ctx, controller,
7✔
2086
                        NewTransientError(errors.New("Forced a failed update")))
7✔
2087
        default:
×
2088
                log.Warnf("Unknown Action: %s, continuing", action)
×
2089
                return c.wrappedState, false
×
2090
        }
2091
}
2092

2093
type fetchControlMapState struct {
2094
        baseState
2095
        wrappedState UpdateState
2096
        pauseState   UpdateState
2097
}
2098

2099
func (c *fetchControlMapState) PermitLooping() bool { return true }
×
2100

2101
func NewFetchControlMapState(wrappedState, pauseState UpdateState) State {
21✔
2102
        return &fetchControlMapState{
21✔
2103
                baseState: baseState{
21✔
2104
                        id: datastore.MenderStateFetchUpdateControl,
21✔
2105
                        t:  ToNone,
21✔
2106
                },
21✔
2107
                wrappedState: wrappedState,
21✔
2108
                pauseState:   pauseState,
21✔
2109
        }
21✔
2110
}
21✔
2111

2112
func (c *fetchControlMapState) Handle(ctx *StateContext, controller Controller) (State, bool) {
21✔
2113

21✔
2114
        log.Debugf("Handle fetchControlMap state")
21✔
2115

21✔
2116
        if controller.GetControlMapPool().HasControlMap(c.wrappedState.Update().ID) {
23✔
2117

2✔
2118
                err := controller.RefreshServerUpdateControlMap(c.wrappedState.Update().ID)
2✔
2119
                if err != nil {
2✔
2120
                        if errors.Is(err, client.ErrNoDeploymentAvailable) {
×
2121
                                return c.wrappedState.HandleError(ctx, controller,
×
2122
                                        NewTransientError(errors.New("The deployment was aborted from the server")))
×
2123
                        }
×
2124

2125
                        log.Errorf("Update control map check failed: %s, retrying...", err.Error())
×
2126
                        return NewFetchRetryControlMapState(c.wrappedState, c.pauseState), false
×
2127
                }
2128

2129
        }
2130

2131
        // Reset the retry count
2132
        ctx.controlMapFetchAttempts = 0
21✔
2133

21✔
2134
        return NewControlMapState(c.wrappedState, c.pauseState), false
21✔
2135
}
2136

2137
type fetchRetryControlMapState struct {
2138
        waitState
2139
        wrappedState UpdateState
2140
        pauseState   UpdateState
2141
}
2142

2143
func (c *fetchRetryControlMapState) PermitLooping() bool { return true }
×
2144

2145
func NewFetchRetryControlMapState(wrappedState, pauseState UpdateState) State {
×
2146
        return &fetchRetryControlMapState{
×
2147
                waitState: waitState{
×
2148
                        baseState: baseState{
×
2149
                                id: datastore.MenderStateFetchRetryUpdateControl,
×
2150
                                t:  ToNone,
×
2151
                        },
×
2152
                },
×
2153
                wrappedState: wrappedState,
×
2154
                pauseState:   pauseState,
×
2155
        }
×
2156
}
×
2157

2158
func (f *fetchRetryControlMapState) Handle(ctx *StateContext, c Controller) (State, bool) {
×
2159

×
2160
        log.Debugf("Handle fetch update control retry state")
×
2161

×
2162
        intvl, err := client.GetExponentialBackoffTime(
×
2163
                ctx.controlMapFetchAttempts,
×
2164
                c.GetUpdatePollInterval(),
×
2165
                c.GetRetryPollCount(),
×
2166
        )
×
2167
        if err != nil {
×
2168
                return f.wrappedState.HandleError(ctx, c,
×
2169
                        NewTransientError(err))
×
2170
        }
×
2171

2172
        ctx.controlMapFetchAttempts++
×
2173

×
2174
        log.Infof("Wait %v before next update control map fetch/update attempt", intvl)
×
2175
        return f.Wait(
×
2176
                NewFetchControlMapState(f.wrappedState, f.pauseState),
×
2177
                f,
×
2178
                intvl,
×
2179
                ctx.WakeupChan,
×
2180
        )
×
2181
}
2182

2183
type controlMapPauseState struct {
2184
        *UpdateControlMapWaitState
2185
        wrappedState UpdateState
2186
}
2187

2188
func (c *controlMapPauseState) PermitLooping() bool { return true }
×
2189

2190
func NewControlMapPauseState(wrappedState UpdateState) State {
12✔
2191
        return &controlMapPauseState{
12✔
2192
                UpdateControlMapWaitState: NewUpdateControlMapWaitState(
12✔
2193
                        datastore.MenderStateUpdateControlPause,
12✔
2194
                        ToNone,
12✔
2195
                ),
12✔
2196
                wrappedState: wrappedState,
12✔
2197
        }
12✔
2198
}
12✔
2199

2200
// Pause until 1 of two events:
2201
//
2202
// 1. a timeout, which means we need to refresh the map
2203
// 2. an update map expires, which means we need to query the next action
2204
func (c *controlMapPauseState) Handle(ctx *StateContext, controller Controller) (State, bool) {
12✔
2205

12✔
2206
        log.Debug("Handle controlMapPause state")
12✔
2207

12✔
2208
        // Schedule a timer for the next update map event to
12✔
2209
        // fetch from the server
12✔
2210
        nextServerMapRefresh, err := controller.GetControlMapPool().
12✔
2211
                NextIDControlMapHalfTime(c.wrappedState.Update().ID)
12✔
2212

12✔
2213
        if errors.Is(err, NoUpdateMapsErr) {
22✔
2214
                log.Debug("No server control maps present")
10✔
2215

10✔
2216
                // There are no server-side active maps, try with any other active maps
10✔
2217
                nextMapRefresh, err := controller.GetControlMapPool().
10✔
2218
                        NextAnyControlMapHalfTime(c.wrappedState.Update().ID)
10✔
2219

10✔
2220
                if errors.Is(err, NoUpdateMapsErr) {
11✔
2221
                        log.Error("No control maps no longer present, continuing")
1✔
2222
                        return NewControlMapState(c.wrappedState, nil), false
1✔
2223
                }
1✔
2224

2225
                updateMapFromAnyIn := time.Until(nextMapRefresh)
10✔
2226

10✔
2227
                if updateMapFromAnyIn < 0 {
13✔
2228
                        updateMapFromAnyIn = 30
3✔
2229
                }
3✔
2230

2231
                return c.Wait(
10✔
2232
                        NewControlMapState(c.wrappedState, nil),
10✔
2233
                        c,
10✔
2234
                        updateMapFromAnyIn,
10✔
2235
                        controller.GetControlMapPool().Updates)
10✔
2236
        }
2237

2238
        updateMapFromServerIn := time.Until(nextServerMapRefresh)
2✔
2239

2✔
2240
        // For rapid feedback, use the smallest number of the map refresh time
2✔
2241
        // and the update poll interval. The latter is often much shorter.
2✔
2242
        if updateMapFromServerIn > controller.GetUpdatePollInterval() {
2✔
2243
                updateMapFromServerIn = controller.GetUpdatePollInterval()
×
2244
        }
×
2245

2246
        if updateMapFromServerIn <= 0 {
2✔
2247
                updateMapFromServerIn = 30
×
2248
        }
×
2249

2250
        log.Infof("Next update refresh from the server in: %s", updateMapFromServerIn)
2✔
2251

2✔
2252
        log.Debug("Pausing the event loop")
2✔
2253
        return c.MultiplexWait(
2✔
2254
                NewFetchControlMapState(c.wrappedState, nil), // on ticker expiry
2✔
2255
                NewControlMapState(c.wrappedState, nil),      // on map updates
2✔
2256
                updateMapFromServerIn,
2✔
2257
                controller.GetControlMapPool().Updates,
2✔
2258
        )
2✔
2259
}
2260

2261
type UpdateControlMapWaitState struct {
2262
        waitState
2263
}
2264

2265
func NewUpdateControlMapWaitState(
2266
        id datastore.MenderState,
2267
        t Transition,
2268
) *UpdateControlMapWaitState {
12✔
2269
        return &UpdateControlMapWaitState{
12✔
2270
                waitState{
12✔
2271
                        baseState: baseState{id: id, t: t},
12✔
2272
                        cancel:    make(chan bool),
12✔
2273
                },
12✔
2274
        }
12✔
2275
}
12✔
2276

2277
// MultiplexWait multiplexes the next state depending on the action which occurs
2278
// first. If the timer expires, it goes to 'timerState', and if a 'wakeup' is
2279
// received, it goes to the 'wakeupState'.
2280
func (ws *UpdateControlMapWaitState) MultiplexWait(
2281
        timerState, wakeupState State,
2282
        wait time.Duration,
2283
        wakeup chan bool) (State, bool) {
2✔
2284
        timer := timers.Get(wait)
2✔
2285
        ws.wakeup = wakeup
2✔
2286

2✔
2287
        defer timers.Put(timer)
2✔
2288
        select {
2✔
2289
        case <-timer.C:
×
2290
                log.Debug("Wait complete")
×
2291
                return timerState, false
×
2292
        case <-ws.wakeup:
2✔
2293
                log.Info("Forced wake-up from sleep")
2✔
2294
                return wakeupState, false
2✔
2295
        case <-ws.cancel:
×
2296
                log.Info("Wait canceled")
×
2297
        }
2298
        return timerState, true
×
2299
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc